Written in the book of the former

In the previous article, we learned about the ImageButton in Compose.

In this article, we will continue to learn about Switch, CheckBox and RadioButton. These three controls have a long history in human-computer interface.

The code in this article is based on version 1.0.1

Unless otherwise specifiedComposeBoth refer toJetpack compose

All the code in this article is available in WorkShop, and this code is concentrated under the Post28 package

A complete series of directory: making Pages | Denver | CSDN

Switch

We can use this control when the function is like a switch, such as “dark mode”, “airplane mode”.

use

fun Switch(
    checked: Boolean,
    onCheckedChange: ((Boolean) - >Unit)? , modifier:Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: SwitchColors = SwitchDefaults.colors()
)
Copy the code

After the previous study, the meaning of these parameters should be familiar.

To change color matching, you can build an instance of the SwitchColors object using the following API

object SwitchDefaults {
    fun colors(
        checkedThumbColor: Color = MaterialTheme.colors.secondaryVariant,
        checkedTrackColor: Color = checkedThumbColor,
        checkedTrackAlpha: Float = 0.54f,
        uncheckedThumbColor: Color = MaterialTheme.colors.surface,
        uncheckedTrackColor: Color = MaterialTheme.colors.onSurface,
        uncheckedTrackAlpha: Float = 0.38f,
        disabledCheckedThumbColor: Color = checkedThumbColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface),
        disabledCheckedTrackColor: Color = checkedTrackColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface),
        disabledUncheckedThumbColor: Color = uncheckedThumbColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface),
        disabledUncheckedTrackColor: Color = uncheckedTrackColor
            .copy(alpha = ContentAlpha.disabled)
            .compositeOver(MaterialTheme.colors.surface)
    ): SwitchColors
}
Copy the code

Based on the above knowledge, it is not difficult to guess that the following code does not really switch the function:

Switch(checked = false, onCheckedChange = {
    Log.d("tag"."onCheckChanged:$it")})Copy the code

The actual effect can be experienced in WorkShop

We need to build a “observed” data source that is observed by a node in Compose’s tree, the Switch, when its content changes and is reflected on the UI.

Such as:

var checked by rememberSaveable { mutableStateOf(false) }

Switch(checked = checked, onCheckedChange = {
    checked = it
    Log.d("tag"."onCheckChanged:$it")})Copy the code

Or:

val checked = remember { mutableStateOf(false) }

Switch(checked = checked.value, onCheckedChange = {
    checked.value = it
    Log.d("tag"."onCheckChanged:$it")})Copy the code

Of course, with Compose’s usage, these functions must exist at the same time@ComposeIn order to be captured by the target node"Observation"

The effect will be given at the end of the article

CheckBox

This control can be used when the form is: Selected/unselected.

use

fun Checkbox(
    checked: Boolean,
    onCheckedChange: ((Boolean) - >Unit)? , modifier:Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: CheckboxColors = CheckboxDefaults.colors()
)
Copy the code

Very similar to Switch, we can also change the color scheme:

object CheckboxDefaults {
   
    @Composable
    fun colors(
        checkedColor: Color = MaterialTheme.colors.secondary,
        uncheckedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
        checkmarkColor: Color = MaterialTheme.colors.surface,
        disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled),
        disabledIndeterminateColor: Color = checkedColor.copy(alpha = ContentAlpha.disabled)
    ): CheckboxColors
}
Copy the code

At this point, we realized,ComposeFixed theSwitch , CheckboxStyle style, if custom icon can be usedImageButton

The following code produces the simplest CheckBox available

var checked by rememberSaveable { mutableStateOf(false) }

Checkbox(checked = checked, onCheckedChange = {
    checked = it
    Log.d("tag"."onCheckChanged:$it")})Copy the code

Unlike native Android controls, the Checkbox in Compose cannot add text — which is Compose! .

One principle that permeates Compose is composition, obviously: Text should be used for Text sections.

