কাস্টম ভিউ ইন্টার‍্যাক্টিভ করুন

Compose-এর মাধ্যমে চেষ্টা করুন
Jetpack Compose হল Android-এর জন্য সাজেস্ট করা UI টুলকিট। Compose-এ লেআউট নিয়ে কীভাবে কাজ করতে হয় তা জানুন।

UI আঁকা হল কাস্টম ভিউ তৈরি করার একটি অংশ মাত্র। এছাড়াও, আপনাকে ব্যবহারকারীর ইনপুটের উপর ভিত্তি করে এমনভাবে ভিউ দেখাতে হবে যা বাস্তব-জগতের অ্যাকশনের সাথে ঘনিষ্ঠভাবে সাদৃশ্যপূর্ণ।

আপনার অ্যাপের অবজেক্টগুলিকে বাস্তব অবজেক্টের মতো কাজ করতে দিন। যেমন, আপনার অ্যাপের ছবিগুলিকে অস্তিত্ব থেকে পপ আউট হতে এবং অন্য কোথাও আবার উপস্থিত হতে দেবেন না, কারণ বাস্তব জগতের অবজেক্টগুলি তা করে না। পরিবর্তে, আপনার ছবি এক জায়গা থেকে অন্য জায়গায় সরান।

ব্যবহারকারীরা ইন্টারফেসে সূক্ষ্ম আচরণ বা অনুভূতিও বুঝতে পারেন এবং বাস্তব জগতের অনুকরণ করে এমন সূক্ষ্মতার প্রতি সবচেয়ে ভালো প্রতিক্রিয়া দেখান। যেমন, ব্যবহারকারী কোনও UI অবজেক্ট ফ্লিং করলে, শুরুতে তাকে জড়তার অনুভূতি দিন যা মোশনকে বিলম্বিত করে। মোশনের শেষে, তাদের মোমেন্টামের অনুভূতি দিন যা অবজেক্টকে ফ্লিংয়ের বাইরে নিয়ে যায়।

এই পৃষ্ঠা থেকে জানা যায় যে কীভাবে Android ফ্রেমওয়ার্কের ফিচার ব্যবহার করে আপনার কাস্টম ভিউতে এইসব বাস্তব-জগতের আচরণ যোগ করবেন।

আপনি ইনপুট ইভেন্টের ওভারভিউ এবং প্রপার্টি অ্যানিমেশনের ওভারভিউ থেকে অতিরিক্ত সম্পর্কিত তথ্য পেতে পারেন।

ইনপুট জেসচার ম্যানেজ করুন

অন্যান্য অনেক UI ফ্রেমওয়ার্কের মতো, Android-এ ইনপুট ইভেন্ট মডেল কাজ করে। ব্যবহারকারীর অ্যাকশন ইভেন্টে পরিণত হয় যা কলব্যাক ট্রিগার করে এবং আপনার অ্যাপ ব্যবহারকারীর কাছে কীভাবে প্রতিক্রিয়া জানাবে তা কাস্টমাইজ করতে আপনি কলব্যাক ওভাররাইড করতে পারেন। Android সিস্টেমে সবচেয়ে সাধারণ ইনপুট ইভেন্ট হল টাচ, যা onTouchEvent(android.view.MotionEvent) ট্রিগার করে। ইভেন্ট ম্যানেজ করতে এই পদ্ধতি ওভাররাইড করুন, যেমন:

Kotlin

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

জাভা

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

টাচ ইভেন্ট নিজে থেকে বিশেষ উপযোগী নয়। আধুনিক টাচ UI ট্যাপ করা, টানা, ঠেলা, ফ্লিং করা এবং জুম করার মতো জেসচারের মাধ্যমে ইন্টার‍্যাকশনকে সংজ্ঞায়িত করে। র' টাচ ইভেন্টকে জেসচারে কনভার্ট করতে, Android GestureDetector প্রদান করে।

GestureDetector.OnGestureListener ইমপ্লিমেন্ট করে এমন ক্লাসের ইনস্ট্যান্স পাস করে GestureDetector তৈরি করুন। আপনি যদি শুধুমাত্র কয়েকটি জেসচার প্রসেস করতে চান, তাহলে 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)

জাভা

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
    }
}

জাভা

@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 রিটার্ন করে। তারপর আপনি নিজের কাস্টম জেসচার-শনাক্তকরণ কোড রান করতে পারবেন।

বাস্তবসম্মত মোশন তৈরি করা

জেসচার হল টাচস্ক্রিন ডিভাইস নিয়ন্ত্রণ করার একটি শক্তিশালী উপায়, তবে সেগুলি স্বজ্ঞাত নাও হতে পারে এবং মনে রাখা কঠিন হতে পারে যদি না সেগুলি শারীরিকভাবে সম্ভাব্য ফলাফল তৈরি করে।

