סרגל החיפוש

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

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

פלטפורמת ה-API

כדי להטמיע סרגלי חיפוש, משתמשים ב-SearchBar composable. הפרמטרים העיקריים של רכיב ה-Composable הזה כוללים את הפרמטרים הבאים:

  • 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 של הפעילות המארחת לפני שהפעילות נהרסת במהלך שינוי בהגדרות.
  • מקש הצירוף semantics שולט בסדר המעבר בין הרכיבים ב-TalkBack.
    • ההגדרה של isTraversalGroup היא Box כדי לקבץ את כל רכיבי ה-Composable ברמת הצאצא.
    • המאפיין 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 בתוצאת החיפוש שנבחרה.

תוצאה

סרגל חיפוש עם האות &#39;א&#39; שמוקלדת בתוכו. רשימה עם שש הצעות לחיפוש מוצגת מתחת לסרגל החיפוש.
איור 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 lambda מופעלת בכל פעם שהמשתמש מקליד או מוחק טקסט בסרגל החיפוש.
  • SearchBarDefaults.InputField מכיל את התג leadingIcon, שמוסיף סמל חיפוש לתחילת שדה להזנת קלט, ואת התג trailingIcon, שמוסיף סמל של 'אפשרויות נוספות' לסוף שדה להזנת קלט. כאן אפשר לספק למשתמש אפשרויות מיון וסינון.
  • הפונקציה onSearch = { … } קוראת לפונקציית ה-lambda‏ onSearch ומכווצת את סרגל החיפוש כששולחים את החיפוש.
  • LazyColumn מטפל ביעילות במספר גדול של תוצאות חיפוש. היא מבצעת איטרציה ברשימה searchResults ומציגה כל תוצאה כ-ListItem.
  • כל קומפוזבילי ListItem מציג את הטקסט של הפריט, טקסט שמציג מידע נוסף וסמל כוכב כ-leadingContent של הפריט. בדוגמה הזו, מוצגת אפשרות להוסיף את הפריט למועדפים.
  • לוגיקת הסינון מפורטת ב-CustomizableSearchBarExample בקוד המקור המלא ב-GitHub.

תוצאה

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

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