סיבוב קלט באמצעות 'כתיבה'

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

ברוב השעונים, שלושת המקורות העיקריים לקלט החוגה כוללים את הצד המסתובב (RSB), ומסגרת פיזית או מסגרת מגע, שהיא מעגלית את אזור המגע מסביב למסך. למרות שההתנהגות הצפויה עשויה להשתנות בהתאם סוג הקלט, הקפידו לתמוך בקלט חוגה לכל האינטראקציות החיוניות.

גלילה

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

מטמיעים גלילה בחוגה באמצעות 'כתיבה' ל-Wear OS. הדוגמה הזו מתארת אפליקציה עם פיגוע וScalingLazyColumn, שגוללת אנכית. פיגוע מספק את מבנה הפריסה הבסיסי לאפליקציות ל-Wear OS, וכבר יש בו חריץ לאינדיקטור גלילה. שפת תרגום להציג את התקדמות הגלילה, ליצור מחוון מיקום המבוסס על אובייקט של מצב list. תצוגות שניתן לגלול, כולל ScalingLazyColumn, כבר יש מצב שניתן לגלול כדי להוסיף חוגה לקלט הנתונים. כדי לקבל אירועי גלילה סיבובית, מבצעים את הפעולות הבאות:

  1. בקשת מיקוד מפורשת באמצעות FocusRequester. כדאי להשתמש HierarchicalFocusCoordinator במקרים מורכבים יותר, כמו מספר ScalingLazyColumns אובייקטים ב-HorizontalPager.

  2. מוסיפים את הצירוף onRotaryScrollEvent כדי ליירט אירועים שהמערכת נוצר כשמשתמש מסובב את הכתר או מסובב את המסגרת. כל אירוע סיבובי יש לו ערך פיקסל מוגדר והוא גולל אנכית או אופקית. גם המאפיין כולל קריאה חוזרת (callback) כדי לציין אם האירוע נצפה, ומפסיק את הפצת האירוע להורים שלו כשהם צורכים אותו.

val listState = rememberScalingLazyListState()
Scaffold(
    positionIndicator = {
        PositionIndicator(scalingLazyListState = listState)
    }
) {

    val focusRequester = rememberActiveFocusRequester()
    val coroutineScope = rememberCoroutineScope()

    ScalingLazyColumn(
        modifier = Modifier
            .onRotaryScrollEvent {
                coroutineScope.launch {
                    listState.scrollBy(it.verticalScrollPixels)
                    listState.animateScrollBy(0f)
                }
                true
            }
            .focusRequester(focusRequester)
            .focusable()
            .fillMaxSize(),
        state = listState
    ) {
        // Content goes here
        // ...
    }
}

ערכים נפרדים

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

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

var selectedColumn by remember { mutableIntStateOf(0) }

val hoursFocusRequester = remember { FocusRequester() }
val minutesRequester = remember { FocusRequester() }
// ...
Scaffold(modifier = Modifier.fillMaxSize()) {
    Row(
        // ...
        // ...
    ) {
        // ...
        Picker(
            readOnly = selectedColumn != 0,
            modifier = Modifier.size(64.dp, 100.dp)
                .onRotaryScrollEvent {
                    coroutineScope.launch {
                        hourState.scrollBy(it.verticalScrollPixels)
                    }
                    true
                }
                .focusRequester(hoursFocusRequester)
                .focusable(),
            onSelected = { selectedColumn = 0 },
            // ...
            // ...
        )
        // ...
        Picker(
            readOnly = selectedColumn != 1,
            modifier = Modifier.size(64.dp, 100.dp)
                .onRotaryScrollEvent {
                    coroutineScope.launch {
                        minuteState.scrollBy(it.verticalScrollPixels)
                    }
                    true
                }
                .focusRequester(minutesRequester)
                .focusable(),
            onSelected = { selectedColumn = 1 },
            // ...
            // ...
        )
        LaunchedEffect(selectedColumn) {
            listOf(
                hoursFocusRequester,
                minutesRequester
            )[selectedColumn]
                .requestFocus()
        }
    }
}

פעולות בהתאמה אישית

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

אם הרכיב לא תומך במקור באירועי גלילה כמו עוצמת קול שליטה, תוכלו לטפל באירועי גלילה בעצמכם.

// VolumeScreen.kt

val focusRequester: FocusRequester = remember { FocusRequester() }

Column(
    modifier = Modifier
        .fillMaxSize()
        .onRotaryScrollEvent {
            // handle rotary scroll events
            true
        }
        .focusRequester(focusRequester)
        .focusable(),
) { ... }

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

// VolumeViewModel.kt

object VolumeRange(
    public val max: Int = 10
    public val min: Int = 0
)

val volumeState: MutableStateFlow<Int> = ...

fun onVolumeChangeByScroll(pixels: Float) {
    volumeState.value = when {
        pixels > 0 -> min (volumeState.value + 1, VolumeRange.max)
        pixels < 0 -> max (volumeState.value - 1, VolumeRange.min)
    }
}

כדי לשמור על הפשטות, הדוגמה שלמעלה משתמשת בערכי פיקסלים, עשויות להיות רגישות מדי.

צריך להשתמש בקריאה חוזרת (callback) אחרי שמקבלים את האירועים, כפי שמוצג בקטע הקוד הבא.

val focusRequester: FocusRequester = remember { FocusRequester() }
val volumeState by volumeViewModel.volumeState.collectAsState()

Column(
    modifier = Modifier
        .fillMaxSize()
        .onRotaryScrollEvent {
            volumeViewModel
                .onVolumeChangeByScroll(it.verticalScrollPixels)
            true
        }
        .focusRequester(focusRequester)
        .focusable(),
) { ... }

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

כדאי להשתמש ב-Horology, פרויקט קוד פתוח של Google שמספק ספריות של Wear שמשלימות לפונקציונליות של 'כתיבה מהירה' ל-Wear OS וממשקי API אחרים של Wear OS. הורולוגים מספקים הטמעה לשימוש מתקדם והרבה פרטים ספציפיים למכשירים.

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

אפשר לקרוא מידע נוסף במאמר הורולוג ב-GitHub.