搜尋列

使用搜尋列實作搜尋功能。搜尋列是持續顯示的搜尋欄位,可讓使用者輸入關鍵字或詞組,在應用程式中顯示相關結果。如果搜尋是應用程式的主要功能,建議使用搜尋列。

畫面會顯示兩個搜尋列。左側的畫面只有文字欄位。
  左側的搜尋列包含文字欄位,下方則有搜尋建議。
圖 1. 基本搜尋列 (1) 和附有建議的搜尋列 (2)。

API 介面

使用 SearchBar 可組合函式實作搜尋列。這個可組合函式的重要參數包括:

  • inputField:定義搜尋列的輸入欄位。這通常會使用 SearchBarDefaults.InputField,可自訂以下項目:
    • query:要在輸入欄位中顯示的查詢文字。
    • onQueryChange:Lambda,用於處理查詢字串的變更。
  • expanded:布林值,指出搜尋列是否已展開,可顯示建議或篩選結果。
  • onExpandedChange:Lambda,用於處理下拉式選單展開狀態的變更。

  • content:這個搜尋列的內容,用於在 inputField 下方顯示搜尋結果。

下列程式碼片段顯示 SearchBar 的基本實作方式,並提供建議:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleSearchBar(
    textFieldState: TextFieldState,
    onSearch: (String) -> Unit,
    searchResults: List<String>,
    modifier: Modifier = Modifier
) {
    // Controls expansion state of the search bar
    var expanded by rememberSaveable { mutableStateOf(false) }

    Box(
        modifier
            .fillMaxSize()
            .semantics { isTraversalGroup = true }
    ) {
        SearchBar(
            modifier = Modifier
                .align(Alignment.TopCenter)
                .semantics { traversalIndex = 0f },
            inputField = {
                SearchBarDefaults.InputField(
                    query = textFieldState.text.toString(),
                    onQueryChange = { textFieldState.edit { replace(0, length, it) } },
                    onSearch = {
                        onSearch(textFieldState.text.toString())
                        expanded = false
                    },
                    expanded = expanded,
                    onExpandedChange = { expanded = it },
                    placeholder = { Text("Search") }
                )
            },
            expanded = expanded,
            onExpandedChange = { expanded = it },
        ) {
            // Display search results in a scrollable column
            Column(Modifier.verticalScroll(rememberScrollState())) {
                searchResults.forEach { result ->
                    ListItem(
                        headlineContent = { Text(result) },
                        modifier = Modifier
                            .clickable {
                                textFieldState.edit { replace(0, length, result) }
                                expanded = false
                            }
                            .fillMaxWidth()
                    )
                }
            }
        }
    }
}

程式碼重點

  • rememberSaveable 可確保搜尋列的展開或收合狀態在設定變更後仍會保留。在設定變更期間刪除 Activity 之前,它會將記憶的值寫入代管 Activity 的 savedInstanceState 組合。
  • semantics 修飾符可控制 TalkBack 遍歷順序。
    • isTraversalGroup 會針對 Box 進行設定,將所有子項可組合函式分組。
    • traversalIndex,指定 TalkBack 讀取每個群組對等互連裝置無障礙資訊的順序。TalkBack 會先讀取值為負數的同層級無障礙資訊 (例如 -1),再讀取值為正數的同層級無障礙資訊 (例如 1)。由於值是浮點數,因此您可以在每個同層級項目上設定 -1.01.0 之間的值,指定許多同層級項目的自訂順序。
  • SearchBar 包含使用者輸入內容的 inputField,以及顯示搜尋建議的 Column
    • SearchBarDefaults.InputField 會建立輸入欄位,並處理使用者查詢的變更。
    • onQueryChange 會處理文字輸入內容,並在輸入欄位中的文字變更時更新狀態。
    • The expanded 狀態會控管建議清單的顯示狀態。
  • searchResults.forEach { result -> … } 會疊代 searchResults 清單,並為每個結果建立 ListItem
    • 點選 ListItem 時,系統會更新 textFieldState、收合搜尋列,並在 textField 中填入所選搜尋結果。

