במכשירים עם מסכים גדולים, המשתמשים לרוב יוצרים אינטראקציה עם האפליקציות באמצעות מקלדת, עכבר, משטח מגע, סטיילוס או גיימפאד. כדי לאפשר לאפליקציה לקבל קלט ממכשירים חיצוניים:
- בודקים את התמיכה הבסיסית במקלדת, כמו 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
מכיל מידע על האירוע:
- הפונקציה
MotionEvent#getToolType()
מחזירה את הערכיםTOOL_TYPE_FINGER
,TOOL_TYPE_STYLUS
אוTOOL_TYPE_ERASER
, בהתאם לכלי שנגע במסך MotionEvent#getPressure()
מדווח על הלחץ הפיזי שמופעל על עט הסtylus (אם יש תמיכה)MotionEvent#getAxisValue()
עםMotionEvent.AXIS_TILT
ו-MotionEvent.AXIS_ORIENTATION
מספקים את הטיה הפיזית והכיוון של עט הסtylus (אם יש תמיכה)
נקודות היסטוריות
מערכת Android אוספת אירועי קלט ומעבירה אותם פעם אחת לכל מסגרת. עט יכול לדווח על אירועים בתדירויות גבוהות בהרבה מאשר המסך. כשאתם יוצרים אפליקציות ציור, אתם יכולים לבדוק אם קרו אירועים בעבר הקרוב באמצעות ממשקי ה-API של getHistorical
:
MotionEvent#getHistoricalX()
MotionEvent#getHistoricalY()
MotionEvent#getHistoricalPressure()
MotionEvent#getHistoricalAxisValue()
דחייה של 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:
- מעבר למצב פיתוח והפעלת הרשאת כתיבה במכשיר
- מקישים על Ctrl+Alt+F2 כדי לפתוח מסוף.
- מריצים את הפקודה
sudo vi /etc/chrome_dev.conf
- לוחצים על
i
כדי לערוך ולהוסיף את--ash-enable-palette
לשורה חדשה בסוף הקובץ - כדי לשמור, מקישים על Esc ואז מקלידים :, w, q ומקישים על Enter.
- לוחצים על Ctrl+Alt+F1 כדי לחזור לממשק המשתמש הרגיל של ChromeOS.
- יוצאים מהחשבון ונכנסים אליו שוב.
עכשיו אמור להופיע תפריט של עט במדף:
- מקישים על לחצן העט על המדף ובוחרים באפשרות הערה חדשה. אמורה להיפתח הערה ריקה לציור.
- מצלמים את המסך. במדף, בוחרים באפשרות לחצן הסטיילוס > צילום מסך או מורידים תמונה. בהתראה אמורה להופיע האפשרות הוספת הערות לתמונה. הפעולה הזו אמורה להפעיל את האפליקציה עם התמונה מוכנה להוספת הערות.
תמיכה בעכבר ובלוח מגע
בדרך כלל, רוב האפליקציות צריכות לטפל רק בשלושה אירועים שמתמקדים במסך גדול: לחיצה ימנית, החזקה וגרירה ושחרור.
לחיצה ימנית
כל פעולה שגורמת לאפליקציה להציג תפריט הקשר, כמו לחיצה ארוכה על פריט ברשימה, אמורה להגיב גם לאירועי לחיצה ימנית.
כדי לטפל באירועים של לחיצה ימנית, האפליקציות צריכות לרשום אירוע 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
- חשוב לזכור לבקש הרשאה באמצעות
requestDragAndDropPermissions()
כדי לגשת לפריטים שגוררים מחוץ לאפליקציה כדי שאפשר יהיה לגרור פריט לאפליקציות אחרות, הוא צריך לכלול את הדגל
View.DRAG_FLAG_GLOBAL
.
תמיכה מתקדמת במצביע
אפליקציות שמטפלות באופן מתקדם בקלט של עכבר ולוח מגע צריכות להטמיע את המשתנה המשנה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
כדי לקבוע את הפרטים הבאים:
PointerType
: עכבר, עט, מגע וכו' מ-PointerEvent#changes
PointerEventType
: פעולות של סמן, כמו לחיצה, תנועה, גלילה ושחרור
שלטים למשחקים
חלק ממכשירי Android עם מסך גדול תומכים בעד ארבעה בקרי משחקים. משתמשים בממשקי ה-API הרגילים של בקרי המשחקים ל-Android כדי לטפל בבקרי משחקים (מידע נוסף זמין במאמר תמיכה בבקרי משחקים).
לחצני בקר המשחקים ממופים לערכים נפוצים לפי מיפוי נפוץ. עם זאת, לא כל יצרני בקרי המשחקים פועלים לפי אותן מוסכמות מיפוי. כדי לספק חוויית שימוש טובה יותר, כדאי לאפשר למשתמשים לבחור מיפויים שונים של בקרי משחק פופולריים. מידע נוסף זמין במאמר עיבוד לחיצות על לחצני בקר המשחקים.
מצב תרגום הקלט
כברירת מחדל, מערכת ChromeOS מפעילה מצב תרגום קלט. במצב הזה, רוב האפליקציות ל-Android פועלות כצפוי בסביבת מחשב. דוגמאות לכך הן הפעלה אוטומטית של גלילה בשני אצבעות בלוח המגע, גלילה באמצעות גלגל העכבר ומיפוי של קואורדינטות תצוגה גולמיות לקווי אורך ורוחב של חלון. בדרך כלל, מפתחי האפליקציות לא צריכים להטמיע אף אחד מההתנהגויות האלה בעצמם.
אם אפליקציה מטמיעה התנהגות קלט מותאמת אישית, למשל הגדרת פעולה מותאמת אישית של צביטה בשתי אצבעות במשטח מגע, או אם תרגומי הקלט האלה לא מספקים את אירועי הקלט שהאפליקציה מצפה להם, אפשר להשבית את מצב תרגום הקלט על ידי הוספת התג הבא למניפסט של Android:
<uses-feature
android:name="android.hardware.type.pc"
android:required="false" />