前言
此篇博客讲解LazyColumn 与 LazyRow、LazyVerticalGrid、LazyHorizontalGrid、LazyHorizontalGrid、LazyVerticalStaggeredGrid,在compose里LazyColumn与LazyRow与是用来延迟加载数据的,它对标原来xml里的ListView与RecyclerView。
而LazyVerticalGrid、LazyHorizontalGrid对标的是原来xml的GridView,LazyHorizontalGrid、LazyVerticalStaggeredGrid对标是原来的瀑布流列表。
此外此博客还会讲解滚动状态监听、下拉刷新、自定义下拉刷新与上拉加载、侧滑Item删除等等例子,这里只用LazyColumn来实现举例,与其他列表大差不差,希望都能举一反三。
LazyColumn 纵向列表
效果图
代码
@Composable
fun APage() {
val listData = remember {
//mutableStateListOf 这个很关键,需要动态新增数据一定需要使用mutableStateListOf
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
)
}
Column {
Button(modifier = Modifier.fillMaxWidth(), onClick = { listData.add("山竹" to R.mipmap.ic_fruit_mangosteen) }) {
Text(text = "新增数据")
}
//LazyColumn
LazyColumn(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
//这个items很关键,LazyColumn一定需要使用这个
items(listData.size){
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(20.dp)) {
Image(painter = painterResource(id = listData[it].second), contentDescription = null, modifier = Modifier
.width(100.dp)
.height(100.dp))
Text(text = listData[it].first, fontSize = 25.sp, modifier = Modifier.padding(start = 80.dp))
}
}
}
}
}
LazyRow 横向列表
效果图
代码
@Composable
fun APage() {
val listData = remember {
//mutableStateListOf 这个很关键,需要动态新增数据一定需要使用mutableStateListOf
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
)
}
Column {
Button(modifier = Modifier.fillMaxWidth(), onClick = { listData.add("山竹" to R.mipmap.ic_fruit_mangosteen) }) {
Text(text = "新增数据")
}
//LazyRow
LazyRow(
Modifier.fillMaxSize(),
) {
//这个items很关键
items(listData.size){
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(20.dp)) {
Image(painter = painterResource(id = listData[it].second), contentDescription = null, modifier = Modifier
.width(100.dp)
.height(100.dp))
Text(text = listData[it].first, fontSize = 25.sp)
}
}
}
}
}
LazyVerticalGrid 纵向宫格列表
效果图
代码
@Composable
fun APage() {
val listData = remember {
//mutableStateListOf 这个很关键,需要动态新增数据一定需要使用mutableStateListOf
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
"橘子" to R.mipmap.ic_fruit_orange,
"梨子" to R.mipmap.ic_fruit_pear,
)
}
Column {
Button(modifier = Modifier.fillMaxWidth(), onClick = { listData.add("山竹" to R.mipmap.ic_fruit_mangosteen) }) {
Text(text = "新增数据")
}
//LazyVerticalGrid
//columns 设置显示几列
LazyVerticalGrid(columns = GridCells.Fixed(3)) {
items(listData.size){
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(20.dp)) {
Image(painter = painterResource(id = listData[it].second), contentDescription = null, modifier = Modifier
.width(90.dp)
.height(90.dp))
Text(text = listData[it].first, fontSize = 18.sp)
}
}
}
}
}
LazyHorizontalGrid横向宫格列表
效果图
代码
@Composable
fun APage() {
val listData = remember {
//mutableStateListOf 这个很关键,需要动态新增数据一定需要使用mutableStateListOf
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
"橘子" to R.mipmap.ic_fruit_orange,
"梨子" to R.mipmap.ic_fruit_pear,
)
}
Column {
Button(modifier = Modifier.fillMaxWidth(), onClick = { listData.add("山竹" to R.mipmap.ic_fruit_mangosteen) }) {
Text(text = "新增数据")
}
//LazyHorizontalGrid
//rows 设置显示几行
LazyHorizontalGrid(rows = GridCells.Fixed(4)) {
items(listData.size){
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(20.dp)) {
Image(painter = painterResource(id = listData[it].second), contentDescription = null, modifier = Modifier
.width(90.dp)
.height(90.dp))
Text(text = listData[it].first, fontSize = 18.sp)
}
}
}
}
}
LazyVerticalStaggeredGrid 纵向瀑布流列表
请注意!LazyVerticalStaggeredGrid为实验性API,并且Compose版本需要1.4.0以上。 这个其实跟我另一篇博客介绍的东西差不多 Android开发 Jetpack Compose FlowColumn与FlowRow瀑布流布局
效果图
代码
//因为LazyVerticalStaggeredGrid是实验性API,所以需要这个注解
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun APage() {
val listData = remember {
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
)
}
Column {
//LazyVerticalStaggeredGrid
//StaggeredGridCells设置显示几列
LazyVerticalStaggeredGrid(columns = StaggeredGridCells.Fixed(4)) {
items(listData.size){
//这边设置一个item的随机高度,产生随机高度的瀑布效果
val height = (80..200).random()
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(10.dp).background(
Color.Gray)) {
Image(painter = painterResource(id = listData[it].second), contentDescription = null, modifier = Modifier
.width(90.dp)
.height(height.dp))
Text(text = listData[it].first, fontSize = 18.sp)
}
}
}
}
}
LazyHorizontalStaggeredGrid 横向瀑布流列表
请注意!LazyHorizontalStaggeredGrid 为实验性API,并且Compose版本需要1.4.0以上。
效果图
代码
//因为LazyHorizontalStaggeredGrid是实验性API,所以需要这个注解
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun APage() {
val listData = remember {
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
)
}
Column {
//LazyHorizontalStaggeredGrid
//StaggeredGridCells设置显示几行
LazyHorizontalStaggeredGrid(rows = StaggeredGridCells.Fixed(4)) {
items(listData.size){
//这边设置一个item的随机宽度,产生随机宽度的瀑布效果
val width = (80..200).random()
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(10.dp).background(
Color.Gray)) {
Image(painter = painterResource(id = listData[it].second), contentDescription = null, modifier = Modifier
.width(width.dp)
.height(90.dp))
Text(text = listData[it].first, fontSize = 18.sp)
}
}
}
}
}
列表的滚动监听与设置滚动位置
这里使用LazyColumn举例,因为其他形式的列表基本差不多。如果你不太了解下面代码中的rememberCoroutineScope,请参考博客 Android开发 Jetpack_Compose_6 附带效应 这属于附带效应中的知识。 如果你疑问为什么要使用rememberCoroutineScope?这是因为onClick不属于协程域和compose域,而rememberLazyListState需要协程域内才能调用。
效果图
代码
@Composable
fun APage() {
val listData = remember {
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
)
}
//协程范围
val coroutineScope = rememberCoroutineScope()
//关键代码!滚动状态监听
val listState = rememberLazyListState()
Column {
DescribeText(text = "当前位置 = ${listState.firstVisibleItemIndex}")
DescribeText(text = "滚动偏移量 = ${listState.firstVisibleItemScrollOffset}")
DescribeText(text = "是否正在滚动 = ${listState.isScrollInProgress}")
DescribeText(text = "可以向前滚动(是否可以向下滚) = ${listState.canScrollForward}")
DescribeText(text = "可以向后滚动(是否可以向上滚) = ${listState.canScrollBackward}")
Button(modifier = Modifier.fillMaxWidth(), onClick = {
coroutineScope.launch {
listState.scrollToItem(0)//关键代码!点击回到最上面
}
}) {
Text(text = "回到最上面")
}
LazyColumn(
state = listState,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize(),
) {
items(listData.size) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(20.dp)
) {
Image(
painter = painterResource(id = listData[it].second),
contentDescription = null,
modifier = Modifier
.width(100.dp)
.height(100.dp)
)
Text(
text = listData[it].first,
fontSize = 25.sp,
modifier = Modifier.padding(start = 80.dp)
)
}
}
}
}
}
@Composable
fun DescribeText(text:String) {
Text(text = text, color = Color.Black, textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth())
}
使用pullRefresh简单实现下拉刷新
这种方式实现需要compose版本为1.4.0,使用pullRefresh与PullRefreshIndicator的组合
效果图
代码
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun APage() {
val listData = remember {
mutableStateListOf(
R.mipmap.ic_fruit_apple,
R.mipmap.ic_fruit_banana,
R.mipmap.ic_fruit_avocado,
R.mipmap.ic_fruit_blueberry,
R.mipmap.ic_fruit_coconut,
R.mipmap.ic_fruit_grape,
R.mipmap.ic_fruit_hami_melon,
R.mipmap.ic_fruit_kiwifruit,
R.mipmap.ic_fruit_lemon,
R.mipmap.ic_fruit_litchi,
R.mipmap.ic_fruit_mango,
)
}
//是否正在刷新
val isRefreshing = remember {
mutableStateOf(false)
}
//refreshing为设置当前是否在刷新状态,如果设置为true则onRefresh不会触发
val pullRefreshState =
rememberPullRefreshState(refreshing = isRefreshing.value, onRefresh = {
isRefreshing.value = true
//这个回调里可以执行刷新数据的网络请求
})
Box(modifier = Modifier.pullRefresh(state = pullRefreshState, enabled = true)) {
LazyColumn(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
items(listData.size) {
Image(
painter = painterResource(id = listData[it]),
contentDescription = null,
modifier = Modifier
.padding(top = 20.dp, bottom = 20.dp)
.fillMaxWidth()
.height(100.dp)
)
}
}
//请注意。这个PullRefreshIndicator并不是我自己实现的compose方法,这是最新1.4.0版本新增的下拉指示物
//请注意,Modifier.align(Alignment.TopCenter)是上面Box的子属性
PullRefreshIndicator(
refreshing = isRefreshing.value,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
}
}
使用pullRefresh实现下拉刷新与上拉加载
这种方式实现需要compose版本为1.4.0,这个是较为自定义的实现方式,相对上面的比较复杂。
效果图
代码
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun APage() {
val listData = remember {
mutableStateListOf(
"苹果" to R.mipmap.ic_fruit_apple,
"香蕉" to R.mipmap.ic_fruit_banana,
"牛油果" to R.mipmap.ic_fruit_avocado,
"蓝莓" to R.mipmap.ic_fruit_blueberry,
"椰子" to R.mipmap.ic_fruit_coconut,
"葡萄" to R.mipmap.ic_fruit_grape,
"哈密瓜" to R.mipmap.ic_fruit_hami_melon,
"猕猴桃" to R.mipmap.ic_fruit_kiwifruit,
"柠檬" to R.mipmap.ic_fruit_lemon,
"荔枝" to R.mipmap.ic_fruit_litchi,
"芒果" to R.mipmap.ic_fruit_mango,
)
}
//是否显示下拉刷新
val isShowDownPullRefresh = remember {
mutableStateOf(false)
}
//是否显示上拉加载
val isShowUpPullLoading = remember {
mutableStateOf(false)
}
//滚动状态监听
val listState = rememberLazyListState()
Column(modifier = Modifier
//请注意!这里的pullRefresh是关键
.pullRefresh(onPull = { pullDelta ->
//拉动中
if (pullDelta > 0){
//触发下拉刷新,在这里可以实现请求刷新数据的逻辑
isShowDownPullRefresh.value = true
return@pullRefresh pullDelta
} else {
if (!listState.canScrollForward){
//触发上拉加载,在这里可以实现请求更多数据的逻辑
isShowUpPullLoading.value = true
}
//这里一定需要返回0,否者会影响向下滑动
return@pullRefresh 0F
}
}, onRelease = { flingVelocity ->
//松开释放,停止拉动
isShowDownPullRefresh.value = false
isShowUpPullLoading.value = false
return@pullRefresh flingVelocity
}, true)
) {
LazyColumn(
Modifier.fillMaxSize(),
state = listState,
verticalArrangement = Arrangement.Center
) {
//这里listData.size + 2, 加2是为了增加头部的”下拉刷新“与尾部的”上拉加载“
items(listData.size + 2) {
if (it == 0){
if (isShowDownPullRefresh.value){
Text(
text = "下拉刷新",
fontSize = 25.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
return@items
}
if (it == listData.size + 1){
if (isShowUpPullLoading.value){
Text(
text = "上拉加载",
fontSize = 25.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
return@items
}
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(20.dp)
) {
Image(
painter = painterResource(id = listData[it - 1].second),
contentDescription = null,
modifier = Modifier
.width(100.dp)
.height(100.dp)
)
Text(
text = listData[it - 1].first,
fontSize = 25.sp,
modifier = Modifier.padding(start = 80.dp)
)
}
}
}
}
}
使用SwipeToDismiss实现侧滑删除
关键代码是SwipeToDismiss与rememberDismissState
效果图
代码
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun APage() {
val listData = remember {
mutableStateListOf(
R.mipmap.ic_fruit_apple,
R.mipmap.ic_fruit_banana,
R.mipmap.ic_fruit_avocado,
R.mipmap.ic_fruit_blueberry,
R.mipmap.ic_fruit_coconut,
R.mipmap.ic_fruit_grape,
R.mipmap.ic_fruit_hami_melon,
R.mipmap.ic_fruit_kiwifruit,
R.mipmap.ic_fruit_lemon,
R.mipmap.ic_fruit_litchi,
R.mipmap.ic_fruit_mango,
)
}
val coroutineScope = rememberCoroutineScope()
Box() {
LazyColumn(
Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
items(listData.size) { position ->
//创建侧滑状态,请注意这里是在items里面创建的
val dismissState = rememberDismissState()
SwipeToDismiss(
state = dismissState,
//允许侧滑的方向
directions = setOf(DismissDirection.EndToStart),
dismissThresholds = {
//清除项目需要刷过的阈值,值越大需要拖动的距离就会越长
FixedThreshold(100.dp)
},
//background是侧滑后会显示出来的隐藏内容
background = {
//判断侧滑的方向
if (dismissState.isDismissed(DismissDirection.EndToStart)) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Gray)
) {
//删除图标
Icon(
imageVector = Icons.Default.Delete,
contentDescription = null,
modifier = Modifier
.padding(end = 30.dp)
.width(50.dp)
.height(50.dp)
.align(Alignment.CenterEnd)
.clickable {
coroutineScope.launch {
dismissState.reset()
//点击了删除,将这个数据删除掉
listData.removeAt(position)
}
}
)
//恢复图标
Icon(
imageVector = Icons.Default.SettingsBackupRestore,
contentDescription = null,
modifier = Modifier
.padding(end = 100.dp)
.width(50.dp)
.height(50.dp)
.align(Alignment.CenterEnd)
.clickable {
coroutineScope.launch {
//将侧滑状态恢复
dismissState.reset()
}
}
)
}
}
}) {
Image(
painter = painterResource(id = listData[position]),
contentScale = ContentScale.Inside,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.height(120.dp)
//使用drawBehind画一个上面和下面的边线
.drawBehind {
drawLine(
Color.Blue,
Offset(0f, 0f),
Offset(size.width, 0f),
strokeWidth = 3f
)
drawLine(
Color.Blue,
Offset(0f, size.height),
Offset(size.width, size.height),
strokeWidth = 3f
)
}
)
}
}
}
}
}
end
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17611791.html