Строка поиска

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

Показаны две строки поиска. В левой части экрана есть только текстовое поле.
  В строке поиска справа есть текстовое поле и подсказка.
Рисунок 1. Строка поиска без подсказок (1) и строка поиска с подсказкой (2).

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

Используйте composable-функцию SearchBar, чтобы реализовать строку поиска. Основные параметры этого компонента:

  • 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 обрабатывает ввод текста и обновляет состояние при каждом изменении текста в поле ввода.
    • Статус expanded определяет видимость списка подсказок.
  • searchResults.forEach { result -> … } перебирает searchResults список и создает для каждого результата ListItem.
    • При нажатии на ListItem обновляется textFieldState, сворачивается строка поиска и в textField добавляется выбранный результат поиска.

Результат

Показана строка поиска с буквой &quot;а&quot;. Под строкой поиска отображается список из шести подсказок.
Рисунок 2. Строка поиска с подсказками.

Строка поиска с отфильтрованным списком

В этом примере показан SearchBar, который фильтрует список на основе поискового запроса пользователя:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomizableSearchBar(
    query: String,
    onQueryChange: (String) -> Unit,
    onSearch: (String) -> Unit,
    searchResults: List<String>,
    onResultClick: (String) -> Unit,
    modifier: Modifier = Modifier,
    // 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,
) {
    // 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.

Результат

Показана строка поиска с текстом &quot;поиск по подсказкам&quot;. Под строкой поиска появится список подсказок, рядом с каждой из которых будет значок звездочки.
Рисунок 3. Строка поиска с подсказками.

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