搜尋列

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

畫面上會顯示兩個搜尋列。左側的只有文字欄位。左側的搜尋列包含文字欄位,下方則有搜尋建議。
圖 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 可確保在設定變更期間,搜尋列是否展開或收合。在設定變更期間刪除活動之前,它會將記住的值寫入代管活動的 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. 顯示相關建議的搜尋列。

其他資源