Compose笔记(三十六)--SearchBar
这一节主要了解一下Compose中的SearchBar,在Jetpack Compose中,SearchBar是Material 3组件库提供的一种搜索组件,它结合了文本输入框和下拉结果列表的功能,提供了良好的搜索体验,简单总结如下:
API
query:当前搜索查询文本
onQueryChange:查询文本变化时的回调
onSearch:用户提交搜索时的回调
active:搜索栏是否处于活动状态(展开状态)
onActiveChange:搜索栏活动状态变化时的回调
placeholder:搜索框为空时显示的占位文本
leadingIcon:搜索框前面显示的图标
trailingIcon:搜索框后面显示的图标
shape:搜索框的形状配置
colors:搜索框的颜色配置
content:搜索栏处于活动状态时显示的内容
场景:
1 内容检索,在列表、网格或复杂内容中快速查找特定项
2 动态过滤,实时根据输入内容过滤列表数据
3 搜索历史与建议,显示用户历史搜索记录或智能推荐关键词。
栗子:
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay// 搜索结果数据类
data class SearchResult(val id: String,val title: String,val subtitle: String,val imageResId: Int = android.R.drawable.ic_menu_gallery
)@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TestSearchBarExample() {val scope = rememberCoroutineScope()var query by remember { mutableStateOf("") }var active by remember { mutableStateOf(false) }var isLoading by remember { mutableStateOf(false) }val allResults = remember {mutableListOf<SearchResult>().apply {for (i in 1..50) {add(SearchResult(id = "result_$i",title = "搜索结果 $i",subtitle = "这是关于搜索结果 $i 的详细描述"))}}}var filteredResults by remember { mutableStateOf(emptyList<SearchResult>()) }val recentSearches = remember {mutableStateListOf<String>("Android开发", "Jetpack Compose", "Kotlin语言")}LaunchedEffect(query) {if (query.length >= 2) { isLoading = truedelay(500) filteredResults = allResults.filter {it.title.contains(query, ignoreCase = true) ||it.subtitle.contains(query, ignoreCase = true)}isLoading = false} else if (query.isEmpty()) {filteredResults = emptyList()}}Scaffold(topBar = {TopAppBar(title = { Text("搜索示例") },colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primaryContainer))}) { padding ->Column(modifier = Modifier.fillMaxSize().padding(padding)) {SearchBar(query = query,onQueryChange = { query = it },onSearch = {active = falseif (query.isNotEmpty() && !recentSearches.contains(query)) {recentSearches.add(0, query) if (recentSearches.size > 5) {recentSearches.removeAt(recentSearches.size - 1) }}},active = active,onActiveChange = { active = it },placeholder = { Text("搜索...") },leadingIcon = {Icon(imageVector = Icons.Default.Search,contentDescription = "搜索")},trailingIcon = {if (query.isNotEmpty()) {IconButton(onClick = { query = "" }) {Icon(imageVector = Icons.Default.Close,contentDescription = "清除")}}},modifier = Modifier.fillMaxWidth().padding(16.dp)) {if (query.isEmpty()) {if (recentSearches.isNotEmpty()) {Text(text = "最近搜索",fontWeight = FontWeight.Bold,modifier = Modifier.padding(16.dp))LazyColumn {items(recentSearches) { search ->Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().clickable {query = searchactive = false}.padding(16.dp)) {Icon(imageVector = Icons.Default.Search,contentDescription = "历史记录",modifier = Modifier.padding(end = 16.dp))Text(search)}}}}} else if (isLoading) {Box(modifier = Modifier.fillMaxWidth().padding(16.dp),contentAlignment = Alignment.Center) {CircularProgressIndicator()}} else if (filteredResults.isEmpty()) {Box(modifier = Modifier.fillMaxWidth().padding(16.dp),contentAlignment = Alignment.Center) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Icon(imageVector = Icons.Default.Search,contentDescription = "无结果",tint = Color.Gray,modifier = Modifier.size(48.dp))Spacer(modifier = Modifier.height(8.dp))Text("没有找到与 \"$query\" 相关的结果")}}} else {LazyColumn {items(filteredResults) { result ->SearchResultItem(result = result,onClick = {active = false})}}}}if (!active) {Box(modifier = Modifier.fillMaxSize().padding(16.dp),contentAlignment = Alignment.Center) {Column(horizontalAlignment = Alignment.CenterHorizontally) {Icon(imageVector = Icons.Default.Search,contentDescription = "搜索",tint = MaterialTheme.colorScheme.primary,modifier = Modifier.size(96.dp))Spacer(modifier = Modifier.height(16.dp))Text(text = "开始搜索",fontSize = 24.sp,fontWeight = FontWeight.Bold)Spacer(modifier = Modifier.height(8.dp))Text(text = "在上方搜索栏输入关键词查找内容",color = Color.Gray)}}}}}
}@Composable
fun SearchResultItem(result: SearchResult, onClick: () -> Unit) {Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().clickable(onClick = onClick).padding(16.dp)) {Icon(painter = painterResource(id = result.imageResId),contentDescription = result.title,modifier = Modifier.size(40.dp).padding(end = 16.dp))Column(modifier = Modifier.weight(1f)) {Text(text = result.title,fontWeight = FontWeight.Medium)Spacer(modifier = Modifier.height(4.dp))Text(text = result.subtitle,fontSize = 12.sp,color = Color.Gray)}}
}
注意:
1 状态管理与交互处理,SearchBar的query和active状态需要分别管理
2 搜索防抖处理,避免用户每输入一个字符就触发搜索,可以使用LaunchedEffect和delay实现防抖
3 结果列表性能优化,使用LazyColumn而非Column展示大量结果,避免性能问题