Compose笔记(三十七)--FilterChip

发布于:2025-07-21 ⋅ 阅读:(17) ⋅ 点赞:(0)

        这一节主要了解一下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),


网站公告

今日签到

点亮在社区的每一天
去签到