Android Compose实现数字选择器

Android Compose实现数字选择器

前言

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

借鉴的博客

Jetpack Compose : 超简单实现滚轮控件(WheelPicker)
Jetpack Compose 实现的时间选择组件

正文来啦

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

val listState = rememberLazyListState()
LazyColumn(
            modifier = Modifier,
            state = listState
        ) {}

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

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

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

 LazyColumn(
            modifier = Modifier,
            state = listState,
            flingBehavior = rememberSnapFlingBehavior(listState),
        ) 

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

val listState = rememberLazyListState(
            initialFirstVisibleItemIndex = selectIndex
        )
        LazyColumn(
            modifier = Modifier,
            state = listState,
            flingBehavior = rememberSnapFlingBehavior(listState),
        ){}

整个组件的源代码为:

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

    }
}

使用过程

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

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

结语

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

华丽转场

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值