天天看點

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

  • ​​前言​​
  • ​​正文​​
  • ​​一、申請API​​
  • ​​① 增加服務接口​​
  • ​​② HomeRepository​​
  • ​​③ HomeViewModel​​
  • ​​二、網絡圖檔加載​​
  • ​​三、BottomBar遮擋​​
  • ​​四、Tab + HorizontalPager​​
  • ​​五、修改頁面​​
  • ​​六、源碼​​

前言

  在上一篇文章中,新增加了一個首頁面,那麼這個首頁面用來做什麼呢?首頁面的底部我分為兩個部分,目前是首頁和收藏,首頁需要顯示好幾個類型的新聞資料,那麼我們先來做這一步,本文效果圖如下:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

正文

  首先我們需要申請API,在天行API中申請如下圖所示的API接口.

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

鑒于五個不同的資料類型,我們就需要五個接口。

一、申請API

首先從社會新聞這個接口開始,我們通過測試請求,然後就能拿到此接口的傳回值,通過這個傳回值我們生成一個資料類,在bean包下建立一個News類,代碼如下:

data class News(val msg: String = "",
                val code: Int = 0,
                val newslist: List<Newslist>)

data class Newslist(val picUrl: String = "",
                        val ctime: String = "",
                        val description: String = "",
                        val id: String = "",
                        val source: String = "",
                        val title: String = "",
                        val url: String = "")      

現在資料有了,下面我們就現在HomeItem.kt中顯示資料。顯示資料也得一步一步來,首先。

① 增加服務接口

首先在ApiService中添加getSocialNews()函數,代碼如下:

/**
     * 擷取社會新聞
     */
    @GET("/social/index?key=$API_KEY")
    fun getSocialNews(): Call<News>      

然後在NetworkRequest中增加getSocialNews()函數,代碼如下:

//擷取社會新聞
    suspend fun getSocialNews() = service.getSocialNews().await()      

② HomeRepository

現在我們還沒有HomeRepository的,在repository中新增HomeRepository,代碼如下:

@ViewModelScoped
class HomeRepository @Inject constructor() : BaseRepository() {

    /**
     * 擷取社會新聞
     */
    fun getSocialNews() = fire(Dispatchers.IO) {
        val news = NetworkRequest.getSocialNews()
        if (news.code == CODE) Result.success(news)
        else Result.failure(RuntimeException("getNews response code is ${news.code} msg is ${news.msg}"))
    }
}      

這個方法就是調用NetworkRequest中的getSocialNews()函數,這在前面的文章中你可能見到過。那麼下一個就是建立ViewModel,與HomeItem相對應的就是HomeViewModel。

③ HomeViewModel

在viewmodel包下中新增一個HomeViewModel,裡面的代碼如下:

@HiltViewModel
class HomeViewModel @Inject constructor(repository: HomeRepository) :ViewModel () {

    val result = repository.getSocialNews()
}      

然後在頁面上我們需要一層一層的傳遞。通常我們Activity和ViewModel是綁定,之前我們在HomeActivity中建立了一個MainViewModel,然後我們在HomeActivity中再加一個HomeViewModel,代碼如下:

val homeViewModel: HomeViewModel = viewModel()      

同樣我們需要在導航到HomePage中時增加導航控制器和homeViewModel,如下圖所示:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

下面我們更改HomePage()函數中的參數,如下圖所示:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

這裡又把參數傳遞到HomeItem中,下面我們再修改一下HomeItem中的代碼,如下所示:

@Composable
fun HomeItem(mNavController: NavHostController, viewModel: HomeViewModel) {

    val dataState = viewModel.result.observeAsState()

    dataState.value?.let {
        ShowNewsList(mNavController,it.getOrNull()!!.newslist)
    }
}

@Composable
fun ShowNewsList(mNavController: NavHostController, newslist: List<Newslist>) {
    LazyColumn(
        state = rememberLazyListState(),
        modifier = Modifier.padding(8.dp)
    ) {
        items(newslist) { new ->
            Log.d("TAG", "ShowNewsList: ${Gson().toJson(new)}")
            Column(modifier = Modifier
                .clickable {
                    val encodedUrl = URLEncoder.encode(new.url, StandardCharsets.UTF_8.toString())
                    mNavController.navigate("${PageConstant.WEB_VIEW_PAGE}/${new.title}/$encodedUrl")
                }
                .padding(8.dp)) {
                Text(
                    text = new.title,
                    fontWeight = FontWeight.ExtraBold,
                    fontSize = 16.sp,
                    modifier = Modifier.padding(0.dp, 10.dp)
                )
                Text(text = new.description, fontSize = 12.sp)
                Text(text = new.ctime, fontSize = 12.sp)
            }
            Divider(
                modifier = Modifier.padding(horizontal = 8.dp),
                color = colorResource(id = R.color.black).copy(alpha = 0.08f)
            )
        }
    }
}      

下面我們運作一下:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

這裡的資料就顯示出來了,通過日志列印我看到有一個圖檔Url。

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

然後如果我們要通過圖檔Url顯示圖檔要怎麼做呢?

二、網絡圖檔加載

  之前在Android的開發你肯定是了解過Glide架構的,那麼現在在Compose中使用Coli庫,這個庫有什麼優點呢?

Coil 是一個 Android 圖檔加載庫,通過 Kotlin 協程的方式加載圖檔。使用它需要添加依賴,在app的build.gradle的dependencies{}閉包,代碼如下:

//Coil庫
    implementation 'io.coil-kt:coil-compose:2.0.0-rc03'      

然後我們需要修改一下之前的item,因為要添加一個圖檔,是以在Column的外部再添加一個Row,代碼如下:

