Панель поиска

Используйте строку поиска для реализации функциональности поиска. Строка поиска — это постоянное поле поиска, которое позволяет пользователям вводить ключевое слово или фразу для отображения релевантных результатов в вашем приложении, и рекомендуется, когда поиск является основным направлением вашего приложения.

Показаны две строки поиска. В левой строке есть только текстовое поле. В левой строке поиска есть текстовое поле и поисковая подсказка под ним.
Рисунок 1. Базовая строка поиска (1) и строка поиска с подсказкой (2).

API поверхность

Используйте SearchBar composable для реализации панелей поиска. Ключевые параметры для этого composable включают следующее:

  • inputField : Определяет поле ввода строки поиска. Обычно использует SearchBarDefaults.InputField , что позволяет настраивать:
    • query : Текст запроса, который будет отображен в поле ввода.
    • onQueryChange : лямбда-выражение для обработки изменений в строке запроса.
  • expanded : логическое значение, указывающее, расширена ли строка поиска для отображения предложений или отфильтрованных результатов.
  • onExpandedChange : лямбда-выражение для обработки изменений в развернутом состоянии раскрывающегося списка.

  • 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.0 до 1.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 вызывается всякий раз, когда пользователь вводит или удаляет текст в строке поиска.
  • SearchBarDefaults.InputField содержит leadingIcon , который добавляет значок поиска в начало поля ввода, и trailingIcon , который добавляет значок «дополнительные параметры» в конец поля ввода. Здесь вы можете предоставить пользователю параметры сортировки и фильтрации.
  • onSearch = { … } вызывает лямбда- onSearch и сворачивает строку поиска при отправке поиска.
  • LazyColumn эффективно обрабатывает потенциально большое количество результатов поиска. Он проходит по списку searchResults и отображает каждый результат как ListItem .
  • Каждый составной элемент ListItem показывает текст элемента, текст с дополнительной информацией и значок звезды в качестве leadingContent элемента. В этом примере представлена ​​возможность добавить элемент в избранное.
  • Логику фильтрации см. в разделе CustomizableSearchBarExample в полном исходном коде на GitHub .

Результат

Показана строка поиска, содержащая слова hinted text search inside. Под строкой поиска отображается список поисковых предложений со значком звездочки рядом с каждым предложением.
Рисунок 3. Панель поиска с отображаемыми соответствующими предложениями.

Дополнительные ресурсы