RadioButton

Compose has no content for RadioButtonGroup. Or maybe I didn’t find it.

But that doesn’t matter! Compose doesn’t advocate maintaining state inside controls, which would violate the idea of pure functions. Moreover, we can easily use composition mode to plug in a piece of logic to achieve radio selection.

Note: In general UI design language, multiple selection should use Checkbox, implementation ideas are similar

How to use

@Composable
fun RadioButton(
    selected: Boolean,
    onClick: (() -> Unit)? , modifier:Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: RadioButtonColors = RadioButtonDefaults.colors()
)
Copy the code

Unlike the first two, RadioButton’s callback function is a clickback meaning.

In a UI design language, being clicked meansThe selectedIn radio, the group is logically kept only the target items selected.

The color scheme can be modified through the following API

object RadioButtonDefaults {
    
    @Composable
    fun colors(
        selectedColor: Color = MaterialTheme.colors.secondary,
        unselectedColor: Color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
        disabledColor: Color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
    ): RadioButtonColors
}
Copy the code

Build a radio group:

val tags = arrayListOf("A"."B"."C"."D")
val selectedTag = remember { mutableStateOf("") }

Row {
    tags.forEach {
        Row {
            RadioButton(
                selected = it == selectedTag.value,
                onClick = {
                    selectedTag.value = it
                }
            )

            Text(text = it)
        }

        Spacer(modifier = Modifier.width(20.dp))
    }
}
Copy the code

Similarly, the reader can create an observable data source using the proxy in any way he likes

val tags = arrayListOf("A"."B"."C"."D")
var selectedTag by rememberSaveable { mutableStateOf("") }


Row {
    tags.forEach {
        Row {
            RadioButton(
                selected = selectedTag == it,
                onClick = {
                    selectedTag = it
                }
            )

            Text(text = it,modifier = Modifier.clickable {
                selectedTag = it
            })
        }

        Spacer(modifier = Modifier.width(20.dp))
    }
}
Copy the code

Also, depending on the UX design style, you can flexibly add triggers to text sections.

Consider: Can the following code implement multiple selection?

val tags = arrayListOf("A"."B"."C"."D")
var selectedItems by rememberSaveable { mutableStateOf(linkedSetOf<String>()) }

Row {
    tags.forEach {
        Row {
            Checkbox(checked = selectedItems.contains(it), onCheckedChange = {selected->
                if (selected) {
                    selectedItems.add(it)
                } else {
                    selectedItems.remove(it)
                }
                Log.d("tag"."onCheckChanged:$it.$selected")
            })

            Text(text = it)
        }

        Spacer(modifier = Modifier.width(20.dp))
    }
}
Copy the code

Of course not! Changes in the contents of a Set cannot be observed! So how do you deal with that?

With our current knowledge, we can:

  • Bitwise operation
  • Add an observation source, combination judgment, but this will be checked by Lint for warnings
// Combination judgment will be warned
val tags = arrayListOf("A"."B"."C"."D")
val selectedItems by rememberSaveable { mutableStateOf(linkedSetOf<String>()) }
var version by rememberSaveable { mutableStateOf(0) } Row { tags.forEach { Row { Checkbox( checked = version ! =null && selectedItems.contains(it),
                onCheckedChange = { selected ->
                    if (selected) {
                        selectedItems.add(it)
                    } else {
                        selectedItems.remove(it)
                    }
                    version++
                    Log.d("tag"."onCheckChanged:$it.$selected")
                })

            Text(text = it)
        }

        Spacer(modifier = Modifier.width(20.dp))
    }
}
Copy the code

Is there a more elegant way to do it? Let’s keep that in mind to make our exploration more smooth and fun without having to spend too much energy at the beginning.

The effect

conclusion

Soon we’re at this stage again, which means new knowledge and more confusion, but don’t worry, all the puzzles will eventually be solved.