סרגל החיפוש

משתמשים בשורת חיפוש כדי להטמיע פונקציונליות של חיפוש. סרגל חיפוש הוא שדה חיפוש קבוע שמאפשר למשתמשים להזין מילת מפתח או ביטוי כדי להציג תוצאות רלוונטיות באפליקציה. מומלץ להשתמש בו כשהחיפוש הוא המוקד העיקרי של האפליקציה.

מוצגות שתי שורות חיפוש. בצד ימין יש רק שדה טקסט.
  שורת החיפוש שמשמאל כוללת שדה טקסט ומתחתיו הצעה לחיפוש.
איור 1. סרגל חיפוש בסיסי (1) וסרגל חיפוש עם הצעה (2).

פלטפורמת ה-API

משתמשים ברכיב ה-Composable‏ 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 של ה-Activity המארח לפני שה-Activity נהרס במהלך שינוי ההגדרות.
  • המשתנה 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 = { … } קורא ל-lambda של onSearch ומכווץ את שורת החיפוש כששולחים את החיפוש.
  • LazyColumn מטפל ביעילות במספר גדול של תוצאות חיפוש. הוא עובר על הרשימה searchResults ומציג כל תוצאה בתור ListItem.
  • כל רכיב קומפוזבילי מסוג ListItem מציג את טקסט הפריט, טקסט עם מידע נוסף וסמל כוכב בתור ה-leadingContent של הפריט. בדוגמה הזו, מוצגת אפשרות להוסיף את הפריט למועדפים.
  • לוגיקת הסינון מופיעה ב-CustomizableSearchBarExample בקוד המקור המלא ב-GitHub.

תוצאה

מוצג סרגל חיפוש עם המילים &quot;חיפוש טקסט&quot; בתוכו. מתחת לסרגל החיפוש מוצגת רשימה של הצעות לחיפוש, עם סמל כוכב לצד כל הצעה.
איור 3. שורת חיפוש עם הצעות רלוונטיות.

מקורות מידע נוספים