סרגל החיפוש

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

יוצגו שני סרגי חיפוש. בצד ימין יש רק שדה טקסט.
  בסרגל החיפוש שמימין יש שדה טקסט והצעת חיפוש מתחתיו.
איור 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 של הפעילות המארחת לפני שהפעילות נהרסת במהלך שינוי הגדרות.
  • מקש המקש semantics קובע את סדר הסריקה של TalkBack.
    • isTraversalGroup מוגדר ל-Box כדי לקבץ את כל הרכיבים הניתנים לקישור (composables) הצאצאים שלו.
    • הערך של 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;a&#39; שמוקלדת בתוך הסרגל. מתחת לסרגל החיפוש תופיע רשימה של שש הצעות חיפוש.
איור 2. סרגל חיפוש עם הצעות מוצגות.

סרגל חיפוש עם רשימה מסוננת

בדוגמה הזו מוצג SearchBar שמסנן רשימה על סמך שאילתה החיפוש של המשתמש:

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

התוצאה

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

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