• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Android Jetpack Compose实现Spinner

武飞扬头像
Thousand Dust
帮助1

在我写项目的时候要用到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)
    }
}
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanheeaaji
系列文章
更多 icon
同类精品
更多 icon
继续加载