Row(modifier = Modifier
                .clickable {
                    val encodedUrl = URLEncoder.encode(new.url, StandardCharsets.UTF_8.toString())
                    mNavController.navigate("${PageConstant.WEB_VIEW_PAGE}/${new.title}/$encodedUrl")
                }
                .padding(8.dp)
            ) {
                AsyncImage(
                    model = new.picUrl,
                    contentDescription = null,
                    modifier = Modifier
                        .width(120.dp)
                        .height(80.dp),
                    contentScale = ContentScale.FillBounds
                )
                Column(modifier = Modifier.padding(8.dp,0.dp,0.dp,0.dp)) {
                    Text(
                        text = new.title,
                        fontWeight = FontWeight.ExtraBold,
                        fontSize = 16.sp
                    )
                    Row(modifier = Modifier.padding(0.dp, 10.dp)) {
                        Text(text = new.source, fontSize = 12.sp)
                        Text(
                            text = new.ctime,
                            fontSize = 12.sp,
                            modifier = Modifier.padding(8.dp, 0.dp)
                        )
                    }
                }
            }      

這裡的圖檔使用AsyncImage,而不是Image,在這個控件裡面增加圖檔的加載位址,然後修改一下圖檔的寬高和占滿邊界,注意一下上面這段代碼添加的位置,如下圖所示:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

下面我們運作一下:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

三、BottomBar遮擋

我們嘗試一下把這個清單滑動到頁面底部看看。

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

可以看到這裡的BottomBar遮擋住客了這個清單的最後一項,那麼怎麼解決這個問題呢?其實很簡單,一行代碼解決問題。

navigationBarsPadding()      

在HomeItem中增加,如下圖所示:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

這樣就可以解決了。

四、Tab + HorizontalPager

這裡的Tab是已經有了,但是要使用HorizontalPager還需要添加依賴,在app的build.gradle的dependencies{}閉包中添加如下依賴:

//viewpage
    implementation "com.google.accompanist:accompanist-pager:$accompanist_version"
    //viewpage訓示器
    implementation "com.google.accompanist:accompanist-pager-indicators:$accompanist_version"      

使用的話我們用一個函數來表示,在HomeItem中新增一個TabViewPager函數,代碼如下:

@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalPagerApi::class)
@Composable
private fun TabViewPager() {
    Column(modifier = Modifier.fillMaxSize()) {
        val pages by mutableStateOf(
            listOf("社會", "軍事", "科技", "财經", "娛樂")
        )
        val pagerState = rememberPagerState(initialPage = 0)//初始化頁面,0就表示第一個頁面
        TabRow(
            selectedTabIndex = pagerState.currentPage,
            // 使用提供的 pagerTabIndicatorOffset 修飾符自定義訓示器
            indicator = { tabPositions ->
                TabRowDefaults.Indicator(
                    Modifier.pagerTabIndicatorOffset(pagerState, tabPositions)
                )
            },
            backgroundColor = colorResource(id = R.color.white),
            contentColor = colorResource(id = R.color.black)
        ) {
            //給全部頁面添加标簽欄
            pages.forEachIndexed { index, title ->
                Tab(
                    text = { Text(title) },
                    selected = pagerState.currentPage == index,//是否選中
                    onClick = {
                        CoroutineScope(Dispatchers.Main).launch {
                            pagerState.scrollToPage(index)
                        }
                    },
                    modifier = Modifier.alpha(0.9f),//透明度
                    enabled = true,//是否啟用
                    selectedContentColor = colorResource(id = R.color.black),//選中的顔色
                    unselectedContentColor = colorResource(id = R.color.gray),//未選中的顔色
                )
            }
        }
        HorizontalPager(
            count = pages.size,
            state = pagerState,//用于控制或觀察viewpage狀态的狀态對象。
            modifier = Modifier.padding(top = 4.dp),
            itemSpacing = 2.dp
        ) { page ->                                                 
            Column(modifier = Modifier.fillMaxSize()) {
                Text(
                    text = "Page: $page",
                    modifier = Modifier.fillMaxWidth()
                )
            }
        }
    }
}      

你不熟悉的隻是控件使用而已,裡面的參數用幾次就都會了,下面在HomeItem中調用此函數。

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

運作一下,看看效果:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

五、修改頁面

現在五個頁面的内容就隻有一個Text,下面我們設定第一個頁面為之前寫的社會新聞資料,這裡首先我們要确定一個事情,那就參數要傳遞進入TabViewPager函數,如下圖所示修改:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

然後就是修改頁面的顯示内容,代碼如下:

val dataState = viewModel.result.observeAsState()
            when(page) {
                0 -> dataState.value?.let {
                    ShowNewsList(mNavController, it.getOrNull()!!.newslist)
                }
                else -> {
                    Column(modifier = Modifier.fillMaxSize()) {
                        Text(
                            text = "Page: $page",
                            modifier = Modifier.fillMaxWidth()
                        )
                    }
                }
            }      

這裡當頁面為第一個頁面時,我們現實社會新聞資料,其他頁面就和之前一樣顯示頁面下标,代碼添加位置如下圖所示:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

還有一個地方要注意,那就是之前我們在ShowNewsList函數中設定的navigationBarsPadding(),挪到TabViewPager函數中,如下圖所示:

Android Compose 新聞App(七)網絡圖檔加載、Tab、HorizontalPager

上面的代碼在電腦虛拟機和真機上運作效果不一樣,是以我将naviationBarsPadding改成了.padding(0.dp, 0.dp, 0.dp, 50.dp),這個我就不截圖了,下面運作一下: