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

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

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

API surface

Используйте composable-функцию SearchBar, чтобы добавить строку поиска. Ключевые параметры для этой 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 размещающего объекта Activity до того, как он будет уничтожен во время изменения конфигурации.
  • Модификатор semantics управляет порядком обхода TalkBack.
    • Для Box задано значение isTraversalGroup, чтобы сгруппировать все дочерние composable-функции.
    • 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.
  • Каждая composable-функция ListItem показывает текст элемента, текст с дополнительной информацией и значок звезды в качестве leadingContent элемента. В этом примере показан вариант добавления объекта в избранное.
  • Логика фильтрации (CustomizableSearchBarExample) описана в полном исходном коде на GitHub.

Результат

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

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