מושגים מרכזיים

כדאי לנסות את התכונה 'כתיבה מהירה'
Jetpack Compose היא ערכת הכלים המומלצת לבניית ממשק משתמש ב-Android. איך משתמשים ב'גרירה ושחרור' ב-Compose

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

תהליך הגרירה והשחרור

יש ארבעה שלבים או מצבים בתהליך הגרירה והשחרור: 'התחלה', 'המשך', 'שחרור' ו'סיום'.

ספרים שהתחלת

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

  • הנתונים שרוצים לגרור.
  • קריאה חוזרת (callback) לציור הצללית של הגרירה
  • מטא-נתונים שמתארים את הנתונים שנגררו
  • המערכת מגיבה על ידי קריאה לאפליקציה כדי לקבל צללית של גרירה. לאחר מכן, המערכת מציגה את צל ההזזה במכשיר.
  • לאחר מכן, המערכת שולחת אירוע גרירה עם סוג הפעולה ACTION_DRAG_STARTED למאזין של אירוע הגרירה של כל אובייקטי ה-View בפריסה הנוכחית. כדי להמשיך לקבל אירועי גרירה – כולל אירוע השמטה אפשרי – ה-drag event listener צריך להחזיר את הערך true. הפעולה הזו רושמת את המאזין במערכת. רק מאזינים רשומים ימשיכו לקבל אירועי גרירה. בשלב הזה, המאזינים יכולים גם לשנות את המראה של אובייקט היעד של ההעברה View כדי לציין שהתצוגה יכולה לקבל אירוע העברה.
  • אם מאזין האירועים של גרירה מחזיר את הערך false, הוא לא יקבל אירועי גרירה של הפעולה הנוכחית עד שהמערכת תשלח אירוע גרירה עם סוג הפעולה ACTION_DRAG_ENDED. כשהמאזין מחזיר את הערך false, הוא מעדכן את המערכת שהוא לא מעוניין בפעולה של גרירה ושחרור ולא רוצה לקבל את הנתונים שנגררו.
התהליך ממשיך
המשתמש ממשיך לגרור. כשצל הגרירה חוצה את תיבת הגבול של יעד ההעברה, המערכת שולחת אירוע גרירה אחד או יותר למאזין לאירועי גרירה של היעד. יכול להיות שהמאזין ישנה את המראה של יעד ההטמעה View בתגובה לאירוע. לדוגמה, אם האירוע מציין שהצל של הגרירה נכנס לתיבת המגבלות של יעד ההשלכה – סוג הפעולה ACTION_DRAG_ENTERED – המאזין יכול להגיב על כך על ידי הדגשת ה-View.
בוטל
המשתמש משחרר את צללית ההזזה בתוך תיבת הגבול של יעד ההעברה. המערכת שולחת למאזין של יעד ההשלכה אירוע גרירה עם סוג פעולה ACTION_DROP. אובייקט אירוע הגרירה מכיל את הנתונים שמועברים למערכת בקריאה ל-startDragAndDrop() שמפעילה את הפעולה. אם המאזין מעבד בהצלחה את הנתונים שהוחמצו, הוא אמור להחזיר למערכת את הערך הבוליאני true. : השלב הזה מתרחש רק אם המשתמש מוריד את צללית ההזזה בתוך תיבת המכסה של View שהמאזין שלו רשום לקבלת אירועי גרירה (יעד השמטה). אם המשתמש משחרר את צללית ההזזה בכל מצב אחר, לא נשלח אירוע ACTION_DROP של גרירה.
הסתיים

אחרי שהמשתמש משחרר את צללית ההזזה, ואחרי שהמערכת שולחת

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

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

אירועי גרירה

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

פונקציות event listener של אירועי גרירה מקבלות את האובייקט DragEvent. כדי לקבל את סוג הפעולה, המאזינים צריכים לבצע קריאה ל-DragEvent.getAction(). יש שישה ערכים אפשריים שמוגדרים על ידי קבועים בכיתה DragEvent, שמפורטים בטבלה 1:

טבלה 1. סוגי הפעולות של DragEvent

סוג הפעולה משמעות
ACTION_DRAG_STARTED האפליקציה קוראת ל-startDragAndDrop() ומקבלת צללית גרירה. אם המאזין רוצה להמשיך לקבל אירועי גרירה של הפעולה הזו, הוא צריך להחזיר למערכת את הערך הבוליאני true.
ACTION_DRAG_ENTERED צללית ההזזה נכנסת לתיבת הגבול של View של ה-drag event listener. זהו סוג הפעולה הראשונה של האירוע שהמאזין מקבל כשצללית ההזזה נכנסת לתיבת הגבול.
ACTION_DRAG_LOCATION אחרי אירוע ACTION_DRAG_ENTERED, צללית ההזזה עדיין נמצאת בתוך תיבת הגבול של View של רכיב ההקשבה לאירועי גרירה.
ACTION_DRAG_EXITED אחרי אירוע ACTION_DRAG_ENTERED ולפחות אירוע אחד מסוג ACTION_DRAG_LOCATION, צל ההזזה ינוע מחוץ לתיבת הגבול של View של רכיב ההקשבה לאירועי גרירה.
ACTION_DROP צללית ההזזה משתחררת מעל View של ה-event listener של ההזזה. סוג הפעולה הזה נשלח למאזין של אובייקט View רק אם המאזין מחזיר את הערך הבווליאני true בתגובה לאירוע הגרירה ACTION_DRAG_STARTED. סוג הפעולה הזה לא נשלח אם המשתמש משחרר את צללית ההזזה מעל View שה-listener שלו לא רשום, או אם המשתמש משחרר את צללית ההזזה מעל רכיב שאינו חלק מהפריסה הנוכחית.

