टच और पॉइंटर मूवमेंट को ट्रैक करें

Compose को आज़माएं
Jetpack Compose, Android के लिए यूज़र इंटरफ़ेस (यूआई) का सुझाया गया टूलकिट है. Compose में टच और इनपुट का इस्तेमाल करने का तरीका जानें.

इस सबक में, टच इवेंट में मूवमेंट को ट्रैक करने का तरीका बताया गया है.

एक नया onTouchEvent() ट्रिगर होता है, जब मौजूदा टच कॉन्टैक्ट की पोज़िशन, प्रेशर या साइज़ बदलता है.ACTION_MOVE सामान्य जेस्चर का पता लगाना लेख में बताए गए तरीके के मुताबिक, इन सभी इवेंट को onTouchEvent() के MotionEvent पैरामीटर में रिकॉर्ड किया जाता है.

उंगलियों से टच करना, इंटरैक्शन का सबसे सटीक तरीका नहीं होता. इसलिए, टच इवेंट का पता लगाने के लिए, अक्सर सिर्फ़ टच के बजाय मूवमेंट को ज़्यादा अहमियत दी जाती है. ऐप्लिकेशन को मूवमेंट पर आधारित जेस्चर (जैसे, स्वाइप) और बिना मूवमेंट वाले जेस्चर (जैसे, एक बार टैप करना) के बीच अंतर करने में मदद करने के लिए, Android में टच स्लोप की सुविधा शामिल है. टच स्लोप, पिक्सल में वह दूरी होती है जो उपयोगकर्ता का टच, मूवमेंट पर आधारित जेस्चर के तौर पर इंटरप्रेट होने से पहले तय कर सकता है. इस विषय के बारे में ज़्यादा जानने के लिए, ViewGroup में टच इवेंट मैनेज करना लेख पढ़ें.

आपके ऐप्लिकेशन की ज़रूरतों के हिसाब से, किसी जेस्चर में मूवमेंट को ट्रैक करने के कई तरीके हैं. ये उदाहरण हैं:

  • पॉइंटर की शुरुआती और आखिरी पोज़िशन. जैसे, स्क्रीन पर दिखने वाले किसी ऑब्जेक्ट को पॉइंट A से पॉइंट B पर ले जाना.
  • पॉइंटर की दिशा, जिसे X और Y कोऑर्डिनेट से तय किया जाता है.
  • इतिहास पर टैप करें. किसी जेस्चर के इतिहास का साइज़ पता करने के लिए, MotionEvent तरीके को कॉल किया जा सकता है getHistorySize(). इसके बाद, मोशन इवेंट के तरीकों का इस्तेमाल करके, हर पुराने इवेंट की पोज़िशन, साइज़, समय, और प्रेशर की जानकारी हासिल की जा सकती है.getHistorical<Value> इतिहास की मदद से, उपयोगकर्ता की उंगली के निशान को रेंडर किया जा सकता है. जैसे, टच से ड्रॉइंग करने के लिए. ज़्यादा जानकारी के लिए, MotionEvent का रेफ़रंस देखें.
  • टचस्क्रीन पर मूव करते समय, पॉइंटर की वेलोसिटी.

इससे जुड़े ये संसाधन देखें:

वेलोसिटी ट्रैक करना

आपके पास मूवमेंट पर आधारित ऐसा जेस्चर हो सकता है जो पॉइंटर की तय की गई दूरी या दिशा पर आधारित हो. हालांकि, किसी जेस्चर की विशेषताओं को ट्रैक करने या यह तय करने में कि जेस्चर हुआ है या नहीं, वेलोसिटी अक्सर एक अहम फ़ैक्टर होती है. वेलोसिटी की कैलकुलेशन को आसान बनाने के लिए, Android में VelocityTracker क्लास उपलब्ध है. VelocityTracker की मदद से, टच इवेंट की वेलोसिटी को ट्रैक किया जा सकता है. यह उन जेस्चर के लिए काम का है जिनमें वेलोसिटी, जेस्चर के लिए ज़रूरी शर्तों का हिस्सा होती है. जैसे, फ़्लिंग.

यहां एक उदाहरण दिया गया है, जिसमें VelocityTracker एपीआई में मौजूद तरीकों के मकसद के बारे में बताया गया है:

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 (एपीआई लेवल 26) और उसके बाद के वर्शन में उपलब्ध एक सुविधा है. यह सुविधा, आपके ऐप्लिकेशन में फ़ोकस किए गए व्यू को सभी माउस इवेंट डिलीवर करके, कंट्रोल करने की सुविधा देती है.

पॉइंटर कैप्चर का अनुरोध करना

आपके ऐप्लिकेशन में मौजूद कोई व्यू, पॉइंटर कैप्चर का अनुरोध सिर्फ़ तब कर सकता है, जब उसे शामिल करने वाले व्यू हाइरार्की पर फ़ोकस किया गया हो. इसलिए, व्यू पर किसी खास उपयोगकर्ता की कार्रवाई के दौरान, पॉइंटर कैप्चर का अनुरोध करें. जैसे, किसी onClick() इवेंट के दौरान या आपकी गतिविधि के onWindowFocusChanged() इवेंट हैंडलर में.

पॉइंटर कैप्चर का अनुरोध करने के लिए, व्यू पर requestPointerCapture() तरीके को कॉल करें. यहां दिए गए कोड के उदाहरण में, यह दिखाया गया है कि उपयोगकर्ता के किसी व्यू पर क्लिक करने पर, पॉइंटर कैप्चर का अनुरोध कैसे किया जाता है:

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() को साफ़ तौर पर कॉल किए बिना भी, व्यू से कैप्चर ले सकता है. ऐसा आम तौर पर तब होता है, जब कैप्चर का अनुरोध करने वाले व्यू को शामिल करने वाली व्यू हाइरार्की का फ़ोकस हट जाता है.