কম্পিউটার টিউটোরিয়াল

কোটলিন অ্যান্ড্রয়েড অ্যানিমেশনকে অ্যাক্সেসিবল করা: একটি প্র্যাক্টিক্যাল গাইড

প্রথম بار অ্যান্ড্রয়েডে অবদান রাখার জন্য উদাহরণ খোঁজার সময়, কোটলিনে লেখা অ্যানিমেশনের জন্য খুব কম উদাহরণ পাওয়া যাচ্ছিল। নেটিভ অ্যানিমেশনে অ্যাক্সেসিবিলিটির বিবেচনা সম্পর্কেও কোড উদাহরণ নাগণ্য ছিল।

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

কোটলিন অ্যান্ড্রয়েড অ্যানিমেশনকে অ্যাক্সেসিবল করা: একটি প্র্যাক্টিক্যাল গাইড
অ্যাপের চূড়ান্ত ফলাফলের GIF

অ্যান্ড্রয়েড অ্যাক্সেসিবিলিটি (a11y)

সকল অ্যান্ড্রয়েড ডিভাইসে টকব্যাক নামে একটি বিল্ট-ইন স্ক্রিন রিডার থাকে। এটি ডিভাইসের সেটিংস থেকে চালু করা যায় এবং প্রথমবার ব্যবহার করার গাইডও রয়েছে। জেসচার ব্যবহার করে পৃষ্ঠা নেভিগেট করা হয়, এবং ফোকাসকৃত এলিমেন্টের বর্ণনা بصوتপ্রকাশ করা হয়। এর অভাবেই অ্যাপটি অনেক দৃষ্টিবাধিত ব্যবহারকারীর জন্য অনुपযোগী হয়ে যায়।

সবচেয়ে গুরুত্বপূর্ণ বিষয় হল সঠিক এলিমেন্টগুলো ফোকাসযোগ্য হওয়া, তাদের বর্ণনা থাকা, এবং ভিউতে পরিবর্তন হলে তা ঘোষণা করা।

একই সেটিংস মেনুতে ডিফল্ট বেস ফন্ট সাইজ সমায়োজিত করা যায়, যা ১.০ থেকে স্কেল করে। ভিউগুলো এই ফন্ট সাইজ পরিবর্তনের সাথে প্রতিক্রিয়া জানাতে হবে, যাতে সব এলিমেন্ট উপস্থিত এবং কার্যকর থাকে।

লেআউট

এখানে লেআউটের স্টাইলিং নির্দিষ্টات আলোচনা করব না কারণ সেগুলো এই উদাহরণের জন্য খুবই অনন্য, তবে অ্যাক্সেসিবিলিটির দিকগুলো হাইলাইট করার যোগ্য।

দুই প্রপার্টি ব্যবহার করা হয়েছে: android:contentDescription এবং android:importantForAccessibility।

contentDescription হল वो টেক্সট যা একটি এলিমেন্ট ফোকাস পেলে পড়া হয়। যেকোনো ImageView যা ফোকাস পাবে, তার জন্য এটি অত্যন্ত আবশ্যক, না হলে স্ক্রিন রিডার ব্যবহারকারীকে বুলবোলে 'আনলেবেলড' বলবে।

যদি এটি একটি বাটন হতো, তবে ডিফল্টভাবে এটি '<বর্ণনা> বাটন, এক্টিভেট করতে ডবল ট্যাপ করুন' পড়ত। কিন্তু আমাদের ImageView আইকনের জন্য আমরা ম্যানুয়ালি অ্যাকশন নির্দিষ্ট করি কারণ ডিফল্টটি নেই।

android:contentDescription="ত الإضافية ব্যক্তির তথ্য টগল করতে ট্যাপ করুন"

আমরা importantForAccessibility:no ব্যবহার করি '+' TextView-এর জন্য ফোকাস বন্ধ করার জন্য, কারণ দুটি ব্যাজের নিচের টেক্সট বর্ণনা দেয় এবং '+' ভয়েস আউটপুটে পড়লে আরও বিভ্রান্তিকর।

এই দুটি ক্ষেত্রে, টকব্যাক চালু করে একটি রিয়েল ডিভাইসে ম্যানুয়াল টেস্টিংই বিনা ভিজ্যুয়ালস প্রসঙ্গ বোঝার সেরা নির্দেশক।

এক্সপ্যান্ড অ্যানিমেশন

