英文:
Jetpack Compose DropDown with generic implementation
问题
Here's the translated code portion:
目前,我有一个使用特定类型实现的 `DropDown`。我想要将这个下拉菜单变成通用的,这样我就可以在任何地方重用它,并且去掉样板代码。
**当前实现**
```kotlin
@Composable
fun SingleLineDropDown(optionsFieldState: OptionsFieldState<String>, hintText: String, isEditable: Boolean = true) {
var expanded by remember { mutableStateOf(false) }
var textFieldSize by remember { mutableStateOf(Size.Zero) }
val icon = if (expanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown
val modifier = if (isEditable) {
Modifier.clickable { expanded = !expanded }
} else Modifier
Box {
EditableRow(
value = optionsFieldState.value ?: "",
onValueChange = { optionsFieldState.value = it },
enabled = false,
error = optionsFieldState.error.orEmpty(),
modifier = modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
// 这个值用于分配给下拉菜单相同的宽度
textFieldSize = coordinates.size.toSize()
},
hintText = hintText,
trailingIcon = {
Icon(
icon, null, modifier
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
disabledTextColor = if (isEditable) MaterialTheme.colors.onBackground else Color.LightGray
)
)
if (isEditable) {
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(with(LocalDensity.current) { textFieldSize.width.toDp() })
) {
optionsFieldState.options.forEach { option ->
DropdownMenuItem(onClick = {
optionsFieldState.value = option
expanded = !expanded
}) {
Text(text = option)
}
}
}
}
}
}
调用函数
SingleLineDropDown(
optionsFieldState = state.province,
hintText = stringResource(id = R.string.province_territory),
isEditable = state.isAddressEditable
)
OptionsFieldState 是 Compose UI 的自定义状态类
open class OptionsFieldState<T>(
initialValue: T?,
options: List<T> = listOf(),
validators: List<Validator> = listOf(),
onValueChanged: () -> Unit = {},
)
英文:
Currently, I have a DropDown
implemented with particular types. I would like to make that drop-down menu generic so I can reuse it everywhere with custom objects and remove boilerplate code.
Current Implementation
@Composable
fun SingleLineDropDown(optionsFieldState: OptionsFieldState<String>, hintText: String, isEditable: Boolean = true) {
var expanded by remember { mutableStateOf(false) }
var textFieldSize by remember { mutableStateOf(Size.Zero) }
val icon = if (expanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown
val modifier = if (isEditable) {
Modifier.clickable { expanded = !expanded }
} else Modifier
Box {
EditableRow(
value = optionsFieldState.value ?: "",
onValueChange = { optionsFieldState.value = it },
enabled = false,
error = optionsFieldState.error.orEmpty(),
modifier = modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
//This value is used to assign to the DropDown the same width
textFieldSize = coordinates.size.toSize()
},
hintText = hintText,
trailingIcon = {
Icon(
icon, null, modifier
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
disabledTextColor = if (isEditable) MaterialTheme.colors.onBackground else Color.LightGray
)
)
if (isEditable) {
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.width(with(LocalDensity.current) { textFieldSize.width.toDp() })
) {
optionsFieldState.options.forEach { option ->
DropdownMenuItem(onClick = {
optionsFieldState.value = option
expanded = !expanded
}) {
Text(text = option)
}
}
}
}
}
}
Caller function
SingleLineDropDown(
optionsFieldState = state.province,
hintText = stringResource(id = R.string.province_territory),
isEditable = state.isAddressEditable
)
OptionFiledState is custom state for compose UI
open class OptionsFieldState<T>(
initialValue: T?,
options: List<T> = listOf(),
validators: List<Validator> = listOf(),
onValueChanged: () -> Unit = {},
)
答案1
得分: 1
If you want your drop-down menu to be applicable to generics custom objects, you can change your SingleLineDropDown
function with generics, along side one extra parameters for the function to make it work.
@Composable
fun <T> SingleLineDropDown(
optionsFieldState: OptionsFieldState<T>,
hintText: String,
isEditable: Boolean = true,
displayText: @Composable (T) -> Unit // Composable function to display the selected option
) {
// Rest of your code...
Box {
EditableRow(
// Rest of your code...
)
if (isEditable) {
DropdownMenu(
// Rest of your code...
) {
optionsFieldState.options.forEach { option ->
DropdownMenuItem(onClick = {
optionsFieldState.value = option
expanded = !expanded
}) {
displayText(option) // Call the displayText function to show the selected option
}
}
}
}
}
}
Now you can use it the following way:
SingleLineDropDown(
optionsFieldState = state.province,
hintText = stringResource(id = R.string.province_territory),
isEditable = state.isAddressEditable
) { option ->
// Customize how the selected option is displayed
Text(text = option.name) // Assuming the custom object has a "name" property
}
Update
You can also provide a default value for the generic to be used in the EditableRow
, something like this:
@Composable
fun <T> SingleLineDropDown(
optionsFieldState: OptionsFieldState<T>,
hintText: String,
isEditable: Boolean = true,
displayText: @Composable (T) -> Unit,
defaultOptionValue: T
) {
// Rest of your code...
Box {
EditableRow(
value = optionsFieldState.value ?: defaultOptionValue,
onValueChange = { optionsFieldState.value = it },
enabled = false,
error = optionsFieldState.error.orEmpty(),
modifier = modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
// This value is used to assign to the DropDown the same width
textFieldSize = coordinates.size.toSize()
},
hintText = hintText,
trailingIcon = {
Icon(
icon, null, modifier
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
disabledTextColor = if (isEditable) MaterialTheme.colors.onBackground else Color.LightGray
)
)
// Rest of your code...
}
}
英文:
If you want your drop-down menu to be applicable to generics custom objects, you can change your SingleLineDropDown
function with generics, along side one extra parameters for the function to make it work
Follow this example to understand how it can be done:
@Composable
fun <T> SingleLineDropDown(
optionsFieldState: OptionsFieldState<T>,
hintText: String,
isEditable: Boolean = true,
displayText: @Composable (T) -> Unit // Composable function to display the selected option
) {
// Rest of your code...
Box {
EditableRow(
// Rest of your code...
)
if (isEditable) {
DropdownMenu(
// Rest of your code...
) {
optionsFieldState.options.forEach { option ->
DropdownMenuItem(onClick = {
optionsFieldState.value = option
expanded = !expanded
}) {
displayText(option) // Call the displayText function to show the selected option
}
}
}
}
}
}
Now you can use it the following way:
SingleLineDropDown(
optionsFieldState = state.province,
hintText = stringResource(id = R.string.province_territory),
isEditable = state.isAddressEditable
) { option ->
// Customize how the selected option is displayed
Text(text = option.name) // Assuming the custom object has a "name" property
}
Update
You can also provide a default value for the generic to be used in the EditableRow
, something like that:
@Composable
fun <T> SingleLineDropDown(
optionsFieldState: OptionsFieldState<T>,
hintText: String,
isEditable: Boolean = true,
displayText: @Composable (T) -> Unit,
defaultOptionValue : T
) {
// Rest of your code...
Box {
EditableRow(
value = optionsFieldState.value ?: defaultOptionValue,
onValueChange = { optionsFieldState.value = it },
enabled = false,
error = optionsFieldState.error.orEmpty(),
modifier = modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
//This value is used to assign to the DropDown the same width
textFieldSize = coordinates.size.toSize()
},
hintText = hintText,
trailingIcon = {
Icon(
icon, null, modifier
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
disabledTextColor = if (isEditable) MaterialTheme.colors.onBackground else Color.LightGray
)
)
// Rest of your code...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论