कस्टम व्यू को इंटरैक्टिव बनाना

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

कस्टम व्यू बनाना, यूज़र इंटरफ़ेस (यूआई) बनाने का सिर्फ़ एक हिस्सा है. आपको यह भी पक्का करना होगा कि आपका व्यू, उपयोगकर्ता के इनपुट का जवाब इस तरह दे कि वह असल दुनिया में होने वाली उस कार्रवाई से मिलता-जुलता हो जिसकी नकल की जा रही है.

अपने ऐप्लिकेशन में मौजूद ऑब्जेक्ट को असल दुनिया के ऑब्जेक्ट की तरह काम करने दें. उदाहरण के लिए, अपने ऐप्लिकेशन में मौजूद इमेज को अचानक गायब न होने दें और न ही उन्हें कहीं और दिखने दें, क्योंकि असल दुनिया में ऑब्जेक्ट ऐसा नहीं करते. इसके बजाय, अपनी इमेज को एक जगह से दूसरी जगह ले जाएं.

उपयोगकर्ताओं को इंटरफ़ेस में मामूली बदलाव या अनुभव भी महसूस होते हैं. साथ ही, वे असल दुनिया की नकल करने वाले मामूली बदलावों पर सबसे अच्छा रिस्पॉन्स देते हैं. उदाहरण के लिए, जब उपयोगकर्ता यूज़र इंटरफ़ेस (यूआई) के किसी ऑब्जेक्ट को फ़्लिंग करता है, तो उसे शुरुआत में इनर्शिया का एहसास कराएं, जिससे मोशन में देरी होती है. मोशन के आखिर में, उसे मोमेंटम का एहसास कराएं, जिससे ऑब्जेक्ट फ़्लिंग से आगे बढ़ जाता है.

इस पेज पर, Android फ़्रेमवर्क की सुविधाओं का इस्तेमाल करके, अपने कस्टम व्यू में असल दुनिया के इन व्यवहारों को जोड़ने का तरीका बताया गया है.

इनपुट इवेंट की खास जानकारी और प्रॉपर्टी ऐनिमेशन की खास जानकारी में, इससे जुड़ी ज़्यादा जानकारी देखी जा सकती है.

इनपुट जेस्चर मैनेज करना

Android, कई अन्य यूज़र इंटरफ़ेस (यूआई) फ़्रेमवर्क की तरह, इनपुट इवेंट मॉडल के साथ काम करता है. उपयोगकर्ता की कार्रवाइयां, इवेंट में बदल जाती हैं. इनसे कॉलबैक ट्रिगर होते हैं. साथ ही, कॉलबैक को बदलकर, यह तय किया जा सकता है कि आपका ऐप्लिकेशन, उपयोगकर्ता को कैसे जवाब दे. Android सिस्टम में, टच सबसे आम इनपुट इवेंट है. इससे onTouchEvent(android.view.MotionEvent) ट्रिगर होता है. इवेंट को मैनेज करने के लिए, इस तरीके को इस तरह बदलें:

Kotlin

override fun onTouchEvent(event: MotionEvent): Boolean {
    return super.onTouchEvent(event)
}

Java

@Override
   public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
   }

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

क्लास का इंस्टेंस पास करके, GestureDetector बनाएं जो लागू करता है GestureDetector.OnGestureListener. अगर आपको सिर्फ़ कुछ जेस्चर प्रोसेस करने हैं, तो GestureDetector.OnGestureListener इंटरफ़ेस को लागू करने के बजाय, GestureDetector.SimpleOnGestureListener को बढ़ाया जा सकता है. उदाहरण के लिए, इस कोड से एक ऐसी क्लास बनती है जो GestureDetector.SimpleOnGestureListener को बढ़ाती है और onDown(MotionEvent) को बदलती है.

Kotlin

private val myListener =  object : GestureDetector.SimpleOnGestureListener() {
    override fun onDown(e: MotionEvent): Boolean {
        return true
    }
}

private val detector: GestureDetector = GestureDetector(context, myListener)

Java

class MyListener extends GestureDetector.SimpleOnGestureListener {
   @Override
   public boolean onDown(MotionEvent e) {
       return true;
   }
}
detector = new GestureDetector(getContext(), new MyListener());

GestureDetector.SimpleOnGestureListener का इस्तेमाल करें या न करें, हमेशा onDown() तरीका लागू करें, जो true दिखाता है. ऐसा इसलिए ज़रूरी है, क्योंकि सभी जेस्चर, onDown() मैसेज से शुरू होते हैं. अगर onDown() से false मिलता है, जैसा कि GestureDetector.SimpleOnGestureListener करता है, तो सिस्टम मान लेता है कि आपको जेस्चर के बाकी हिस्से को अनदेखा करना है. साथ ही, GestureDetector.OnGestureListener के अन्य तरीकों को कॉल नहीं किया जाता. onDown() से false सिर्फ़ तब दिखाएं, जब आपको किसी पूरे जेस्चर को अनदेखा करना हो.

GestureDetector.OnGestureListener को लागू करने और GestureDetector का इंस्टेंस बनाने के बाद, onTouchEvent() में मिलने वाले टच इवेंट को समझने के लिए, अपने GestureDetector का इस्तेमाल किया जा सकता है.

Kotlin

override fun onTouchEvent(event: MotionEvent): Boolean {
    return detector.onTouchEvent(event).let { result ->
        if (!result) {
            if (event.action == MotionEvent.ACTION_UP) {
                stopScrolling()
                true
            } else false
        } else true
    }
}

Java

@Override
public boolean onTouchEvent(MotionEvent event) {
   boolean result = detector.onTouchEvent(event);
   if (!result) {
       if (event.getAction() == MotionEvent.ACTION_UP) {
           stopScrolling();
           result = true;
       }
   }
   return result;
}

