Pasek wyszukiwania

Aby zaimplementować funkcję wyszukiwania, użyj paska wyszukiwania. Pasek wyszukiwania to trwałe pole wyszukiwania, w którym użytkownicy mogą wpisać słowo kluczowe lub frazę, aby wyświetlić odpowiednie wyniki w aplikacji. Jest on zalecany, gdy wyszukiwanie jest głównym elementem aplikacji.

Wyświetlają się 2 paski wyszukiwania. Po lewej stronie znajduje się tylko pole tekstowe.
  Pasek wyszukiwania po lewej stronie zawiera pole tekstowe i sugestię wyszukiwania poniżej.
Rysunek 1. Podstawowy pasek wyszukiwania (1) i pasek wyszukiwania z sugestią (2).

Powierzchnia interfejsu API

Do implementowania pasków wyszukiwania używaj elementu SearchBar. Kluczowe parametry tego elementu obejmują:

  • inputField: Definiuje pole do wprowadzania danych na pasku wyszukiwania. Zwykle używa SearchBarDefaults.InputField, który umożliwia dostosowanie:
    • query: tekst zapytania, który ma się wyświetlać w polu do wprowadzania danych.
    • onQueryChange: lambda do obsługi zmian w ciągu zapytania.
  • expanded: wartość logiczna wskazująca, czy pasek wyszukiwania jest rozwinięty, aby wyświetlać sugestie lub przefiltrowane wyniki.
  • onExpandedChange: lambda do obsługi zmian stanu rozwinięcia listy.

  • content: treść tego paska wyszukiwania, która ma wyświetlać wyniki wyszukiwania poniżej inputField.

Ten fragment kodu pokazuje podstawową implementację SearchBar z sugestiami:

@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()
                    )
                }
            }
        }
    }
}

Najważniejsze informacje o kodzie

  • rememberSaveable zapewnia, że stan rozwinięcia lub zwinięcia paska wyszukiwania zostanie zachowany po zmianach konfiguracji. Zapisuje zapamiętaną wartość w pakiecie savedInstanceState aktywności hosta, zanim aktywność zostanie zniszczona podczas zmiany konfiguracji.
  • Modyfikator semantics określa kolejność przechodzenia TalkBack.
    • isTraversalGroup jest ustawiony dla Box, aby grupować wszystkie podrzędne elementy.
    • traversalIndex jest ustawiony tak, aby określać kolejność, w jakiej TalkBack odczytuje informacje o ułatwieniach dostępu z każdego elementu w grupie. TalkBack odczytuje informacje o ułatwieniach dostępu w przypadku elementu z wartością ujemną, np. -1, przed elementem z wartością dodatnią, np. 1. Ponieważ wartość jest liczbą zmiennoprzecinkową, możesz określić niestandardową kolejność wielu elementów, ustawiając wartości między -1.0 a 1.0 w każdym elemencie.
  • SearchBar zawiera inputField do wprowadzania danych wejściowych użytkownika oraz Column do wyświetlania sugestii wyszukiwania.
    • SearchBarDefaults.InputField tworzy pole do wprowadzania danych i obsługuje zmiany w zapytaniu użytkownika.
    • onQueryChange obsługuje wprowadzanie tekstu i aktualizuje stan za każdym razem, gdy tekst w polu do wprowadzania danych się zmienia.
    • The expanded stan określa widoczność listy sugestii.
  • searchResults.forEach { result -> … } iteruje po searchResults liście i tworzy ListItem dla każdego wyniku.
    • Gdy klikniesz ListItem, zaktualizuje się textFieldState, pasek wyszukiwania zostanie zwinięty, a textField zostanie wypełniony wybranym wynikiem wyszukiwania.

Wynik

Wyświetla się pasek wyszukiwania z wpisaną w nim literą „a”. Pod paskiem wyszukiwania wyświetla się lista z 6 sugestiami wyszukiwania.
Rysunek 2. Pasek wyszukiwania z wyświetlonymi sugestiami.

Pasek wyszukiwania z przefiltrowaną listą

Ten przykład pokazuje SearchBar, który filtruje listę na podstawie zapytania użytkownika:

@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)
                    )
                }
            }
        }
    }
}

Najważniejsze informacje o kodzie

  • Funkcja lambda onQueryChange jest wywoływana za każdym razem, gdy użytkownik wpisuje lub usuwa tekst na pasku wyszukiwania.
  • SearchBarDefaults.InputField zawiera leadingIcon, który dodaje ikonę wyszukiwania na początku pola do wprowadzania danych, oraz trailingIcon, który dodaje ikonę „więcej opcji” na końcu pola do wprowadzania danych. W tym miejscu możesz udostępnić użytkownikowi opcje sortowania i filtrowania.
  • onSearch = { … } wywołuje lambdę onSearch i zwija pasek wyszukiwania po przesłaniu wyszukiwania.
  • LazyColumn wydajnie obsługuje potencjalnie dużą liczbę wyników wyszukiwania. Iteruje po liście searchResults i wyświetla każdy wynik jako ListItem.
  • Każdy element ListItem wyświetla tekst elementu, tekst z dodatkowymi informacjami oraz ikonę gwiazdki jako leadingContent elementu. W tym przykładzie jest dostępna opcja dodania elementu do ulubionych.
  • Logikę filtrowania znajdziesz w CustomizableSearchBarExample w pełnym kodzie źródłowym na GitHubie.

Wynik

Wyświetla się pasek wyszukiwania zawierający słowa „wyszukiwanie tekstu z podpowiedziami”. Pod paskiem wyszukiwania wyświetla się lista sugestii wyszukiwania, a obok każdej z nich znajduje się ikona gwiazdki.
Rysunek 3. Pasek wyszukiwania z wyświetlonymi odpowiednimi sugestiami.

Dodatkowe materiały