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().apply {
            for (i in 1..50) {
                add(SearchResult(
                    id = "result_$i",
                    title = "搜索结果 $i",
                    subtitle = "这是关于搜索结果 $i 的详细描述"
                ))
            }
        }
    }

   
    var filteredResults by remember { mutableStateOf(emptyList()) }

    
    val recentSearches = remember {
        mutableStateListOf("Android开发", "Jetpack Compose", "Kotlin语言")
    }

    
    LaunchedEffect(query) {
        if (query.length >= 2) { 
            isLoading = true
            delay(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 = false
                    if (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 = search
                                            active = 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展示大量结果,避免性能问题

你可能感兴趣的:(Android基础,笔记,android,jetpack)