Android Compose 新闻App网络图片加载TabHorizontalPager

Posted 初学者-Study

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Compose 新闻App网络图片加载TabHorizontalPager相关的知识,希望对你有一定的参考价值。

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

以上是关于Android Compose 新闻App网络图片加载TabHorizontalPager的主要内容,如果未能解决你的问题,请参考以下文章

Android Compose 新闻App网络图片加载TabHorizontalPager

Android Compose 新闻App网络框架搭建

Android Compose 新闻App网络数据Compose UI显示加载Room和DataStore使用

Android Compose 新闻App网络数据Compose UI显示加载Room和DataStore使用

Android Compose 新闻App抽屉布局动态权限拍照返回

Android Compose 新闻App抽屉布局动态权限拍照返回