אם המאזין מעבד את ההעברה בהצלחה, הוא מחזיר את הערך הבווליאני true. אחרת, צריך להחזיר את הערך false.

ACTION_DRAG_ENDED המערכת מסיימת את פעולת הגרירה והשליפה. סוג הפעולה הזה לא חייב להיות מלווה באירוע ACTION_DROP. אם המערכת שולחת ACTION_DROP, קבלת סוג הפעולה ACTION_DRAG_ENDED לא מעידה על כך שההשמטה הצליחה. כדי לקבל את הערך שמוחזר בתגובה ל-ACTION_DROP, המאזין צריך להפעיל את getResult(), כפי שמתואר בטבלה 2. אם לא נשלח אירוע ACTION_DROP, הפונקציה getResult() מחזירה את הערך false.

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

טבלה 2. נתוני DragEvent תקינים לפי סוג הפעולה

getAction()
value
getClipDescription()
value
getLocalState()
value
getX()
value
getY()
value
getClipData()
value
getResult()
value
ACTION_DRAG_STARTED ✓ ✓        
ACTION_DRAG_ENTERED ✓ ✓        
ACTION_DRAG_LOCATION ✓ ✓ ✓ ✓    
ACTION_DRAG_EXITED ✓ ✓        
ACTION_DROP ✓ ✓ ✓ ✓ ✓  
ACTION_DRAG_ENDED   ✓       ✓

השיטות DragEventgetAction(),‏ describeContents(),‏ writeToParcel() ו-toString() תמיד מחזירות נתונים תקינים.

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

צללית גרירה

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

התמונה נקראת צללית גרירה. יוצרים אותו באמצעות שיטות שמצהירים על אובייקט View.DragShadowBuilder. מעבירים את ה-builder למערכת כשמתחילים פעולת גרירה ושחרור באמצעות startDragAndDrop(). כחלק מהתגובה שלה להודעה startDragAndDrop(), המערכת מפעילה את שיטות ה-callback שהגדרתם ב-View.DragShadowBuilder כדי לקבל צללית של גרירה.

לכיתה View.DragShadowBuilder יש שני קונסטרוקטורים:

View.DragShadowBuilder(View)

ה-constructor הזה מקבל כל אחד מהאובייקטים של View באפליקציה. ה-constructor שומר את האובייקט View באובייקט View.DragShadowBuilder, כדי שהפונקציות החוזרות (callbacks) יוכלו לגשת אליו כדי ליצור את צל הגרירה. התצוגה לא חייבת להיות View שהמשתמש בוחר כדי להתחיל את פעולת הגרירה.

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

View.DragShadowBuilder()

אם משתמשים ב-constructor הזה, לא יהיה אובייקט View באובייקט View.DragShadowBuilder. השדה מוגדר ל-null. צריך להרחיב את View.DragShadowBuilder ולשנות את השיטות שלו, אחרת תקבלו צללית גרירה בלתי נראית. המערכת לא תיצור שגיאה.

לכיתה View.DragShadowBuilder יש שתי שיטות שיוצרות יחד את צללית ההזזה:

onProvideShadowMetrics()

המערכת קוראת ל-method הזה מיד אחרי שמפעילים את startDragAndDrop(). משתמשים בשיטה הזו כדי לשלוח למערכת את המימדים ואת נקודת המגע של צללית הגרירה. ל-method יש שני פרמטרים:

outShadowSize: אובייקט Point. רוחב הצללית של משיכה מוזן בשדה x, והגובה שלה מוזן בשדה y.

outShadowTouchPoint: אובייקט Point. נקודת המגע היא המיקום בתוך צללית ההזזה שצריך להיות מתחת לאצבע של המשתמש במהלך ההזזה. המיקום ב-X מופיע ב-x והמיקום ב-Y מופיע ב-y.

onDrawShadow()

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

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

גרירה של פונקציות event listener ושל קריאות חוזרות (callbacks)

View מקבל אירועי גרירה באמצעות מאזין לאירועי גרירה שמטמיע את View.OnDragListener או באמצעות שיטת ה-callback‏ onDragEvent() של התצוגה. כשהמערכת קוראת ל-method או ל-listener, היא מספקת ארגומנט DragEvent.

ברוב המקרים, עדיף להשתמש בבורר במקום בשיטת ה-callback. בדרך כלל, כשמתכננים ממשקי משתמש לא יוצרים מחלקות משנה של View, אבל השימוש ב-method‏ callback מאלץ אתכם ליצור מחלקות משנה כדי לשנות את ברירת המחדל של ה-method. לעומת זאת, אפשר להטמיע סוג אחד של מאזין ולהשתמש בו עם כמה אובייקטים שונים של View. אפשר גם להטמיע אותו כמחלקה אנונימית בקוד או כביטוי lambda. כדי להגדיר את המאזין לאובייקט View, צריך להפעיל את השיטה setOnDragListener().

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

  • הפונקציה מחזירה את הערך true בתגובה לקריאה ל-startDragAndDrop().
  • performReceiveContent() אם מושכים את הנתונים ומשחררים אותם בתצוגה. הנתונים מועברים לשיטה כאובייקט ContentInfo. השיטה מפעילה את OnReceiveContentListener.

  • הפונקציה מחזירה את הערך true אם הנתונים שגוררים ומשחררים מושלכים לתצוגה ו-OnReceiveContentListener צורך חלק מהתוכן.

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

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

השילוב של השיטה onDragEvent() ו-View.OnDragListener דומה לשילוב של onTouchEvent() ו-View.OnTouchListener שנעשה בו שימוש באירועי מגע.