这一节主要了解一下Compose中的FilterChip,在Compose中,FilterChip是Material Design组件库提供的一种交互式芯片,主要用于提供多选或单选的筛选条件。简单总结如下
API
selected:控制Chip是否选中
onClick:点击事件回调
label:Chip显示的文本内容
leadingIcon:左侧图标
trailingIcon: 右侧图标(常用于显示选中状态)
colors:控制Chip在不同状态下的颜色
border:边框样式
shape:形状配置
enabled:是否可用状态
场景:
1. 内容筛选,适用于需要按类别、属性过滤列表/网格内容的场景
2. 标签式分类选择,适用于需要按标签对内容进行分类的场景,支持单选或多选
3. 状态切换型筛选,适用于需要按“状态”过滤内容的场景,状态通常是二元或少量固定选项
栗子:
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material.icons.filled.Home
import androidx.compose.material3.FilterChip
import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
@Composable
fun TestFilterChipExample() {
var selected by remember { mutableStateOf(false) }
FilterChip( selected = selected,
onClick = { selected = !selected },
label = { Text("Filter chip") },
leadingIcon = if (selected) { {
Icon( imageVector = Icons. Filled.Done,
contentDescription = "Localized Description",
modifier = Modifier.size(FilterChipDefaults. IconSize)
) } } else { {
Icon( imageVector = Icons. Filled.Home
,contentDescription = "Localized description",
modifier = Modifier. size(FilterChipDefaults. IconSize)
)
}
} )
}
import android.annotation.SuppressLint
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
data class Article(
val id: String,
val title: String,
val tags: List<String>
)
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun TestTagFilterExample() {
val articles = remember {
listOf(
Article("1", "Jetpack Compose基础", listOf("Android", "Compose")),
Article("2", "Kotlin协程", listOf("Kotlin", "协程")),
Article("3", "Material3设计规范", listOf("Android", "UI")),
Article("4", "Compose状态管理", listOf("Android", "Compose", "状态管理"))
)
}
val allTags = remember {
articles.flatMap { it.tags }.distinct().toMutableStateList()
}
var selectedTags by remember { mutableStateOf(setOf<String>()) }
var newTag by remember { mutableStateOf("") }
val filteredArticles by derivedStateOf {
if (selectedTags.isEmpty()) articles
else articles.filter { article ->
article.tags.any { selectedTags.contains(it) }
}
}
Column(modifier = Modifier.fillMaxSize()) {
Text(
text = "文章标签筛选",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(16.dp)
)
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
OutlinedTextField(
value = newTag,
onValueChange = { newTag = it },
label = { Text("添加新标签") },
modifier = Modifier.weight(1f),
singleLine = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {
if (newTag.isNotBlank() && !allTags.contains(newTag)) {
allTags.add(newTag)
newTag = ""
}
})
)
Button(
onClick = {
if (newTag.isNotBlank() && !allTags.contains(newTag)) {
allTags.add(newTag)
newTag = ""
}
},
modifier = Modifier.padding(start = 8.dp),
enabled = newTag.isNotBlank()
) {
Text("添加")
}
}
Text(
text = "标签列表",
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(16.dp, 8.dp, 16.dp, 0.dp)
)
FlowRow(
modifier = Modifier.padding(12.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
allTags.forEach { tag ->
val isSelected = selectedTags.contains(tag)
FilterChip(
selected = isSelected,
onClick = {
selectedTags = if (isSelected) {
selectedTags - tag
} else {
selectedTags + tag
}
},
label = { Text(tag) },
leadingIcon = if (isSelected) {
{
Icon(
imageVector = Icons.Filled.Done,
contentDescription = "已选择",
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
}
} else null,
colors = FilterChipDefaults.filterChipColors(
containerColor = if (isSelected)
MaterialTheme.colorScheme.primaryContainer
else MaterialTheme.colorScheme.surface,
labelColor = if (isSelected)
MaterialTheme.colorScheme.onPrimaryContainer
else MaterialTheme.colorScheme.onSurface,
iconColor = MaterialTheme.colorScheme.onPrimaryContainer
),
border = FilterChipDefaults.filterChipBorder(
borderColor = MaterialTheme.colorScheme.outline,
borderWidth = 1.dp,
enabled = true,
selected = isSelected,
selectedBorderColor = MaterialTheme.colorScheme.primary,
disabledBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.38f),
disabledSelectedBorderColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f),
selectedBorderWidth = 1.dp
),
shape = MaterialTheme.shapes.small
)
}
}
Text(
text = "筛选结果 (${filteredArticles.size})",
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(16.dp)
)
LazyColumn(modifier = Modifier.weight(1f)) {
items(filteredArticles) { article ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = article.title,
fontWeight = FontWeight.Bold,
fontSize = 16.sp
)
Spacer(modifier = Modifier.height(8.dp))
FlowRow(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
article.tags.forEach { tag ->
Box(
modifier = Modifier
.background(
if (selectedTags.contains(tag))
MaterialTheme.colorScheme.primaryContainer
else MaterialTheme.colorScheme.surfaceVariant,
shape = MaterialTheme.shapes.small
)
.padding(4.dp, 2.dp)
) {
Text(
text = tag,
fontSize = 12.sp,
color = if (selectedTags.contains(tag))
MaterialTheme.colorScheme.onPrimaryContainer
else MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
}
}
}
}
注意:
1 明确“选中即生效”的设计原则,避免将其用于“需要批量选择后统一生效”的场景,选中状态变化时,应立即更新筛选结果
2 区分“单选”和“多选”场景,多选场景:允许同时选中多个芯片,筛选逻辑为 “满足任一条件”;单选场景:选中新芯片时,需自动取消同组其他芯片的选中状态。
3 正确处理FilterChipDefaults参数,Material 3的FilterChip参数严格,需注意border参数需指定所有状态(选中/未选中/禁用)的边框样式,colors参数需区分容器色(containerColor)和内容色(labelColor),