Has*_*ssa 7 android kotlin android-jetpack-compose android-compose-textfield android-jetpack-compose-text
我想知道是否有针对 jetpack compose 的 Exposed 下拉菜单的解决方案?我无法在 jetpack compose 中为这个组件找到合适的解决方案。有什么帮助吗?
Gab*_*tti 13
目前1.0.x
没有内置组件。
您可以使用OutlinedTextField
+ DropdownMenu
。
这只是一个基本的(非常基本的)实现:
var expanded by remember { mutableStateOf(false) }
val suggestions = listOf("Item1","Item2","Item3")
var selectedText by remember { mutableStateOf("") }
var textfieldSize by remember { mutableStateOf(Size.Zero)}
val icon = if (expanded)
Icons.Filled.ArrowDropUp //it requires androidx.compose.material:material-icons-extended
else
Icons.Filled.ArrowDropDown
Column() {
OutlinedTextField(
value = selectedText,
onValueChange = { selectedText = it },
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
//This value is used to assign to the DropDown the same width
textfieldSize = coordinates.size.toSize()
},
label = {Text("Label")},
trailingIcon = {
Icon(icon,"contentDescription",
Modifier.clickable { expanded = !expanded })
}
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.width(with(LocalDensity.current){textfieldSize.width.toDp()})
) {
suggestions.forEach { label ->
DropdownMenuItem(onClick = {
selectedText = label
}) {
Text(text = label)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的版本。我在没有使用TextField
(所以没有键盘)的情况下实现了这一点。有“常规”和“大纲”版本。
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.ZeroCornerSize
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.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
// ExposedDropDownMenu will be added in Jetpack Compose 1.1.0.
// This is a reimplementation while waiting.
// See https://stackoverflow.com/questions/67111020/exposed-drop-down-menu-for-jetpack-compose/6904285
@Composable
fun SimpleExposedDropDownMenu(
values: List<String>,
selectedIndex: Int,
onChange: (Int) -> Unit,
label: @Composable () -> Unit,
modifier: Modifier = modifier,
backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.BackgroundOpacity),
shape: Shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize)
) {
SimpleExposedDropDownMenuImpl(
values = values,
selectedIndex = selectedIndex,
onChange = onChange,
label = label,
modifier = modifier,
backgroundColor = backgroundColor,
shape = shape,
decorator = { color, width, content ->
Box(
Modifier
.drawBehind {
val strokeWidth = width.value * density
val y = size.height - strokeWidth / 2
drawLine(
color,
Offset(0f, y),
Offset(size.width, y),
strokeWidth
)
}
) {
content()
}
}
)
}
@Composable
fun SimpleOutlinedExposedDropDownMenu(
values: List<String>,
selectedIndex: Int,
onChange: (Int) -> Unit,
label: @Composable () -> Unit,
modifier: Modifier = modifier,
backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.BackgroundOpacity),
shape: Shape = MaterialTheme.shapes.small
) {
SimpleExposedDropDownMenuImpl(
values = values,
selectedIndex = selectedIndex,
onChange = onChange,
label = label,
modifier = modifier,
backgroundColor = backgroundColor,
shape = shape,
decorator = { color, width, content ->
Box(
Modifier
.border(width, color, shape)
) {
content()
}
}
)
}
@Composable
private fun SimpleExposedDropDownMenuImpl(
values: List<String>,
selectedIndex: Int,
onChange: (Int) -> Unit,
label: @Composable () -> Unit,
modifier: Modifier,
backgroundColor: Color,
shape: Shape,
decorator: @Composable (Color, Dp, @Composable () -> Unit) -> Unit
) {
var expanded by remember { mutableStateOf(false) }
var textfieldSize by remember { mutableStateOf(Size.Zero) }
val indicatorColor =
if (expanded) MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high)
else MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.UnfocusedIndicatorLineOpacity)
val indicatorWidth = (if (expanded) 2 else 1).dp
val labelColor =
if (expanded) MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high)
else MaterialTheme.colors.onSurface.copy(ContentAlpha.medium)
val trailingIconColor = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.IconOpacity)
val rotation: Float by animateFloatAsState(if (expanded) 180f else 0f)
val focusManager = LocalFocusManager.current
Column(modifier = modifier.width(IntrinsicSize.Min)) {
decorator(indicatorColor, indicatorWidth) {
Box(
Modifier
.fillMaxWidth()
.background(color = backgroundColor, shape = shape)
.onGloballyPositioned { textfieldSize = it.size.toSize() }
.clip(shape)
.clickable {
expanded = !expanded
focusManager.clearFocus()
}
.padding(start = 16.dp, end = 12.dp, top = 7.dp, bottom = 10.dp)
) {
Column(Modifier.padding(end = 32.dp)) {
ProvideTextStyle(value = MaterialTheme.typography.caption.copy(color = labelColor)) {
label()
}
Text(
text = values[selectedIndex],
modifier = Modifier.padding(top = 1.dp)
)
}
Icon(
imageVector = Icons.Filled.ArrowDropDown,
contentDescription = "Change",
tint = trailingIconColor,
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(top = 4.dp)
.rotate(rotation)
)
}
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.width(with(LocalDensity.current) { textfieldSize.width.toDp() })
) {
values.forEachIndexed { i, v ->
val scope = rememberCoroutineScope()
DropdownMenuItem(
onClick = {
onChange(i)
scope.launch {
delay(150)
expanded = false
}
}
) {
Text(v)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您正在使用material3
较新版本的 compose(这适用于v1.3.1
),则已DropdownMenuItem
略有更改。文本现在必须是属性(而不是@Composable
)。
您仍然需要选择加入实验性 api @OptIn(ExperimentalMaterial3Api::class)
,.
此示例位于androidx.compose.material3文档中。
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
val options = listOf("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
var expanded by remember { mutableStateOf(false) }
var selectedOptionText by remember { mutableStateOf(options[0]) }
// We want to react on tap/press on TextField to show menu
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
) {
TextField(
// The `menuAnchor` modifier must be passed to the text field for correctness.
modifier = Modifier.menuAnchor(),
readOnly = true,
value = selectedOptionText,
onValueChange = {},
label = { Text("Label") },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
colors = ExposedDropdownMenuDefaults.textFieldColors(),
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
options.forEach { selectionOption ->
DropdownMenuItem(
text = { Text(selectionOption) },
onClick = {
selectedOptionText = selectionOption
expanded = false
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
)
}
}
}
Run Code Online (Sandbox Code Playgroud)
按照“旧方法”执行此操作,我遇到了以下错误Text(text = selectionOption)
:
No value passed for parameter 'text'
Type mismatch: inferred type is () -> Unit but MutableInteractionSource was expected
@Composable invocations can only happen from the context of a @Composable function
小智 5
这就是我为获得与文本字段相同的宽度所做的工作:复制和修改 Gabriele 的答案。
var expanded by remember { mutableStateOf(false) }
val suggestions = listOf("Item1","Item2","Item3")
var selectedText by remember { mutableStateOf("") }
var dropDownWidth by remember { mutableStateOf(0) }
val icon = if (expanded)
Icons.Filled.....
else
Icons.Filled.ArrowDropDown
Column() {
OutlinedTextField(
value = selectedText,
onValueChange = { selectedText = it },
modifier = Modifier.fillMaxWidth()
.onSizeChanged {
dropDownWidth = it.width
},
label = {Text("Label")},
trailingIcon = {
Icon(icon,"contentDescription", Modifier.clickable { expanded = !expanded })
}
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.width(with(LocalDensity.current){dropDownWidth.toDp()})
) {
suggestions.forEach { label ->
DropdownMenuItem(onClick = {
selectedText = label
}) {
Text(text = label)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2226 次 |
最近记录: |