যেমন, ধরুন আপনি একটি হরাইজন্টাল ফ্লিং জেসচার প্রয়োগ করতে চান যা ভিউতে আঁকা আইটেমকে তার ভার্টিক্যাল অক্ষের চারপাশে ঘোরার জন্য সেট করে। এই জেসচার তখনই অর্থপূর্ণ হয় যখন UI ফ্লিংয়ের দিকে দ্রুত সরে গিয়ে উত্তর দেয়, তারপর ধীরে হয়ে যায়, যেন ব্যবহারকারী ফ্লাইহুইল পুশ করে সেটিকে ঘোরাচ্ছেন।

কীভাবে স্ক্রল জেসচার অ্যানিমেট করবেন সেই সংক্রান্ত ডকুমেন্টেশন থেকে আপনি নিজের স্ক্রল ব্যবহার কীভাবে প্রয়োগ করবেন সেই সম্পর্কে বিস্তারিত ব্যাখ্যা পাবেন। কিন্তু ফ্লাইহুইলের অনুভূতি সিমুলেট করা সহজ নয়। ফ্লাইহুইল মডেল সঠিকভাবে কাজ করার জন্য অনেক পদার্থবিদ্যা এবং গণিত প্রয়োজন। সৌভাগ্যবশত, Android এই এবং অন্যান্য আচরণ সিমুলেট করার জন্য সহায়ক ক্লাস প্রদান করে। ফ্লাইহুইল-স্টাইল ফ্লিং জেসচার ম্যানেজ করার জন্য Scroller ক্লাস হল ভিত্তি।

ফ্লিং শুরু করতে, fling() কল করুন, যেখানে ফ্লিংয়ের শুরুর গতিবেগ এবং সর্বনিম্ন ও সর্বোচ্চ x ও y মান উল্লেখ করা থাকবে। গতিবেগের মানের জন্য, আপনি 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
}

জাভা

@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)
    }
}

জাভা

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

Scroller ক্লাস আপনার জন্য স্ক্রল পজিশন গণনা করে, কিন্তু এটি আপনার ভিউতে অটোমেটিক সেই পজিশন প্রয়োগ করে না। স্ক্রলিং অ্যানিমেশন যাতে মসৃণ দেখায় তার জন্য নতুন কোঅর্ডিনেট প্রায়শই প্রয়োগ করুন। এটি করার দুটি উপায় আছে:

  • fling() কল করার পরে postInvalidate() কল করে জোর করে রিড্র করুন। এই টেকনিকের জন্য আপনাকে onDraw()-এ স্ক্রল অফসেট গণনা করতে হবে এবং স্ক্রল অফসেট পরিবর্তন হওয়ার সাথে সাথে postInvalidate() কল করতে হবে।
  • ফ্লিংয়ের সময়সীমার জন্য অ্যানিমেট করতে ValueAnimator সেট-আপ করুন এবং addUpdateListener()-এ কল করে অ্যানিমেশন আপডেট প্রসেস করার জন্য লিসেনার যোগ করুন। এই টেকনিকের সাহায্যে আপনি View-এর প্রপার্টি অ্যানিমেট করতে পারবেন।

আপনার ট্রানজিশন স্মুথ করুন

ব্যবহারকারীরা আশা করেন যে আধুনিক UI-তে বিভিন্ন স্টেটের মধ্যে ট্রানজিশন মসৃণ হবে: UI এলিমেন্ট হঠাৎ করে দেখা যাওয়া ও অদৃশ্য হয়ে যাওয়ার পরিবর্তে ফেড-ইন ও ফেড-আউট হবে এবং মোশন হঠাৎ করে শুরু ও শেষ হওয়ার পরিবর্তে মসৃণভাবে শুরু ও শেষ হবে। Android প্রপার্টি অ্যানিমেশন ফ্রেমওয়ার্ক স্মুথ ট্রানজিশন আরও সহজ করে তোলে।

অ্যানিমেশন সিস্টেম ব্যবহার করতে, যখনই কোনও প্রপার্টি আপনার ভিউয়ের অ্যাপিয়ারেন্সকে প্রভাবিত করে এমন কিছু পরিবর্তন করে, তখন সরাসরি প্রপার্টি পরিবর্তন করবেন না। পরিবর্তে, পরিবর্তন করতে ValueAnimator ব্যবহার করুন। নিচের উদাহরণে, ভিউতে বেছে নেওয়া চাইল্ড কম্পোনেন্ট পরিবর্তন করলে, সম্পূর্ণ রেন্ডার করা ভিউ ঘুরে যায় যাতে সিলেকশন পয়েন্টারটি কেন্দ্রে থাকে। ValueAnimator কয়েকশো মিলিসেকেন্ড সময় ধরে রোটেশন পরিবর্তন করে, অবিলম্বে নতুন রোটেশন ভ্যালু সেট করে না।

Kotlin

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

জাভা

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()

জাভা

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