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

结语

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

相关推荐
小张课程5 天前
新-Jetpack Compose:从上手到进阶再到高手
composer
Flash Dog6 天前
Composer 版本不匹配问题:
php·composer
苏琢玉8 天前
一个小项目的记录:PHP 分账组件
php·composer
U_U20469 天前
Java在云原生时代下的微服务架构优化与实践
composer
Pika13 天前
深入浅出 Compose 测量机制
android·android jetpack·composer
Trainer210718 天前
十分钟搭建thinkphp开发框架
php·apache·phpstorm·composer
_Sem19 天前
Compose 动画 + KMM 跨平台开发:从传统View到现代声明式UI动画
android·composer
linuxoffer23 天前
composer 安装与开启PHP扩展支持
开发语言·php·composer
宁小法1 个月前
PHP 线上环境 Composer 依赖包更新部署指南
php·composer·更新部署
苏琢玉1 个月前
用 PHP 玩向量数据库:一个从小说网站开始的小尝试
php·composer