תאימות קלט במסכים גדולים

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

  • בודקים את התמיכה הבסיסית במקלדת, כמו Ctrl+Z לביטול פעולה, Ctrl+C להעתקה ו-Ctrl+S לשמירה. במאמר טיפול בפעולות במקלדת מפורטת רשימה של מקשי הקיצור שמוגדרים כברירת מחדל.
  • בודקים את התמיכה המתקדמת במקלדת, למשל ניווט באמצעות המקשים Tab ו החצים, אישור הזנת טקסט באמצעות המקש Enter, והפעלה והשהיה באמצעות מקש הרווח באפליקציות מדיה.
  • בודקים אינטראקציות בסיסיות עם העכבר, כולל לחיצה ימנית לפתיחת תפריט ההקשר, שינויים בסמל כשמציבים מעליו את הסמן ואירועי גלילה באמצעות גלגל העכבר או משטח המגע ברכיבים בהתאמה אישית.
  • בודקים מכשירים לקליטת נתונים שספציפיים לאפליקציה, כמו סטיילוס, שלטי משחק ובקרי MIDI של אפליקציות מוזיקה.
  • שקלו להוסיף תמיכה בקלט מתקדם שיכולה להבליט את האפליקציה בסביבות של מחשבים, למשל: לוח מגע כ-cross-fader לאפליקציות של תקליטנים, לכידת עכבר למשחקים ומקשי קיצור למשתמשים שמעדיפים להשתמש במקלדת.

מקלדת

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

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

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

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

מידע נוסף זמין במאמר הפיכת רכיב Composable לניתן להתמקדות.

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

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

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

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

מידע נוסף זמין במאמר ניווט עקבי באמצעות קבוצות מיקוד.

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

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

הקשות

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

לדוגמה, אפליקציות צ'אט שבהן לוחצים על Enter כדי לשלוח הודעה, אפליקציות מדיה שבהן לוחצים על מקש הרווח כדי להפעיל ולהפסיק את ההפעלה, ומשחקים שבהם לוחצים על המקשים w, ‏ a, ‏ s ו-d כדי לשלוט בתנועה.

אפשר לטפל בהקשות בודדות באמצעות המשתנה onKeyEvent, שמקבל למבדה שנקראת כשהרכיב שהשתנה מקבל אירוע מפתח. המאפיין KeyEvent#type מאפשר לכם לקבוע אם האירוע הוא לחיצה על מקש (KeyDown) או שחרור של מקש (KeyUp):

Box(
    modifier = Modifier.focusable().onKeyEvent {
        if(
            it.type == KeyEventType.KeyUp &&
            it.key == Key.S
        ) {
            doSomething()
            true
        } else {
            false
        }
    }
)  {
    Text("Press S key")
}

לחלופין, אפשר לשנות את פונקציית ה-callback‏ onKeyUp() ולהוסיף את ההתנהגות הצפויה לכל קוד מקש שמתקבל:

kotlin override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_ENTER -> { sendChatMessage() true } KeyEvent.KEYCODE_SPACE -> { playOrPauseMedia() true } else -> super.onKeyUp(keyCode, event) } }

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

מידע נוסף זמין במאמר טיפול בפעולות במקלדת.

קיצורי דרך

כשמשתמשים במקלדת פיזית, אפשר להשתמש במקשי הקיצור הנפוצים שכוללים את המקשים Ctrl,‏ Alt, ‏ Shift ו-Meta. אם באפליקציה אין מקשי קיצור, חוויית המשתמש עלולה להיות מתסכלת. משתמשים מתקדמים גם מעריכים קיצורי דרך למשימות ספציפיות לאפליקציה שמשתמשים בהן לעיתים קרובות. קיצורי דרך מקלים על השימוש באפליקציה ומבדילים אותה מאפליקציות שאין בהן קיצורי דרך.

מקשי קיצור נפוצים: Ctrl+S (שמירה), Ctrl+Z (ביטול הפעולה האחרונה) ו-Ctrl+Shift+Z (ביצוע מחדש). במאמר הזה מפורטת רשימה של מקשי הקיצור שמוגדרים כברירת מחדל.

לאובייקט KeyEvent יש את המאפיינים הבאים, שמציינים אם מקשי הצירופים נלחצו:

לדוגמה:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

למידע נוסף, קראו את המאמרים הבאים:

סטיילוס

הרבה מכשירים עם מסך גדול מגיעים עם סטיילוס. אפליקציות ל-Android מתייחסות לסטיילוסים כקלט של מסך מגע. בחלק מהמכשירים יכול להיות גם לוח ציור עם חיבור USB או Bluetooth, כמו Wacom Intuos. אפליקציות ל-Android יכולות לקבל קלט מ-Bluetooth אבל לא מ-USB.

כדי לגשת לאובייקטים של סטיילוס MotionEvent, מוסיפים את המשתנה pointerInteropFilter למשטח הציור. מטמיעים מחלקה של ViewModel עם שיטה שמעבדת אירועי תנועה. מעבירים את השיטה בתור פונקציית הלמבדה של onTouchEvent של המשנה pointerInteropFilter:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

האובייקט MotionEvent מכיל מידע על האירוע:

נקודות היסטוריות

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

התעלמות מנגיעות של כף היד

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

