作者:RicardoMJiang 链接:https://juejin.cn/post/6999775929228591112

前言

Compose正式发布也有一段时间了,感觉要上手还是得实战一波。所以借着空闲时间,参照豆瓣榜单页面的设计,开发了几个Compose版的豆瓣榜单页面
UI效果还是挺好看的,有兴趣的同学可以点个Star:Compose仿豆瓣榜单客户端

效果图

首先看下最终的效果图

特性

在项目中主要用到了以下几个特性,以美化UI及体验
  • 支持设置沉浸式状态栏及状态栏颜色
  • 支持水平方向滚动,竖直方向滚动等多种UI效果
  • 支持给Image设置渐变滤镜,以美化显示效果
  • 支持标题与列表页联动
  • 通过Paging支持了分页加载

主要实现

具体源码可以直接查看,这里主要介绍一些主要功能的实现
沉浸式状态栏设置
状态栏主要是通过accompanist-insets及accompanist-systemuicontroller库设置的 accompanist上提供了一系列常用的,如状态栏,权限,FlowLayout,ViewPagerCompose库 如果有时你发现基础库里没有相应的内容,可以去这里查找下 设置状态栏主要分为以下几步
  • 设置沉浸时状态栏
  • 获取状态栏高度
  • 设置状态栏颜色
overridefunonCreate(savedInstanceState: Bundle?)
 {

super
.onCreate(savedInstanceState)

// 1. 设置状态栏沉浸式
        WindowCompat.setDecorFitsSystemWindows(window, 
false
)


        setContent {

            BD_ToolTheme {

// 加入ProvideWindowInsets
                ProvideWindowInsets {

// 2. 设置状态栏颜色
                    rememberSystemUiController().setStatusBarColor(

                        Color.Transparent, darkIcons = MaterialTheme.colors.isLight)

                    Column {

// 3. 获取状态栏高度并设置占位
                        Spacer(modifier = Modifier

                            .statusBarsHeight()

                            .fillMaxWidth())

                        Text(text = 
"首页\r\n首页1\r\n首页2\r\n首页3"
)

                    }

                }

            }

        }

    }

通过以上方法,就可以比较简单的实现沉浸状态栏的设置
Image设置渐变滤镜
豆瓣榜单页面都给Image设置了渐变滤镜,以美化UI效果 其实实现起来也比较简单,给Image前添加一层渐变的蒙层即可
@Composable
funTopRankItem(item: HomeTopRank)
 {

    Box(

        modifier = Modifier

            .size(
180
.dp, 
220
.dp)

            .padding(
8
.dp)

            .clip(RoundedCornerShape(
10
.dp))

    ) {

// 1. 图片
        Image(

            painter = rememberCoilPainter(request = item.imgUrl),

            contentDescription = 
null
,

            contentScale = ContentScale.Crop,

            modifier = Modifier.fillMaxSize()

        )

        Column(

            modifier = Modifier

                .fillMaxSize()

// 渐变滤镜
                .background(

                    brush = Brush.linearGradient(

                        colors = listOf(Color(item.startColor), Color(item.endColor)),

                        start = Offset(
0f
Float
.POSITIVE_INFINITY),

                        end = Offset(
Float
.POSITIVE_INFINITY, 
0f
)

                    )

                )

                .padding(
8
.dp)


        ) {

//内容
        }

    }

}

如上所示,使用Box布局,给前景设置一个从左下到右上渐变的背景即可
标题与列表联动
具体效果可见上面的动图,即在列表滚动时标题会有一个渐现渐隐效果 这个效果其实我们在Android View体系中也很常见,主要思路也很简单:
  • 监听列表滚动,获取列表滚动offset
  • 根据列表滚动offset设置Header效果,如背景或者高度变化等
@Composable
funRankScreen(viewModel: RankViewModel = RankViewModel()
) {

val
 scrollState = rememberLazyListState()

    Box {

// 1. 监听列表
        LazyColumn(state = scrollState) {

//列表内容
        }

        RankHeader(scrollState)

    }

}


@Composable
funRankHeader(scrollState: LazyListState)
 {

val
 target = LocalDensity.current.run {

200
.dp.toPx()

    }

// 2. 根据列表偏移量计算比例
val
 scrollPercent: 
Float
 = 
if
 (scrollState.firstVisibleItemIndex > 
0
) {

1f
    } 
else
 {

        scrollState.firstVisibleItemScrollOffset / target

    }

val
 activity = LocalContext.current 
as
 Activity

val
 backgroundColor = Color(
0xFF7F6351
)

    Column() {

        Spacer(

            modifier = Modifier

                .fillMaxWidth()

                .statusBarsHeight()

// 3. 根据比例设置Header的alpha,以实现渐变效果
                .alpha(scrollPercent)

                .background(backgroundColor)

        )

//....
    }

}

如上所示,主要有三步:
  • 监听列表
  • 根据列表偏移量计算比例
  • 根据比例设置Header的alpha,以实现渐变效果
利用Paging实现分页
目前Pagin3已经支持了Compose,我们可以利用Paging轻松实现分页效果
主要分为以下几步:
  • ViewModel中设置数据源
  • 在页面中监听Paging数据
  • 根据加载状态设置加载更多footer状态
//1. 设置数据源
classRankViewModel : ViewModel
() {

val
 rankItems: Flow<PagingData<RankDetail>> =

        Pager(PagingConfig(pageSize = 
10
, prefetchDistance = 
1
)) {

            MovieSource()

        }.flow

}


@Composable
funRankScreen(viewModel: RankViewModel = RankViewModel()
) {

val
 lazyMovieItems = viewModel.rankItems.collectAsLazyPagingItems()

    Box {

        LazyColumn(state = scrollState) {

// 2. 在页面中监听paging
            items(lazyMovieItems) {

                it?.let {

                    RankListItem(it)

                }

            }

// 3. 根据paging状态设置加载更多footer状态等
            lazyMovieItems.apply {

when
 (loadState.append) {

is
 LoadState.Loading -> {

                        item { LoadingItem() }

                    }

                }

            }

        }

    }

}

通过以上步骤,就可以比较简单方便地实现分页了

总结

项目地址: https://github.com/shenzhen2017/ComposeDouban
开源不易,如果项目对你有所帮助,欢迎点赞,Star,收藏~
参考资料
  • Android Jetpack Compose 沉浸式/透明状态栏
  • Collapsing Toolbar made easy with Compose
  • Infinite Lists With Paging 3 in Jetpack Compose
更文不易,点个“在看”支持一下👇
继续阅读
阅读原文