Android Compose实现数字选择器

前言

好久没有写博客了,现在来写一篇关于compose的实现数字滚动选择器吧,这是因为我公司项目里面的一个组件有用到类似的功能,在刚开始的时候我也去网上查找借鉴了很多的大佬写的实现类似的功能,后面我根据大佬写的去研究了一下重新改了一下比较适合我的一个写法,也认为会比较通用的一个方法,话不多说先上成品给大家看看。

借鉴的博客

Jetpack Compose : 超简单实现滚轮控件(WheelPicker)

Jetpack Compose 实现的时间选择组件

正文来啦

首先在做这个滚动的列表的时候,想到的是Column,虽然要使这个滑动起来还需要加.verticalScroll(rememberScrollState())这个属性就能够使其滑动起来,不过后来我还是选择了compose中的列表组件LazyColumn;这样我们就自然而然的写好了其大概的框架。

kotlin 复制代码
val listState = rememberLazyListState()
LazyColumn(
            modifier = Modifier,
            state = listState
        ) {}

然后为了让其能够复用,在里面的Item直接以数据的大小来生成

kotlin 复制代码
items(size) { index ->
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(itemHeight),
                    contentAlignment = Alignment.Center,
                ) {
                //这里将要展示的数据给回调到外面在外面进行自定义展示
                    content(data[index])
                }
            }

同时我们去查看LazyColunm的源码可以发现 :flingBehavior接口来指定投掷行为。当拖动以scrollable中的velocity结束时,将调用performFling以通过ScrollScope.scrollBy执行fling动画和更新状态。因此我们可以将LazyColumn更改一下:

kotlin 复制代码
 LazyColumn(
            modifier = Modifier,
            state = listState,
            flingBehavior = rememberSnapFlingBehavior(listState),
        ) 

如果要设置初始值的话可以根据rememberLazyListState里面的属性initialFirstVisibleItemIndex来进行设置因此完整的LazyColum的配置如下:

kotlin 复制代码
val listState = rememberLazyListState(
            initialFirstVisibleItemIndex = selectIndex
        )
        LazyColumn(
            modifier = Modifier,
            state = listState,
            flingBehavior = rememberSnapFlingBehavior(listState),
        ){}

整个组件的源代码为:

kotlin 复制代码
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun <T> ListNumberPicker(
    data: List<T>,
    selectIndex: Int,
    visibleCount: Int,
    modifier: Modifier = Modifier,
    onSelect: (index: Int, item: T) -> Unit,
    content: @Composable (item: T) -> Unit,
) {
    BoxWithConstraints(modifier = modifier, propagateMinConstraints = true) {
        val pickerHeight = maxHeight
        val size = data.size
        val itemHeight = pickerHeight / visibleCount
        val listState = rememberLazyListState(
            initialFirstVisibleItemIndex = selectIndex
        )
        val firstVisibleItemIndex by remember { derivedStateOf { listState.firstVisibleItemIndex } }
        LazyColumn(
            modifier = Modifier,
            state = listState,
            flingBehavior = rememberSnapFlingBehavior(listState),
        ) {
        //占据相应的高度比如显示5个那么中间那个是选中的其他的就是非选中,但也要占据一定的空间。
            for (i in 1..visibleCount / 2) {
                item {
                    Surface(modifier = Modifier.height(itemHeight)) {}
                }
            }
            items(size) { index ->
            //预防滑动的时候出现数组越界
                if (firstVisibleItemIndex >= size) {
                    onSelect(size - 1, data[size - 1])
                } else {
                    onSelect(firstVisibleItemIndex, data[firstVisibleItemIndex])
                }
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(itemHeight),
                    contentAlignment = Alignment.Center,
                ) {
                    content(data[index])
                }
            }
            for (i in 1..visibleCount / 2) {
                item {
                    Surface(modifier = Modifier.height(itemHeight)) {}
                }
            }
        }

    }
}

使用过程

不多说其他的,现在就是直接用就行了

kotlin 复制代码
 Row(
            Modifier
                .wrapContentHeight()
                .fillMaxWidth(),
            Arrangement.Center,
            Alignment.CenterVertically
        ) {
            //年
            var selectYear by remember {
                mutableIntStateOf(selectDate.getYearr())
            }

            val yearData = LinkedList<Int>().apply {
                for (i in 1970..nowDate.getYearr()) {
                    add(i)
                }
            }

            ListNumberPicker(
                data = yearData,
                selectIndex = yearData.indexOf(selectYear),
                visibleCount = 3,
                modifier = Modifier
                    .height(150.dp)
                    .width(89.dp)
                    .background(
                        color = colorResource(id = R.color.home_background).copy(alpha = 0.3f),
                        shape = RoundedCornerShape(10.dp)
                    ),
                onSelect = { _, item ->
                    selectYear = item
                    yearCall(selectYear)
                }
            ) {
                Column(
                    modifier = Modifier.fillMaxWidth(),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                //判断是否是选中的状态,选中要展示的样式和非选中的样式
                    if (it == selectYear) {
                        Divider(
                            modifier = Modifier
                                .width(89.dp)
                                .padding(horizontal = 17.5.dp),
                            color = colorResource(id = R.color.color_btn_line).copy(alpha = 0.5f)
                        )
                        Text(
                            text = "$it", color = Color.White,
                            fontWeight = FontWeight.Bold,
                            textAlign = TextAlign.Center,
                            fontSize = 29.sp
                        )
                        Divider(
                            modifier = Modifier
                                .width(89.dp)
                                .padding(horizontal = 17.5.dp),
                            color = colorResource(id = R.color.color_btn_line).copy(alpha = 0.5f)
                        )
                    } else {
                        Text(
                            text = "$it",
                            color = Color.White.copy(alpha = 0.3f),
                            fontWeight = FontWeight.Bold,
                            textAlign = TextAlign.Center,
                            fontSize = 18.sp
                        )
                    }
                }
            }

结语

写到现在感觉自己写的很粗糙,但还有很多不足之处,这篇文章也是根据前人的基础来进行完成的。再次感谢分享出自己经验的大佬。如果文章阅读当中有不同的想法欢迎提出来,让我们一起进步。

相关推荐
用户3074596982071 天前
如何一步步将自己的 PHP 包上传到 Packagist
composer
落华X9 天前
windows安装多个版本composer
php·composer
明天依旧下着大雨2 个月前
PHP8.2.9NTS版本使用composer报错,扩展找不到的问题处理
php·composer
苏琢玉2 个月前
顺手写了个地址解析小工具,支持在线用,也能接 PHP 项目里
php·composer
赵大的程序屋2 个月前
Jetpack Compose基础组件之 Button
composer
赵大的程序屋2 个月前
Jetpack Compose基础组件之 Text
composer
一条九漏鱼3 个月前
数制——FPGA
composer
左小左3 个月前
用Compose撸一个CoordinatorLayout 🔥🔥🔥
android·android jetpack·composer
王大爷~3 个月前
composer 错误汇总
android·php·composer
lihuang3194 个月前
Composer如何通过GitHub Personal Access Token安装私有包:完整教程
github·php·composer