結果

畫面顯示搜尋列,列中輸入了字母「a」。搜尋列下方會顯示六項搜尋建議。
圖 2. 顯示建議的搜尋列。

搜尋列和經過篩選的清單

這個範例顯示 SearchBar,可根據使用者的搜尋查詢篩選清單:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomizableSearchBar(
    query: String,
    onQueryChange: (String) -> Unit,
    onSearch: (String) -> Unit,
    searchResults: List<String>,
    onResultClick: (String) -> Unit,
    // Customization options
    placeholder: @Composable () -> Unit = { Text("Search") },
    leadingIcon: @Composable (() -> Unit)? = { Icon(Icons.Default.Search, contentDescription = "Search") },
    trailingIcon: @Composable (() -> Unit)? = null,
    supportingContent: (@Composable (String) -> Unit)? = null,
    leadingContent: (@Composable () -> Unit)? = null,
    modifier: Modifier = Modifier
) {
    // Track expanded state of search bar
    var expanded by rememberSaveable { mutableStateOf(false) }

    Box(
        modifier
            .fillMaxSize()
            .semantics { isTraversalGroup = true }
    ) {
        SearchBar(
            modifier = Modifier
                .align(Alignment.TopCenter)
                .semantics { traversalIndex = 0f },
            inputField = {
                // Customizable input field implementation
                SearchBarDefaults.InputField(
                    query = query,
                    onQueryChange = onQueryChange,
                    onSearch = {
                        onSearch(query)
                        expanded = false
                    },
                    expanded = expanded,
                    onExpandedChange = { expanded = it },
                    placeholder = placeholder,
                    leadingIcon = leadingIcon,
                    trailingIcon = trailingIcon
                )
            },
            expanded = expanded,
            onExpandedChange = { expanded = it },
        ) {
            // Show search results in a lazy column for better performance
            LazyColumn {
                items(count = searchResults.size) { index ->
                    val resultText = searchResults[index]
                    ListItem(
                        headlineContent = { Text(resultText) },
                        supportingContent = supportingContent?.let { { it(resultText) } },
                        leadingContent = leadingContent,
                        colors = ListItemDefaults.colors(containerColor = Color.Transparent),
                        modifier = Modifier
                            .clickable {
                                onResultClick(resultText)
                                expanded = false
                            }
                            .fillMaxWidth()
                            .padding(horizontal = 16.dp, vertical = 4.dp)
                    )
                }
            }
        }
    }
}

程式碼重點

  • 每當使用者在搜尋列中輸入或刪除文字時,系統就會呼叫 onQueryChange lambda 函式。
  • SearchBarDefaults.InputField 包含 leadingIcon,可在輸入欄位開頭新增搜尋圖示,以及 trailingIcon,可在輸入欄位結尾新增「更多選項」圖示。您可以在這裡為使用者提供排序和篩選選項。
  • onSearch = { … } 會呼叫 onSearch lambda,並在提交搜尋內容時收合搜尋列。
  • LazyColumn 可有效處理大量搜尋結果。這個函式會疊代 searchResults 清單,並將每個結果顯示為 ListItem
  • 每個 ListItem 可組合函式都會顯示項目文字、顯示額外資訊的文字,以及做為項目 leadingContent 的星號圖示。在這個範例中,系統會顯示將項目加入收藏的選項。
  • 如需篩選邏輯,請參閱 GitHub 上的完整原始碼中的 CustomizableSearchBarExample

結果

畫面會顯示搜尋列,其中包含「在裡面輸入文字搜尋」的提示文字。搜尋列下方會顯示搜尋建議清單,每項建議旁邊都有星號圖示。
圖 3. 顯示相關建議的搜尋列。

其他資源