בשיעור הזה נסביר איך לעקוב אחרי תנועה באירועי מגע.
חדש
onTouchEvent()
מופעלת באמצעות
ACTION_MOVE
אירוע
בכל פעם שמיקום המגע, הלחץ או הגודל הנוכחיים של המגע משתנים. בתור
שמתוארים במאמר זיהוי תנועות נפוצות,
האירועים האלה מתועדים
MotionEvent
פרמטר של
onTouchEvent()
.
מכיוון שנגיעה מבוססת-אצבע היא לא תמיד צורת האינטראקציה המדויקת ביותר, זיהוי אירועי מגע לרוב מבוסס על תנועה יותר מאשר על מגע פשוט. כדי לעזור לאפליקציות להבחין בין תנועות מבוססות תנועה (כמו החלקה) וגם תנועות שאינן תנועות (כגון הקשה יחידה), Android כולל את Touch slop. מרווח מגע מתייחס למרחק בפיקסלים שיכול להיות למגע של המשתמש לנוע לפני שהתנועה תתפרש כתנועה המבוססת על תנועה. לקבלת מידע נוסף מידע על נושא זה, ראה ניהול אירועי מגע ViewGroup.
יש כמה דרכים לעקוב אחר תנועה בתנועה, בהתאם ל- לצורכי האפליקציה שלכם. הנה כמה דוגמאות:
- מיקום ההתחלה והסיום של מצביע, למשל העברה של מיקום במסך. מנקודה א' לנקודה ב'.
- כיוון התנועה של המצביע, כפי שנקבע על ידי ה-X ו-Y של הקואורדינטות.
- היסטוריה. כדי לראות את גודל ההיסטוריה של התנועה, צריך להפעיל את הפונקציה
אמצעי תשלום אחד (
MotionEvent
)getHistorySize()
. לאחר מכן אפשר לקבל את המיקומים, הגדלים, הזמן והלחצים של כל אחד של אירועים היסטוריים באמצעותgetHistorical<Value>
שיטות. 'היסטוריה' שימושית בעת רינדור עקבות של אצבע המשתמש, למשל או לשרטוט במגע. פרטים נוספים זמינים בחומר העזר בנושאMotionEvent
. - מהירות הסמן בזמן שהוא זז במסך המגע.
כדאי לעיין במקורות המידע שקשורים לנושא:
מהירות המעקב
אפשר לבצע תנועה שמבוססת על תנועה שמבוססת על המרחק או הכיוון
שהסמן עובר. אבל בדרך כלל מהירות היא גורם מכריע במעקב
המאפיינים של תנועה מסוימת או כדי לקבוע אם התנועה התרחשה. כדי ליצור
חישוב המהירות בקלות רבה יותר, Android מספק
VelocityTracker
.
בעזרת VelocityTracker
אפשר לעקוב אחר המהירות של אירועי מגע. זה מועיל
עבור תנועות שבהן המהירות היא חלק מהקריטריונים לתנועה, כמו
הצמדה.
כאן יש דוגמה שממחישה את המטרה של השיטות
VelocityTracker
API:
Kotlin
private const val DEBUG_TAG = "Velocity" class MainActivity : Activity() { private var mVelocityTracker: VelocityTracker? = null override fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // Reset the velocity tracker back to its initial state. mVelocityTracker?.clear() // If necessary, retrieve a new VelocityTracker object to watch // the velocity of a motion. mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain() // Add a user's movement to the tracker. mVelocityTracker?.addMovement(event) } MotionEvent.ACTION_MOVE -> { mVelocityTracker?.apply { val pointerId: Int = event.getPointerId(event.actionIndex) addMovement(event) // When you want to determine the velocity, call // computeCurrentVelocity(). Then, call getXVelocity() and // getYVelocity() to retrieve the velocity for each pointer // ID. computeCurrentVelocity(1000) // Log velocity of pixels per second. It's best practice to // use VelocityTrackerCompat where possible. Log.d("", "X velocity: ${getXVelocity(pointerId)}") Log.d("", "Y velocity: ${getYVelocity(pointerId)}") } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // Return a VelocityTracker object back to be re-used by others. mVelocityTracker?.recycle() mVelocityTracker = null } } return true } }
Java
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() and // getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second. It's best practice to use // VelocityTrackerCompat where possible. Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId)); Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
שימוש בצילום של מצביע העכבר
אפליקציות מסוימות, כמו משחקים, מחשבים מרחוק ולקוחות וירטואליזציה, נהנים מהיתרונות לקבל שליטה על סמן העכבר. צילום המצביע הוא תכונה זמינה ב-Android 8.0 (רמת API 26) ואילך, שמספקת את אמצעי הבקרה הזה הצגת כל אירועי העכבר לתצוגה ממוקדת באפליקציה.
שליחת בקשה לתיעוד של הסמן
תצוגה מפורטת באפליקציה שלך יכולה לבקש תיעוד של מצביע העכבר רק כאשר היררכיית התצוגות של
מכיל התמקדות. לכן, אפשר לבקש לתעד את הסמן כאשר יש
פעולה ספציפית של המשתמש בתצוגה, כמו
onClick()
או ב
onWindowFocusChanged()
המטפל באירועים של הפעילות שלך.
כדי לבקש תיעוד של הסמן, צריך להפעיל את
requestPointerCapture()
ה-method בתצוגה המפורטת. הקוד שבדוגמה הבאה מראה איך לבקש מצביע
לתעד כאשר משתמש לוחץ על צפייה:
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
Java
@Override public void onClick(View view) { view.requestPointerCapture(); }
לאחר שהבקשה ללכידת הסמן בוצעה בהצלחה, תתבצע שיחת Android
onPointerCaptureChange(true)
המערכת מספקת את אירועי העכבר לתצוגה המיקוד באפליקציה כל עוד
היא באותה היררכיית תצוגות מפורטות כמו התצוגה המפורטת שביקשה את הצילום. המלצות אחרות
אפליקציות יפסיקו לקבל אירועים בעכבר עד לשחרור הצילום, כולל
ACTION_OUTSIDE
אירועים. מערכת Android מספקת אירועים של זיהוי מיקום הסמן של משתמשים אחרים ממקורות אחרים מלבד העכבר
רגיל, אבל מצביע העכבר אינו גלוי יותר.
טיפול באירועי הצבעה שתועדו
לאחר שצפייה משיגה בהצלחה את לכידת הסמן, מערכת Android מספקת את אירועי עכבר. התצוגה הממוקדת יכולה לטפל באירועים על ידי ביצוע אחת את המשימות הבאות:
- אם משתמשים בתצוגה מותאמת אישית, אין לשנות
onCapturedPointerEvent(MotionEvent)
- אחרת, עליך לרשום
OnCapturedPointerListener
הקוד שבדוגמה הבאה מראה איך מטמיעים
onCapturedPointerEvent(MotionEvent)
:
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean { // Get the coordinates required by your app. val verticalOffset: Float = motionEvent.y // Use the coordinates to update your view and return true if the event is // successfully processed. return true }
Java
@Override public boolean onCapturedPointerEvent(MotionEvent motionEvent) { // Get the coordinates required by your app. float verticalOffset = motionEvent.getY(); // Use the coordinates to update your view and return true if the event is // successfully processed. return true; }
הקוד שבדוגמה הבאה מראה איך לרשום
OnCapturedPointerListener
:
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent -> // Get the coordinates required by your app. val horizontalOffset: Float = motionEvent.x // Use the coordinates to update your view and return true if the event is // successfully processed. true }
Java
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { @Override public boolean onCapturedPointer (View view, MotionEvent motionEvent) { // Get the coordinates required by your app. float horizontalOffset = motionEvent.getX(); // Use the coordinates to update your view and return true if the event is // successfully processed. return true; } });
בין אם אתם משתמשים בתצוגה מותאמת אישית או רושמים מאזינים, התצוגה שלכם מקבלת
MotionEvent
עם קואורדינטות של מצביע שמציינות תנועות יחסיות כמו X
או דלתא של Y, בדומה לקואורדינטות שמסופקות על ידי התקן כדור עקיבה. אפשר
לאחזר את הקואורדינטות באמצעות
getX()
ו-
getY()
שחרור הסמן
בתצוגה באפליקציה שלך אפשר לשחרר את תיעוד הסמן על ידי התקשרות
releasePointerCapture()
כפי שאפשר לראות בדוגמת הקוד הבאה:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
המערכת יכולה להרחיק את הצילום מהתצוגה ללא רשותך באופן מפורש
קוראים לפונקציה releasePointerCapture()
, בדרך כלל בגלל היררכיית התצוגות
שכולל את התצוגה שבקשות ללכידת מאבדות את המיקוד.