ב-Android, יש יותר מדרך אחת ליירט את האירועים מאינטראקציה של משתמש עם האפליקציה שלכם. כשמדובר באירועים בממשק המשתמש, הגישה היא לתעד את האירועים מאובייקט התצוגה הספציפי שהמשתמש מקיים איתו אינטראקציה. הכיתה View מספקת את האמצעים לעשות זאת.
בתוך מחלקות התצוגה השונות שבהן תשתמשו כדי ליצור את הפריסה, יכול להיות שתבחינו בכמה שיטות ציבוריות של קריאה חוזרת (callback) שנראות שימושיות לאירועים בממשק המשתמש. השיטות האלה מופעלות על ידי מסגרת Android כשמתרחשת הפעולה הרלוונטית באובייקט הזה. לדוגמה, כשנוגעים בתצוגה (כמו כפתור), מתבצעת קריאה ל-method onTouchEvent() באובייקט הזה. עם זאת, כדי ליירט את הפעולה הזו, צריך להרחיב את המחלקה ולשנות את השיטה. עם זאת, לא מעשי להרחיב כל אובייקט View כדי לטפל באירוע כזה. לכן, המחלקה View מכילה גם אוסף של ממשקי משנה עם קריאות חוזרות (callbacks) שאפשר להגדיר בקלות רבה יותר. הממשקים האלה, שנקראים event listeners, מאפשרים לכם לתעד את האינטראקציה של המשתמש עם ממשק המשתמש.
בדרך כלל משתמשים במאזיני האירועים כדי להאזין לאינטראקציות של המשתמשים, אבל יכול להיות שיהיה צורך להרחיב מחלקה של View כדי ליצור רכיב בהתאמה אישית.
יכול להיות שתרצו להרחיב את המחלקה Button
כדי ליצור משהו יותר מיוחד. במקרה כזה, תוכלו להגדיר את התנהגויות ברירת המחדל של האירועים עבור המחלקה באמצעות רכיבי ה-handler של האירועים במחלקה.
פונקציות מסוג Event Listener
רכיב event listener הוא ממשק במחלקה View שמכיל שיטת קריאה חוזרת אחת. השיטות האלה יופעלו על ידי מסגרת Android כשהתצוגה שהמאזין נרשם אליה תופעל על ידי אינטראקציה של המשתמש עם הפריט בממשק המשתמש.
ממשקי רכיב event listener כוללים את שיטות הקריאה החוזרת (callback) הבאות:
onClick()- החל מ-
View.OnClickListener. השיטה הזו מופעלת כשהמשתמש נוגע בפריט (במצב מגע), או מתמקד בפריט באמצעות מקשי הניווט או כדור העקיבה ולוחץ על מקש Enter המתאים או לוחץ על כדור העקיבה. onLongClick()- החל מ-
View.OnLongClickListener. הפונקציה הזו מופעלת כשהמשתמש נוגע בפריט ולוחץ עליו לחיצה ארוכה (במצב מגע), או כשהוא מתמקד בפריט באמצעות מקשי הניווט או כדור העקיבה ולוחץ לחיצה ארוכה על מקש Enter המתאים או לוחץ לחיצה ארוכה על כדור העקיבה (למשך שנייה אחת). onFocusChange()- החל מ-
View.OnFocusChangeListener. השיטה הזו מופעלת כשהמשתמש עובר אל הפריט או ממנו, באמצעות מקשי הניווט או כדור העקיבה. onKey()- החל מ-
View.OnKeyListener. השיטה הזו מופעלת כשהמשתמש מתמקד בפריט ולוחץ על מקש חומרה במכשיר או משחרר אותו. onTouch()- מ-
View.OnTouchListener. הפונקציה הזו מופעלת כשהמשתמש מבצע פעולה שמסווגת כאירוע מגע, כולל לחיצה, שחרור או תנועת מגע כלשהי במסך (בגבולות הפריט). onCreateContextMenu()- החל מ-
View.OnCreateContextMenuListener. הפונקציה הזו מופעלת כשיוצרים תפריט הקשר (כתוצאה של לחיצה ארוכה ממושכת). אפשר לעיין בדיון על תפריטי הקשר במדריך למפתחים בנושא תפריטים.
השיטות האלה הן הרכיבים היחידים בממשק המתאים. כדי להגדיר אחת מהשיטות האלה ולטפל באירועים, צריך להטמיע את הממשק המקונן בפעילות או להגדיר אותו כמחלקה אנונימית.
לאחר מכן, מעבירים מופע של ההטמעה לשיטה המתאימה View.set...Listener(). (לדוגמה, call
ולהעביר אותו להטמעה של setOnClickListener()OnClickListener.)
בדוגמה הבאה אפשר לראות איך רושמים מאזין ללחיצה על לחצן.
Kotlin
protected void onCreate(savedValues: Bundle) { ... val button: Button = findViewById(R.id.corky) // Register the onClick listener with the implementation above button.setOnClickListener { view -> // do something when the button is clicked } ... }
Java
// Create an anonymous implementation of OnClickListener private OnClickListener corkyListener = new OnClickListener() { public void onClick(View v) { // do something when the button is clicked } }; protected void onCreate(Bundle savedValues) { ... // Capture our button from layout Button button = (Button)findViewById(R.id.corky); // Register the onClick listener with the implementation above button.setOnClickListener(corkyListener); ... }
יכול להיות שיהיה לכם נוח יותר להטמיע את OnClickListener כחלק מהפעילות. כך תימנעו מטעינת מחלקה נוספת והקצאת אובייקט. לדוגמה:
Kotlin
class ExampleActivity : Activity(), OnClickListener { protected fun onCreate(savedValues: Bundle) { val button: Button = findViewById(R.id.corky) button.setOnClickListener(this) } // Implement the OnClickListener callback fun onClick(v: View) { // do something when the button is clicked } }
Java
public class ExampleActivity extends Activity implements OnClickListener { protected void onCreate(Bundle savedValues) { ... Button button = (Button)findViewById(R.id.corky); button.setOnClickListener(this); } // Implement the OnClickListener callback public void onClick(View v) { // do something when the button is clicked } ... }
שימו לב שלפונקציית ההתקשרות חזרה onClick() בדוגמה שלמעלה אין ערך מוחזר, אבל חלק משיטות ה-רכיב event listener האחרות חייבות להחזיר ערך בוליאני. הסיבה תלויה באירוע. אלה הסיבות לכך:
-
– הפונקציה מחזירה ערך בוליאני שמציין אם האירוע נצרך ולא צריך להעביר אותו הלאה. כלומר, מחזירים true כדי לציין שהאירוע טופל וצריך לעצור כאן; מחזירים false אם האירוע לא טופל או אם צריך להמשיך את האירוע לכל מאזין אחר ללחיצות.onLongClick() -
– הפונקציה מחזירה ערך בוליאני שמציין אם האירוע נצרך ולא צריך להעביר אותו הלאה. כלומר, מחזירים true כדי לציין שהאירוע טופל וצריך לעצור כאן; מחזירים false אם האירוע לא טופל או אם צריך להמשיך את האירוע לכל מאזין אחר on-key.onKey() - הפונקציה מחזירה ערך בוליאני שמציין אם ה-listener צורך את האירוע הזה. הדבר החשוב הוא שלאירוע הזה יכולות להיות כמה פעולות שמתבצעות אחת אחרי השנייה. לכן, אם מחזירים false כשמתקבל אירוע של פעולת הורדה, מציינים שלא נעשה שימוש באירוע וגם שאין עניין בפעולות הבאות מהאירוע הזה. לכן, לא תופעלנה פעולות אחרות באירוע, כמו תנועת אצבע או אירוע הפעולה הסופי של ההרמה.onTouch()
חשוב לזכור שאירועים של מקשי חומרה תמיד מועברים לתצוגה שמוצגת כרגע. הם נשלחים מלמעלה בהיררכיית התצוגה, ואז למטה, עד שהם מגיעים ליעד המתאים. אם התצוגה (או צאצא של התצוגה) נמצאת כרגע במוקד, אפשר לראות את האירוע עובר דרך השיטה . במקום לתעד אירועים מרכזיים דרך התצוגה המפורטת, אפשר גם לקבל את כל האירועים בפעילות באמצעות dispatchKeyEvent()
ו-onKeyDown().onKeyUp()
בנוסף, כשחושבים על הזנת טקסט באפליקציה, חשוב לזכור שבמכשירים רבים יש רק שיטות להזנת תוכנה. לא חייבים להשתמש בשיטות כאלה שמבוססות על מקשים. חלק מהשיטות עשויות להשתמש בקלט קולי, בכתב יד וכו'. גם אם שיטת קלט מציגה ממשק שדומה למקלדת, היא בדרך כלל לא תפעיל את סדרת האירועים . אסור ליצור ממשק משתמש שדורש לחיצות ספציפיות על מקשים כדי לשלוט בו, אלא אם רוצים להגביל את האפליקציה למכשירים עם מקלדת פיזית. בפרט, אל תסתמכו על השיטות האלה כדי לאמת קלט כשהמשתמש לוחץ על מקש Enter. במקום זאת, השתמשו בפעולות כמו onKeyDown()IME_ACTION_DONE כדי לציין לשיטת הקלט איך האפליקציה שלכם אמורה להגיב, כדי שהממשק ישתנה בצורה משמעותית. אל תניחו הנחות לגבי אופן הפעולה של שיטת קלט תוכנה, פשוט סמכו עליה שתספק לאפליקציה טקסט שכבר עבר עיצוב.
הערה: מערכת Android תקרא קודם ל-event handlers ואז ל-default handlers המתאימים מהגדרת המחלקה. לכן, אם מעבדי האירועים האלה יחזירו את הערך true, הם יפסיקו את ההפצה של האירוע למעבדי אירועים אחרים ויחסמו את הקריאה החוזרת למטפל באירועים שמוגדר כברירת מחדל בתצוגה. לכן, חשוב לוודא שרוצים לסיים את האירוע כשמחזירים את הערך true.
גורמים מטפלים באירועים
אם אתם יוצרים רכיב מותאם אישית מ-View, תוכלו להגדיר כמה שיטות של קריאה חוזרת (callback) שמשמשות כמטפלי אירועים שמוגדרים כברירת מחדל. במסמך בנושא רכיבי תצוגה בהתאמה אישית מוסבר על חלק מהקריאות החוזרות הנפוצות שמשמשות לטיפול באירועים, כולל:
-
– מופעל כשמתרחש אירוע מרכזי חדש.onKeyDown(int, KeyEvent) -
– מופעל כשמתרחש אירוע של שחרור מקש.onKeyUp(int, KeyEvent) -
– מופעל כשמתרחש אירוע תנועה של כדור עקיבה.onTrackballEvent(MotionEvent) -
– מופעל כשמתרחש אירוע תנועה במסך מגע.onTouchEvent(MotionEvent) -
– מופעל כשמצב הפוקוס של התצוגה משתנה.onFocusChanged(boolean, int, Rect)
יש עוד כמה שיטות שכדאי להכיר, שלא נכללות במחלקה View, אבל יכולות להשפיע ישירות על האופן שבו אתם מטפלים באירועים. לכן, כשמנהלים אירועים מורכבים יותר בתוך פריסה, כדאי להשתמש בשיטות האחרות האלה:
-
– מאפשר ל-Activity.dispatchTouchEvent(MotionEvent)Activityליירט את כל אירועי המגע לפני שהם נשלחים לחלון. -
– מאפשר ל-ViewGroup.onInterceptTouchEvent(MotionEvent)ViewGroupלצפות באירועים בזמן שהם נשלחים לתצוגות צאצא. -
- Call this upon a parent View to indicate that it should not intercept touch events withViewParent.requestDisallowInterceptTouchEvent(boolean).onInterceptTouchEvent(MotionEvent)
מצב מסך מגע
כשמשתמש מנווט בממשק משתמש באמצעות מקשי החצים או כדור עקיבה, צריך להעביר את המיקוד לפריטים שאפשר לבצע בהם פעולה (כמו לחצנים) כדי שהמשתמש יוכל לראות מה יקבל קלט. עם זאת, אם למכשיר יש יכולות מגע והמשתמש מתחיל ליצור אינטראקציה עם הממשק באמצעות מגע, כבר לא צריך להדגיש פריטים או להתמקד בתצוגה מסוימת. לכן, יש מצב לאינטראקציה שנקרא 'מצב מגע'.
במכשיר עם מסך מגע, ברגע שהמשתמש נוגע במסך, המכשיר עובר למצב מגע. מעכשיו ואילך, אפשר יהיה להתמקד רק בתצוגות שבהן התנאי isFocusableInTouchMode() מתקיים, כמו ווידג'טים לעריכת טקסט.
תצוגות אחרות שאפשר לגעת בהן, כמו לחצנים, לא יקבלו את המיקוד כשנוגעים בהן. הן פשוט יפעילו את מאזיני הלחיצה שלהן כשלוחצים עליהן.
בכל פעם שמשתמש לוחץ על מקש חץ או גולל באמצעות כדור עקיבה, המכשיר יוצא ממצב מגע ומחפש תצוגה כדי להעביר אליה את המיקוד. עכשיו המשתמש יכול לחזור לאינטראקציה עם ממשק המשתמש בלי לגעת במסך.
מצב המגע נשמר בכל המערכת (כל החלונות והפעילויות).
כדי לשאול על המצב הנוכחי, אפשר להתקשר אל
isInTouchMode() כדי לראות אם המכשיר נמצא כרגע במצב מגע.
העברת המיקוד
המסגרת תטפל בהעברת המיקוד השגרתית בתגובה לקלט של המשתמש.
זה כולל שינוי של המיקוד כשמסתירים או מסירים תצוגות, או כשמוסיפים תצוגות חדשות. המדד 'צפיות' מצביע על הנכונות שלהם להתמקד בתוכן באמצעות השיטה . כדי לשנות את האפשרות להעביר את המיקוד לתצוגה, קוראים ל-isFocusable(). במצב מגע, אפשר לשאול אם אפשר להתמקד בתצוגה באמצעות setFocusable().
אפשר לשנות את זה באמצעות isFocusableInTouchMode().
setFocusableInTouchMode()
במכשירים שמותקנת בהם גרסת Android 9 (רמת API 28) ומעלה, לא מוקצה מיקוד ראשוני לפעילויות. במקום זאת, אם רוצים, צריך לבקש באופן מפורש את המיקוד הראשוני.
המיקוד מועבר על סמך אלגוריתם שמאתר את השכן הקרוב ביותר בכיוון נתון. במקרים נדירים, יכול להיות שהאלגוריתם שמוגדר כברירת מחדל לא יתאים להתנהגות שהמפתח התכוון להשיג. במקרים כאלה, אפשר לספק ביטולים מפורשים באמצעות מאפייני ה-XML הבאים בקובץ הפריסה: nextFocusDown, nextFocusLeft, nextFocusRight ו-nextFocusUp. מוסיפים אחד מהמאפיינים האלה לתצוגה מממנה הפוקוס יוצא. מגדירים את ערך המאפיין כמזהה של התצוגה to שאליה צריך להעביר את המיקוד. לדוגמה:
<LinearLayout android:orientation="vertical" ... > <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... /> </LinearLayout>
בדרך כלל, בפריסה האנכית הזו, ניווט למעלה מהלחצן הראשון לא יוביל לשום מקום, וגם ניווט למטה מהלחצן השני לא יוביל לשום מקום. עכשיו, אחרי שהגדרנו את הלחצן העליון כ-nextFocusUp (ולהפך), התמקדות ברכיב ממשק יעבור מלמעלה למטה ומלמטה למעלה.
אם רוצים להגדיר תצוגה ככזו שאפשר להתמקד בה בממשק המשתמש (כשהיא לא כזו בדרך כלל), צריך להוסיף את מאפיין ה-XML android:focusable לתצוגה בהצהרת הפריסה.
מגדירים את הערך true. אפשר גם להגדיר View כאלמנט שאפשר להתמקד בו במצב מגע באמצעות android:focusableInTouchMode.
כדי לבקש להתמקד בתצוגה מסוימת, מפעילים את .requestFocus()
כדי להאזין לאירועי מיקוד (לקבל הודעה כש-View מקבל או מאבד מיקוד), משתמשים ב-, כמו שמוסבר בקטע Event listeners.onFocusChange()