Android Jetpack Compose实现Spinner

在我写项目的时候要用到Spinner。找了找,目前compose里好像还没有类似Spinner的函数。使用AndroidView来使用Spinner的话又会出现一些问题。于是自己使用Compose实现了个简易的Spinner组件。

先写好Spinner函数

/**
 * @param modifier 应用于布局的修饰符
 * @param dataArray 数据数组
 * @param position 选择的item
 * @param expanded 是否展开
 * @param arrowColor 下拉箭头颜色
 * @param arrowSize 下拉箭头大小
 * @param maxShowHeight 下拉列表最大高度
 * @param enabled 是否启用
 * @param selectChange 选择item状态改变回调
 * @param expandedChange 列表 展开/收起 状态改变会标
 * @param itemContent 描述item的Compose组件内容。lambda参数:data为数据(dataArray[index]),modifier里写好了用于监听item的点击选择回调
 */
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun <T> Spinner(
    modifier: Modifier = Modifier,
    dataArray: Array<T>?,
    position: Int = 0,
    expanded: Boolean = false,
    arrowColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
    arrowSize: Dp = 30.dp,
    maxShowHeight: Dp = 100.dp,
    enabled: Boolean = true,
    selectChange: (Int, T) -> Unit = { _,_ -> },
    expandedChange: (Boolean) -> Unit = {},
    itemContent: @Composable (data: T, modifier: Modifier) -> Unit,
) {

}

后面代码中用到了实验性api:AnimatedVisibility,要加入@OptIn(ExperimentalAnimationApi::class)

然后写好Spinner的轮廓

Column(modifier = modifier) {
        Row(modifier = Modifier.clickable(enabled = enabled) { expandedChange.invoke(!expanded) }) {
            //item父容器
            Box(modifier = Modifier.align(Alignment.CenterVertically)) {
            //item组件
            }
            //下拉箭头
            Icon(
                imageVector = Icons.Default.ArrowDropDown,
                contentDescription = if (expanded) "下拉" else "上拉",
                tint = arrowColor,
                modifier = Modifier
                    .size(arrowSize)
                    .align(Alignment.CenterVertically)
                    .rotate(degrees)
            )
        }
        //下拉列表
    }

Column作为最外层的容器,包住Spinner的选中内容和下拉列表。
Row里有左右两个东西,item和下拉箭头
Box包住item方便控制摆放位置
Icon则是下拉箭头
下拉列表待会在加上
在这里插入图片描述

再然后将描述item的Compose组件内容lambda加到item父容器里面调用

//dataArray为空时使用Spacer占位
                if (dataArray == null) {
                    Spacer(modifier = Modifier.size(width = 100.dp, height = 38.dp))
                } else {
                    //调用itemContent显示item
                    itemContent.invoke(dataArray[position], Modifier)
                }

最后将下拉列表加到Row的下面就好了

//dataArray不为空时才显示下拉列表
        if (dataArray != null) {
            //显示/隐藏动画
            AnimatedVisibility(expanded) {
                LazyColumn(modifier = Modifier.height(showHeight),
                    content = {
                        items(dataArray.size) {
                            itemContent.invoke(
                                dataArray[it],
                                Modifier.clickable(enabled = enabled) { selectChange.invoke(it) }
                            )
                        }
                    }
                )
            }
        }

        //禁用时收起下拉列表
        if (!enabled && expanded) expandedChange.invoke(false)

写一个预览实例显示看看效果

@Preview
@Composable
fun SpinnerPreview() {
    val dataArray = arrayOf("Test1", "Test2", "Test3")
    var position: Int by remember { mutableStateOf(0) }
    var expanded: Boolean by remember{ mutableStateOf(false) }
    Spinner(
        modifier = Modifier.background(Color.White),
        dataArray = dataArray,
        position = position,
        expanded = expanded,
        selectChange = { i, data ->
               position = i
        },
        expandedChange = {
            expanded = it
        }
    ) { data, modifier ->
        Text(text = data, modifier)
    }
}

看看效果
在这里插入图片描述
在这里插入图片描述

完整代码

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

/**
 * @param modifier 应用于布局的修饰符
 * @param dataArray 数据数组
 * @param position 选择的item
 * @param expanded 是否展开
 * @param arrowColor 下拉箭头颜色
 * @param arrowSize 下拉箭头大小
 * @param maxShowHeight 下拉列表最大高度
 * @param enabled 是否启用
 * @param selectChange 选择item状态改变回调
 * @param expandedChange 列表 展开/收起 状态改变会标
 * @param itemContent 描述item的Compose组件内容。lambda参数:data为数据(dataArray[index]),modifier里写好了用于监听item的点击选择回调
 */
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun <T> Spinner(
    modifier: Modifier = Modifier,
    dataArray: Array<T>?,
    position: Int = 0,
    expanded: Boolean = false,
    arrowColor: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current),
    arrowSize: Dp = 30.dp,
    maxShowHeight: Dp = 100.dp,
    enabled: Boolean = true,
    selectChange: (Int, T) -> Unit = { _,_ -> },
    expandedChange: (Boolean) -> Unit = {},
    itemContent: @Composable (data: T, modifier: Modifier) -> Unit,
) {
    //下拉箭头旋转角度
    val degrees: Float by animateFloatAsState(targetValue = if (expanded) 0f else 90f)

    Column(modifier = modifier) {
        Row(modifier = Modifier.clickable(enabled = enabled) { expandedChange.invoke(!expanded) }) {
            Box(modifier = Modifier.align(Alignment.CenterVertically)) {
                //dataArray为空时使用Spacer占位
                if (dataArray == null) {
                    Spacer(modifier = Modifier.size(width = 100.dp, height = 38.dp))
                } else {
                    //调用itemContent显示item
                    itemContent.invoke(dataArray[position], Modifier)
                }
            }
            Icon(
                imageVector = Icons.Default.ArrowDropDown,
                contentDescription = if (expanded) "下拉" else "收起",
                tint = arrowColor,
                modifier = Modifier
                    .size(arrowSize)
                    .align(Alignment.CenterVertically)
                    .rotate(degrees)
            )
        }

        //dataArray不为空时才显示下拉列表
        if (dataArray != null) {
            //显示/隐藏动画
            AnimatedVisibility(expanded) {
                LazyColumn(modifier = Modifier.heightIn(max = maxShowHeight),
                    content = {
                        items(dataArray.size) {
                            itemContent.invoke(
                                dataArray[it],
                                Modifier.clickable(enabled = enabled) { selectChange.invoke(it, dataArray[it]) }
                            )
                        }
                    }
                )
            }
        }

        //禁用时收起下拉列表
        if (!enabled && expanded) expandedChange.invoke(false)
    }
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值