আমাদের অ্যানিমেশন 'ইনফো' আইকন ট্যাপে এক্টিভেট হবে, একটি ডিটেইলস সেকশনের এক্সপ্যান্ড টগল করবে।

এটি একটি একক এক্টিভিটির ভিতরে করব যাতে কেবল অ্যানিমেশন কোডে ফোকাস করতে পারি। বাস্তব অ্যাপে, এই ভিউটি আরও সম্ভবত নিজস্ব ফ্র্যাগমেন্ট বা রিসাইklar ভিউতে থাকবে, তাই আরও অ্যাবস্ট্রাক্ট কোড কাঠামো ব্যবহার করা হবে।

লিসেনার সেট করা

আমাদের উদাহরণ এক্টিভিটির onCreate-এ আমাদের প্রথমে আমাদের আইকনে একটি লিসেনার সেট করতে হবে এবং টগল করার জন্য ভিউটি পাস করতে হবে।

infoIcon.setOnClickListener { toggleCardBody(root.personEntryBody) }

আমরা ক্লাসের ভিতরে একটি ভেরিয়েবলও সেট আপ করি যা ট্র্যাক করবে ভিউটি টগল করা হয়েছে কিনা, প্রাথমিকভাবে বন্ধ অবস্থায় সেট করে।

private var isToggled = false

টগল এক্সপ্যান্ড অ্যানিমেশন

আমাদের লেআউটে, personEntryBody-র উচ্চতা 0dp সেট করা আছে।

इसे খোলার জন্য আমাদের নতুন উচ্চতা জানতে হবে, অ্যানিমেশন কতক্ষণ চলবে, এবং অ্যানিমেশনের প্রতিটি মুহূর্তে উচ্চতা কত হবে।

তারপর isToggled-কে এর বিপরীত সেট করতে হবে, এবং নিশ্চিত করতে হবে যে আবার ট্যাপ করলে এটি বিপরীত কাজ করবে।

private fun toggleCardBody(body: View) { body.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) val maxHeight = body.measuredHeight + body.paddingTop + body.paddingBottom val startHeight = if (isToggled) maxHeight else 0 val targetHeight = if (isToggled) 0 else maxHeight val expandAnimator = ValueAnimator .ofInt(startHeight, targetHeight) .setDuration(200) expandAnimator.addUpdateListener { val value = it.animatedValue as Int body.layoutParams.height = value body.requestLayout() } expandAnimator.doOnEnd { isToggled = !isToggled } expandAnimator.start() }

যেহেতু ভিউটি প্রথমে ড্র করার সময় উচ্চতা ০, তাই এর লেআউট রিমেজার করে এর নতুন আকার হিসাব করতে হবে।

অ্যান্ড্রয়েড ভিউ লেআউট ডক্স অনুযায়ী, আমরা ভিউতে অ্যাসাইন করা লেআউট প্যারামসের সাথে measure() ব্যবহার করে প্রতিবার ইনফো আইকন ট্যাপলে রিমেজার করতে পারি।

ম্যাক্স উচ্চতা হিসাব করতে হলে টপ এবং বটম প্যাডিং ম্যানুয়ালি যোগ করতে হবে, কারণ এগুলো পরিমাপকৃত উচ্চতায় অন্তর্ভুক্ত থাকে না।

isToggled-এর উপর নির্ভর করে আমরা জানি আমরা ০ থেকে শুরু করব না максимальной উচ্চতা থেকে, এবং সেই অনুযায়ী টার্গেট উচ্চতা।

শুরু মান থেকে টার্গেট এন্ড মান পর্যন্ত যেতে আমরা একটি Value Animator ব্যবহার করি, এবং মিলিসেকেন্ডে ডুরেশন সেট করি। এই ডুরেশন প্রশস্তভাবে ম্যানুয়াল টেস্টিংয়ের উপর ভিত্তি করে UX ফিলের জন্য নির্ধারিত।

ValueAnimator .ofInt(startHeight, targetHeight) .setDuration(200)

আপডেট লিসেনারের সাথে ডুরেশনকে উচ্চতার সাথে বাঁধা হয়, প্রতিটি আপডেটের পরে নতুন লেআউট ড্র করার অনুরোধ করা হয় এবং উচ্চতা সমায়োজিত হয়।

 expandAnimator.addUpdateListener { val value = it.animatedValue as Int body.layoutParams.height = value body.requestLayout() } expandAnimator.doOnEnd { isToggled = !isToggled } expandAnimator.start()

