একটি কাস্টম ভিউ ইন্টারেক্টিভ করুন

কম্পোজ পদ্ধতিটি চেষ্টা করুন
অ্যান্ড্রয়েডের জন্য Jetpack Compose হলো প্রস্তাবিত UI টুলকিট। Compose-এ কীভাবে লেআউট নিয়ে কাজ করতে হয় তা শিখুন।

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

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

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

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

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

ইনপুট অঙ্গভঙ্গি পরিচালনা করুন

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

কোটলিন

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

জাভা

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

শুধুমাত্র টাচ ইভেন্টগুলো তেমন কার্যকর নয়। আধুনিক টাচ ইউআইগুলো ট্যাপ করা, টানা, ধাক্কা দেওয়া, ছুঁড়ে ফেলা এবং জুম করার মতো জেসচারের মাধ্যমে ইন্টারঅ্যাকশনকে সংজ্ঞায়িত করে। সরাসরি টাচ ইভেন্টগুলোকে জেসচারে রূপান্তর করার জন্য অ্যান্ড্রয়েড GestureDetector প্রদান করে।

GestureDetector.OnGestureListener ইমপ্লিমেন্ট করে এমন একটি ক্লাসের ইনস্ট্যান্স পাস করে একটি GestureDetector তৈরি করুন। আপনি যদি শুধুমাত্র কয়েকটি জেসচার প্রসেস করতে চান, তাহলে GestureDetector.SimpleOnGestureListener ইন্টারফেসটি ইমপ্লিমেন্ট করার পরিবর্তে GestureDetector.OnGestureListener এক্সটেন্ড করতে পারেন। উদাহরণস্বরূপ, এই কোডটি এমন একটি ক্লাস তৈরি করে যা GestureDetector.SimpleOnGestureListener এক্সটেন্ড করে এবং onDown(MotionEvent) ওভাররাইড করে।

কোটলিন

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 ব্যবহার করতে পারেন।

কোটলিন

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 ফ্লিং-এর দিকে দ্রুত এগিয়ে গিয়ে আবার ধীর হয়ে যাবে; ঠিক যেন ব্যবহারকারী একটি ফ্লাইহুইলে ধাক্কা দিয়ে সেটিকে ঘোরাচ্ছে।

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

একটি ফ্লিং শুরু করতে, প্রারম্ভিক বেগ এবং ফ্লিংটির সর্বনিম্ন ও সর্বোচ্চ x এবং y মান সহ fling() কল করুন। বেগের মানের জন্য, আপনি GestureDetector দ্বারা গণনা করা মানটি ব্যবহার করতে পারেন।

কোটলিন

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() বর্তমান সময় পড়ে এবং ফিজিক্স মডেল ব্যবহার করে সেই সময়ের xy অবস্থান গণনা করে Scroller অবজেক্টের অভ্যন্তরীণ অবস্থা আপডেট করে। এই মানগুলো পেতে getCurrX() এবং getCurrY() কল করুন।

বেশিরভাগ ভিউ Scroller অবজেক্টের x এবং y অবস্থান সরাসরি scrollTo() ফাংশনে পাস করে। এই উদাহরণটি কিছুটা ভিন্ন: এটি ভিউটির ঘূর্ণন কোণ নির্ধারণ করতে বর্তমান স্ক্রল x অবস্থান ব্যবহার করে।

কোটলিন

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

জাভা

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

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

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

আপনার পরিবর্তনগুলিকে মসৃণ করুন

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

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

কোটলিন

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 থাকে যা একাধিক প্রপার্টির একযোগে অ্যানিমেশনের জন্য অপ্টিমাইজ করা, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

কোটলিন

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

জাভা

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