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

结语

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

相关推荐
微刻时光2 天前
Linux编译部署PHP环境
linux·开发语言·redis·git·php·apache·composer
pikazo6 天前
composer详解
android·php·composer
王大爷~11 天前
composer环境变量(phpstudy集成环境)无法使用问题
php·composer
小和尚敲代码20 天前
初识php库管理工具composer的体验【爽】使用phpword模板功能替换里面的字符串文本
开发语言·php·composer
十一侍卫1 个月前
composer使用
android·android studio·composer
清冬暖雪2 个月前
在Fiddler中的Composer使用post方法发送非法数据
前端·fiddler·composer
那天的烟花雨2 个月前
android display 笔记(五)HWC(Hardware Composer)
android·笔记·composer
xianyinsuifeng3 个月前
AWS无服务器 应用程序开发—第十七章 Application Composer
serverless·aws·composer
fonx3 个月前
如何设置PHP wkhtmltopdf
开发语言·php·composer
liuxin334455663 个月前
深入掌握Symfony与Composer:PHP依赖管理的艺术
php·composer·symfony