מעקב אחרי תנועות מגע וסמן

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

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

חדש 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):

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(), בדרך כלל בגלל היררכיית התצוגות שכולל את התצוגה שבקשות ללכידת מאבדות את המיקוד.