מערכת Android מבטלת אירועי מגע של כף היד על ידי שליחת MotionEvent. אם האפליקציה מקבלת את הערך ACTION_CANCEL, צריך לבטל את התנועה. אם האפליקציה מקבלת את השגיאה ACTION_POINTER_UP, צריך לבדוק אם FLAG_CANCELED מוגדר. אם כן, בטלו את התנועה.

אל תבדקו רק את FLAG_CANCELED. ב-Android 13 (רמת API‏ 33) ומעלה, המערכת מגדירה את FLAG_CANCELED לאירועים מסוג ACTION_CANCEL, אבל המערכת לא מגדירה את הדגל בגרסאות Android ישנות יותר.

12 ‏Android

ב-Android בגרסה 12 (רמת API‏ 32) ומטה, אפשר לזהות דחיית מגע של כף היד רק באירועי מגע עם מצביע יחיד. אם נגיעה בכף היד היא הסמן היחיד, המערכת מבטלת את האירוע על ידי הגדרת ACTION_CANCEL באובייקט של אירוע התנועה. אם יש עוד נקודות מגע, המערכת מגדירה את ACTION_POINTER_UP, וזה לא מספיק כדי לזהות דחיית מגע בכף היד.

Android 13

ב-Android 13 (רמת API‏ 33) ומעלה, אם נגיעה בכף היד היא המצביע היחיד, המערכת מבטלת את האירוע על ידי הגדרת ACTION_CANCEL ו-FLAG_CANCELED באובייקט של אירוע התנועה. אם יש עוד נקודות מגע, המערכת מגדירה את ACTION_POINTER_UP ו-FLAG_CANCELED.

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

אפליקציות לכתיבת הערות

ב-ChromeOS יש Intent מיוחד שמאפשר למשתמשים לראות אפליקציות רשומות לכתיבת פתקים. כדי לרשום אפליקציה כאפליקציה לסיכום פגישות, צריך להוסיף את הקטע הבא לקובץ המניפסט של האפליקציה:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

כשאפליקציה רשומה במערכת, המשתמש יכול לבחור אותה כאפליקציית ברירת המחדל לכתיבת הערות. כשמבקשים הערה חדשה, האפליקציה צריכה ליצור הערה ריקה שמוכנה לקלט מסטיילוס. כשהמשתמש רוצה להוסיף הערות לתמונה (כמו צילום מסך או תמונה שהורדה), האפליקציה מופעלת עם ClipData שמכיל פריט אחד או יותר עם מזהי URI של content://. האפליקציה צריכה ליצור פתק שמשתמש בתמונה המצורפת הראשונה כתמונת רקע, ולהיכנס למצב שבו המשתמש יכול לצייר על המסך באמצעות סטיילוס.

בדיקת Intent של כתיבת הערות בלי סטיילוס

כדי לבדוק אם אפליקציה מגיבה בצורה נכונה ל-intent של כתיבת פתקים בלי סטיילוס פעיל, משתמשים בשיטה הבאה כדי להציג את האפשרויות לכתיבת פתקים ב-ChromeOS:

  1. עוברים למצב פיתוח ומאפשרים כתיבה במכשיר
  2. מקישים על Ctrl+Alt+F2 כדי לפתוח טרמינל.
  3. מריצים את הפקודה sudo vi /etc/chrome_dev.conf
  4. מקישים על i כדי לערוך ומוסיפים את --ash-enable-palette לשורה חדשה בסוף הקובץ.
  5. כדי לשמור, מקישים על Esc, מקלידים :, w, q ומקישים על Enter.
  6. מקישים על Ctrl+Alt+F1 כדי לחזור לממשק המשתמש הרגיל של ChromeOS.
  7. יוצאים מהחשבון ונכנסים אליו שוב

תפריט סטיילוס אמור להופיע עכשיו במדף:

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

תמיכה בעכבר ובלוח מגע

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

לחיצה ימנית

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

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

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

איך יוצרים תפריט הקשר

העברת העכבר מעל

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

שתי הדוגמאות הנפוצות ביותר לכך הן:

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

גרירה ושחרור

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

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

כדי להוסיף תמיכה בגרירה ושחרור, עיינו במאמרים גרירה ושחרור , ובפוסט בבלוג Android ב-ChromeOS – הטמעת גרירה ושחרור.

שיקולים מיוחדים לגבי ChromeOS

תמיכה מתקדמת במצביע

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

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

בודקים את האובייקט PointerEvent כדי לקבוע את הפרטים הבאים:

שלטי משחק

חלק ממכשירי Android עם מסך גדול תומכים בעד ארבעה שלטי משחק. כדי לטפל בשלטי משחק, משתמשים בממשקי ה-API הרגילים של Android לשלטי משחק (ראו תמיכה בשלטי משחק).

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

מצב תרגום קלט

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

אם באפליקציה מוגדרת התנהגות קלט מותאמת אישית, למשל פעולת צביטה מותאמת אישית בשתי אצבעות בלוח המגע, או אם תרגומי הקלט האלה לא מספקים את אירועי הקלט שהאפליקציה מצפה להם, אפשר להשבית את מצב תרגום הקלט על ידי הוספת התג הבא למניפסט של Android:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

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