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

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

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

מקלדת

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

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

באפליקציות רבות, המערכת של Android מטפלת באופן אוטומטי בניווט באמצעות מקשי החצים ומקש Tab. לדוגמה, חלק מהרכיבים הניתנים לשילוב ניתנים להתמקד כברירת מחדל, כמו 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")
}

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

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

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

כדי לשלוט בהתנהגות המיקוד, מוסיפים את המשתנה 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, שמקבל פונקציית lambda שמופיעה כשהרכיב המותאם מקבל אירוע מקש. בעזרת המאפיין 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() ולהוסיף את ההתנהגות הצפויה לכל קוד מפתח שהתקבל:

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 מתרחש כשמפעילים מקש ומפסיקים ללחוץ עליו. השימוש בקריאה החוזרת מונעת מהאפליקציות צורך לעבד מספר אירועים של onKeyDown אם מקישים על מקש או משחררים אותו לאט. משחקים ואפליקציות שצריכים לזהות את הרגע שבו מקישים על מקש או אם המשתמש לוחץ על מקש ומוחזק אותו יכולים להאזין לאירוע onKeyDown ולטפל באירועי onKeyDown חוזרים בעצמם.

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

קיצורי דרך

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

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

לאובייקט KeyEvent יש את המאפיינים הבאים, שמציינים אם מקש שינוי הלחץ (modifier) לוחץ:

לדוגמה:

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

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

סטיילוס

הרבה מכשירים עם מסך גדול מגיעים עם עט stylus. אפליקציות ל-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:

דחייה של Palm

כשמשתמשים מציירים, כותבים או מבצעים פעולות באפליקציה באמצעות סטיילוס, לפעמים הם נוגעים במסך בכף היד. אפשר לדווח לאפליקציה על אירוע המגע (מוגדרת ל-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-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

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

בדיקת כוונת כתיבת הערות ללא סטיילוס

[TBD remove section.]

כדי לבדוק אם אפליקציה מגיבה בצורה נכונה לכוונות ליצירת הערות בלי עט פעיל, משתמשים בשיטה הבאה כדי להציג את אפשרויות הכתיבה ב-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 on ChromeOS — Implementing Drag & Drop.

שיקולים מיוחדים ל-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" />

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