Для приведенного ниже кода я пытаюсь обновить selectedColorsText
всякий раз, когда пользователь нажимает кнопку. После выбора фон кнопки должен стать черным, а текст внутри него — белым. Если флажок снят, кнопка должна снова стать белой, а текст внутри нее — черным. Однако ни selectedColorsText
, ни цвета кнопок не обновляются должным образом. Что я делаю не так?
enum class ColorsEnum(val rawValue: String) {
red("Red"),
orange("Orange"),
yellow("Yellow"),
green("Green"),
blue("Blue")
}
@Composable
fun LazyGridView() {
var selectedColors by remember { mutableStateOf(arrayListOf<String>()) }
var selectedColorsText by remember { mutableStateOf("") }
Column (
verticalArrangement = Arrangement.spacedBy(10.dp)
)
{
Text("Selected Colors: $selectedColorsText")
LazyVerticalGrid(
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
items(ColorsEnum.values()) { color ->
Button(
onClick = {
if (selectedColors.contains(color.rawValue)) {
selectedColors.remove(color.rawValue)
selectedColorsText = selectedColors.joinToString(", ")
Log.d("selectedColors = ", "$selectedColors")
Log.d("selectedColorsText = ", "$selectedColorsText")
} else {
selectedColors.add(color.rawValue)
selectedColors.sortBy { colorName ->
ColorsEnum.values()
.find { it.rawValue == colorName }
?.let(ColorsEnum.values()::indexOf)
}
Log.d("selectedColors = ", "$selectedColors")
Log.d("selectedColorsText = ", "$selectedColorsText")
}
},
modifier = Modifier
.border(
width = 1.dp,
color = Color.Black,
shape = RoundedCornerShape(5.dp)
),
colors = ButtonDefaults.buttonColors(
backgroundColor =
if (isSystemInDarkTheme()) {
if (selectedColors.contains(color.rawValue)) {
Color.White
} else {
Color.Black
}
} else {
if (selectedColors.contains(color.rawValue)) {
Color.Black
} else {
Color.White
}
}
),
) {
Text(
text = color.rawValue,
style = TextStyle(
fontWeight = FontWeight.Bold,
color =
if (isSystemInDarkTheme()) {
if (selectedColors.contains(color.rawValue)) {
Color.Black
} else {
Color.White
}
} else {
if (selectedColors.contains(color.rawValue)) {
Color.White
} else {
Color.Black
}
}
),
)
}
}
}
}
}
Основная проблема в вашем коде в том, что вы пытаетесь изменить состояние, а не его содержимое. Из вашего кода я сделал простой пример. Кроме того, попробуйте взглянуть на подход UDF (Unidirectional Data Flow), он очень поможет в понимании того, как работать с композицией.
class MainActivity : ComponentActivity() {
// Create flow in ViewModel layer
private val _selectedColors = MutableStateFlow<List<ColorsEnum>>(listOf())
val selectedColors: StateFlow<List<ColorsEnum>> = _selectedColors
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestApplicationTheme {
// Subscribe on "selectedColors" flow in compose layer
val selectedList by selectedColors.collectAsStateWithLifecycle()
Grid(selectedList, ::processColor)
}
}
}
private fun processColor(color: ColorsEnum) {
// update selected colors in ViewModel layer
_selectedColors.update { selected ->
if (selected.contains(color)) {
selected - color
} else {
selected + color
}
}
}
}
enum class ColorsEnum(val rawValue: String) {
RED("Red"),
ORANGE("Orange"),
YELLOW("Yellow"),
GREEN("Green"),
BLUE("Blue")
}
@Composable
private fun Grid(selectedColors: List<ColorsEnum>, onClick: (ColorsEnum) -> Unit) {
LazyVerticalGrid(
columns = GridCells.Fixed(2),
verticalArrangement = Arrangement.spacedBy(10.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
items(ColorsEnum.values()) { color ->
GridItem(
color = color,
isChecked = selectedColors.contains(color),
// also look on command pattern for process different
// kinds callback and data from your UI
onClick = onClick
)
}
}
}
@Composable
private fun GridItem(
color: ColorsEnum,
isChecked: Boolean,
onClick: (ColorsEnum) -> Unit
) {
Button(
modifier = Modifier
.border(
width = 1.dp,
color = Color.Black,
shape = RoundedCornerShape(5.dp)
),
colors = ButtonDefaults.buttonColors(
backgroundColor =
if (isSystemInDarkTheme()) {
if (isChecked) {
Color.White
} else {
Color.Black
}
} else {
if (isChecked) {
Color.Black
} else {
Color.White
}
}
),
onClick = {
onClick.invoke(color)
},
) {
Text(
text = color.rawValue,
color = if (isSystemInDarkTheme()) {
if (isChecked) {
Color.Black
} else {
Color.White
}
} else {
if (isChecked) {
Color.White
} else {
Color.Black
}
}
)
}
}
@Vaz Для этого вам нужно создать собственное объявление, некоторый класс, который содержит ваше значение перечисления и состояние (отметить/снять отметку), и предоставить этот объект вместо перечисления в пользовательском интерфейсе.
В моем коде цвета были отсортированы по порядку перечисления. Как я могу сделать это в этом коде сейчас в функции
processColor
?