जब onTouchEvent() को ऐसा टच इवेंट पास किया जाता है जिसे वह जेस्चर के तौर पर नहीं पहचानता, तो वह false दिखाता है. इसके बाद, जेस्चर की पहचान करने के लिए, अपना कस्टम कोड चलाया जा सकता है.

असल दुनिया जैसा मोशन बनाना

जेस्चर, टचस्क्रीन वाले डिवाइसों को कंट्रोल करने का एक बेहतरीन तरीका है. हालांकि, अगर उनसे असल दुनिया जैसे नतीजे नहीं मिलते हैं, तो वे मुश्किल हो सकते हैं और उन्हें याद रखना मुश्किल हो सकता है.

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

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

फ़्लिंग शुरू करने के लिए, शुरुआती वेलोसिटी और फ़्लिंग की कम से कम और ज़्यादा से ज़्यादा x और y वैल्यू के साथ fling() को कॉल करें. वेलोसिटी वैल्यू के लिए, GestureDetector से कैलकुलेट की गई वैल्यू का इस्तेमाल किया जा सकता है.

Kotlin

fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
    scroller.fling(
            currentX,
            currentY,
            (velocityX / SCALE).toInt(),
            (velocityY / SCALE).toInt(),
            minX,
            minY,
            maxX,
            maxY
    )
    postInvalidate()
    return true
}

Java

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
   scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);
   postInvalidate();
    return true;
}

fling() को कॉल करने से, फ़्लिंग जेस्चर के लिए फ़िज़िक्स मॉडल सेट अप हो जाता है. इसके बाद, नियमित अंतराल पर Scroller.computeScrollOffset() को कॉल करके, Scroller को अपडेट करें. computeScrollOffset() , मौजूदा समय को पढ़कर और उस समय x और y पोज़िशन को कैलकुलेट करने के लिए, फ़िज़िक्स मॉडल का इस्तेमाल करके, Scroller ऑब्जेक्ट की इंटरनल स्थिति को अपडेट करता है. इन वैल्यू को पाने के लिए, getCurrX() और getCurrY() को कॉल करें.

ज़्यादातर व्यू, Scroller ऑब्जेक्ट की x और y पोज़िशन को सीधे scrollTo() पर पास करते हैं. यह उदाहरण थोड़ा अलग है: इसमें व्यू के रोटेशनल ऐंगल को सेट करने के लिए, मौजूदा स्क्रोल x पोज़िशन का इस्तेमाल किया जाता है.

Kotlin

scroller.apply {
    if (!isFinished) {
        computeScrollOffset()
        setItemRotation(currX)
    }
}

Java

if (!scroller.isFinished()) {
    scroller.computeScrollOffset();
    setItemRotation(scroller.getCurrX());
}

Scroller क्लास, आपके लिए स्क्रोल पोज़िशन कैलकुलेट करती है. हालांकि, यह उन पोज़िशन को आपके व्यू पर अपने-आप लागू नहीं करती. स्क्रोलिंग ऐनिमेशन को स्मूद दिखाने के लिए, नए कोऑर्डिनेट अक्सर लागू करें. ऐसा करने के दो तरीके हैं:

  • फिर से ड्रॉ करने के लिए फ़ोर्स करें postInvalidate() को कॉल करने के बाद fling() को कॉल करके. इस तकनीक के लिए, ज़रूरी है कि स्क्रोल ऑफ़सेट कैलकुलेट किए जाएं onDraw() और स्क्रोल ऑफ़सेट में बदलाव होने पर, हर बार postInvalidate() को कॉल किया जाए.
  • को सेट अप करें, ताकि वह फ़्लिंग की अवधि के लिए ऐनिमेशन दिखाए. साथ ही, को कॉल करके, ऐनिमेशन अपडेट प्रोसेस करने के लिए एक लिसनर जोड़ें.ValueAnimatoraddUpdateListener() इस तकनीक से, की प्रॉपर्टी में ऐनिमेशन जोड़ा जा सकता हैView.

ट्रांज़िशन को स्मूद बनाना

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

ऐनिमेशन सिस्टम का इस्तेमाल करने के लिए, जब भी कोई प्रॉपर्टी बदलती है, तो व्यू के दिखने पर असर डालने वाली प्रॉपर्टी को सीधे न बदलें. इसके बजाय, बदलाव करने के लिए ValueAnimator का इस्तेमाल करें. यहां दिए गए उदाहरण में, व्यू में चुने गए चाइल्ड कॉम्पोनेंट में बदलाव करने से, रेंडर किया गया पूरा व्यू रोटेट हो जाता है, ताकि सिलेक्शन पॉइंटर बीच में रहे. ValueAnimator , नई रोटेशन वैल्यू को तुरंत सेट करने के बजाय, कुछ सौ मिलीसेकंड की अवधि में रोटेशन को बदलता है.

Kotlin

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply {
    setIntValues(targetAngle)
    duration = AUTOCENTER_ANIM_DURATION
    start()
}

Java

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0);
autoCenterAnimator.setIntValues(targetAngle);
autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
autoCenterAnimator.start();

अगर आपको जिस वैल्यू में बदलाव करना है वह बुनियादी View प्रॉपर्टी में से एक है, तो ऐनिमेशन करना और भी आसान हो जाता है. ऐसा इसलिए, क्योंकि व्यू में एक इन-बिल्ट ViewPropertyAnimator होता है. इसे एक साथ कई प्रॉपर्टी के ऐनिमेशन के लिए ऑप्टिमाइज़ किया जाता है. जैसे, यहां दिए गए उदाहरण में:

Kotlin

animate()
    .rotation(targetAngle)
    .duration = ANIM_DURATION
    .start()

Java

animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();