যেহেতু আমরা কোটলিন ব্যবহার করছি, আমরা androidx লাইব্রেরি আমাদের build.gradle-এ যোগ করি এর doOnEnd এক্সটেনশনের সুবিধা পেতে। এটি আমাদের খুব সহজে isToggled ভেরিয়েবল ইনভার্ট করতে দেয়।

অবশেষে আমরা আমাদের অ্যানিমেশন শুরু করি! ইতিমধ্যেই আমাদের একটি বডি রয়েছে যা আইকন টাচে এক্সপ্যান্ড এবং কন্ট্রাক্ট করে!

স্মূথার অ্যানিমেশন

আমাদের অ্যানিমেশন টেকনিক্যালি ওরক করে, কিন্তু একটি চমৎকার অতিরিক্ত ধাপ হল ইন্টারপোলেটর যোগ করা যাতে মুভমেন্ট আরও প্রাকৃতিক লাগে।

expandAnimator.interpolator = FastOutSlowInInterpolator()

আমাদের অ্যাক্সেসিবিলিটি বাড়ানো

আমরা দুটি চূড়ান্ত জিনিস যোগ করব যাতে আমাদের a11y ব্যবহারকারীদের সাহায্য হয়।

প্রথমে আমরা নেভিগেশনে সাহায্য করতে একটি AccessibilityEvent ব্যবহার করতে পারি।

expandAnimator.doOnEnd { if (!isToggled) body.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) isToggled = !isToggled }

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

দ্বিতীয়, আমরা বিভিন্ন ফন্ট সাইজের অনুমতি দিই। measure() থেকে ফেরত আসা পরিমাপকৃত উচ্চতা ডিভাইসের অ্যাক্সেসিবিলিটি সেটিংসে সেট করা ফন্ট স্কেলিংকে অ্যাকাউন্ট করে না, তাই বড় স্কেলে বর্ণনার নিচের অংশ ক্রপ হয়ে যাবে কারণ এটি ফিট होने জন্য খুব বড়।

আমরা প্রোগ্রাম্যাটিক্যালি ফন্ট স্কেল অ্যাক্সেস করতে পারি, এবং এই স্কেলের ভিত্তিতে আমাদের উচ্চতা স্কেল করতে পারি। আমরা এটি ইন্টিজারে কনভার্ট করি কারণ ফন্ট স্কেল ফ্লোট ফলাফল দিতে পারে যা লেআউট উচ্চতার জন্য কাজ করবে না।

val a11yFontScale = body.context.resources.configuration.fontScale val maxHeight = ((body.measuredHeight + body.paddingTop + body.paddingBottom) * a11yFontScale).toInt()

সমাপ্ত!

কোটলিন অ্যান্ড্রয়েড অ্যানিমেশনকে অ্যাক্সেসিবল করা: একটি প্র্যাক্টিক্যাল গাইড
অ্যাপের চূড়ান্ত ফলাফলের GIF

এবং এটা হলো আমাদের চূড়ান্ত অ্যানিমেশন! শুধু কিছু অতিরিক্ত লাইন দিয়ে আমরা এর a11y কভারেজ বেস্টার করেছি এবং একটি স্মূথ এক্সপ্যান্ডিং সেকশন পেয়েছি যা কোটলিন এবং অ্যান্ড্রয়েড ব্যাজ প্রদর্শন করে 😊

পড়ার জন্য ধন্যবাদ 😊

ಇংরেজি: Here are a couple of other things I've written recently:

  • Customising CodeceptJS E2E tests
  • Testing React with Jest and Enzyme II

ব্যবহারিক অতিরিক্ত

  • জো বার্চের দুর্দান্ত androidx পোস্ট: Exploring KTX for Android
  • Android Accessibility Tutorial: Getting Started
  1. অ্যান্ড্রয়েডে Alexa কে ডিফল্ট অ্যাসিস্ট্যান্ট বানানোর পদ্ধতি

  2. অ্যান্ড্রয়েড ফোনকে উইন্ডোজ ফোনের মত দেখানোর পদ্ধতি

  3. আপনার প্রিয় ইউটিউব গানকে অ্যান্ড্রয়েড রিংটোন বানান

  4. অ্যান্ড্রয়েডে লারাভেল ৮ ইনস্টল করুন: ধাপে ধাপে গাইড