Android Compose 新闻App(七)网络图片加载、Tab、HorizontalPager

前言

  在上一篇文章中,新增加了一个主页面,那么这个主页面用来做什么呢?主页面的底部我分为两个部分,目前是首页和收藏,首页需要显示好几个类型的新闻数据,那么我们先来做这一步,本文效果图如下:
在这里插入图片描述

正文

  首先我们需要申请API,在天行API中申请如下图所示的API接口.
在这里插入图片描述
鉴于五个不同的数据类型,我们就需要五个接口。

一、申请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,如下图所示:
在这里插入图片描述
下面我们更改HomePage()函数中的参数,如下图所示:
在这里插入图片描述
这里又把参数传递到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)
            )
        }
    }
}

下面我们运行一下:
在这里插入图片描述

这里的数据就显示出来了,通过日志打印我看到有一个图片Url。
在这里插入图片描述
然后如果我们要通过图片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,在这个控件里面增加图片的加载地址,然后修改一下图片的宽高和占满边界,注意一下上面这段代码添加的位置,如下图所示:
在这里插入图片描述
下面我们运行一下:
在这里插入图片描述

三、BottomBar遮挡

我们尝试一下把这个列表滑动到页面底部看看。
在这里插入图片描述
可以看到这里的BottomBar遮挡住客了这个列表的最后一项,那么怎么解决这个问题呢?其实很简单,一行代码解决问题。

	navigationBarsPadding()

在HomeItem中增加,如下图所示:
在这里插入图片描述
这样就可以解决了。

四、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中调用此函数。
在这里插入图片描述
运行一下,看看效果:
在这里插入图片描述

五、修改页面

现在五个页面的内容就只有一个Text,下面我们设置第一个页面为之前写的社会新闻数据,这里首先我们要确定一个事情,那就参数要传递进入TabViewPager函数,如下图所示修改:
在这里插入图片描述
然后就是修改页面的显示内容,代码如下:

		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()
                        )
                    }
                }
            }

这里当页面为第一个页面时,我们现实社会新闻数据,其他页面就和之前一样显示页面下标,代码添加位置如下图所示:
在这里插入图片描述

还有一个地方要注意,那就是之前我们在ShowNewsList函数中设置的navigationBarsPadding(),挪到TabViewPager函数中,如下图所示:在这里插入图片描述
上面的代码在电脑虚拟机和真机上运行效果不一样,因此我将naviationBarsPadding改成了.padding(0.dp, 0.dp, 0.dp, 50.dp),这个我就不截图了,下面运行一下:

下面我们再运行一下:

在这里插入图片描述

六、源码

如果你觉得代码对你有帮助的话,不妨Fork或者Star一下~
GitHub:GoodNews
CSDN:GoodNews_7.rar

猜你喜欢

转载自blog.csdn.net/qq_38436214/article/details/124538946