প্রথমবার অ্যান্ড্রয়েড অবদানের উদাহরণ গবেষণা করার সময়, কোটলিনে লেখা অ্যানিমেশনের জন্য কয়েকটি উদাহরণ বিদ্যমান ছিল। স্থানীয় অ্যানিমেশনগুলির মধ্যে অ্যাক্সেসযোগ্যতার বিবেচনার কয়েকটি কোড উদাহরণও ছিল৷
তাই এখানে আমরা যেতে! কোটলিনে একটি নেটিভ 'প্রসারিত' অ্যানিমেশন লেখার দিকে নজর দেওয়া যাক এবং টকব্যাক বা বর্ধিত পাঠ্য চালু থাকা ব্যক্তিদের কীভাবে সহায়তা করা যায় সে সম্পর্কে কথা বলি। সমস্ত কোড এই উদাহরণ রেপোতে উপলব্ধ, এটির মধ্যে একটি অ্যানিমেটেড ভিউ সহ একটি একক কার্যকলাপ তৈরি করে। এই কোডের উপর ভিত্তি করে ক্যালাম টার্নারের সাথে সহ-লিখিত হয়েছিল৷
৷Android অ্যাক্সেসিবিলিটি (a11y)
সমস্ত অ্যান্ড্রয়েড ডিভাইস টকব্যাক নামে একটি অন্তর্নির্মিত স্ক্রিন রিডার সহ আসে। এটি ডিভাইসের সেটিংস থেকে চালু করা যেতে পারে, এবং এটিতে প্রথমবার ব্যবহারের নির্দেশিকাও অন্তর্নির্মিত রয়েছে৷ অঙ্গভঙ্গিগুলি পৃষ্ঠার চারপাশে নেভিগেট করতে ব্যবহৃত হয়, ফোকাস করা উপাদানগুলির বর্ণনা জোরে জোরে পড়া হয়৷ এটি ছাড়া, একটি অ্যাপ অনেক দৃষ্টি প্রতিবন্ধী ব্যবহারকারীদের জন্য অনুপযোগী হয়ে পড়ে।
মূল গুরুত্ব হল সঠিক উপাদানগুলি ফোকাসযোগ্য, বর্ণনা আছে এবং দৃশ্যের পরিবর্তনগুলি ঘোষণা করা হয়েছে৷
একই সেটিংস মেনুতে ডিফল্ট বেস ফন্টের আকার 1.0 থেকে স্কেলিং করে সামঞ্জস্য করা যেতে পারে। হরফের আকারে এই পরিবর্তনের প্রতিক্রিয়া দেখাতে হবে, সমস্ত উপাদান এখনও উপস্থিত এবং কাজ করছে৷
লেআউট
আমরা এখানে লেআউটের স্টাইলিং স্পেসিফিকেশনগুলি দেখব না কারণ সেগুলি এই উদাহরণের জন্য মোটামুটি অনন্য, তবে অ্যাক্সেসিবিলিটি স্পর্শগুলি হাইলাইট করার যোগ্য৷
দুটি বৈশিষ্ট্য ব্যবহার করা হয়:android:contentDescription
এবং android:importantForAccessibility
.
contentDescription
যখন একটি উপাদান ফোকাস লাভ করে তখন যা পড়া হয়। যেকোন ইমেজভিউ যে ফোকাস লাভ করে তার জন্য এটি অপরিহার্য, অন্যথায় একজন স্ক্রিন রিডার পরিবর্তে ব্যবহারকারীর কাছে অকেজো 'লেবেলবিহীন' পাঠ করবে।
এটি একটি বোতাম হলে এটি ডিফল্টরূপে '
android:contentDescription="tap to toggle extra person information"
এছাড়াও আমরা importantForAccessibility:no
ব্যবহার করি '+' টেক্সটভিউ-এর জন্য ফোকাস বন্ধ করতে, যেহেতু দুটি ব্যাজের নীচের পাঠ্য একটি বিবরণ প্রদান করে এবং তাই জোরে পড়লে '+' সহায়কের চেয়ে বেশি বিভ্রান্তিকর।
এই উভয়ের জন্য, টকব্যাক চালু থাকা একটি বাস্তব ডিভাইসে ম্যানুয়াল টেস্টিং হল ভিজ্যুয়াল ছাড়াই প্রসঙ্গটি অর্থপূর্ণ কিনা তার সর্বোত্তম ইঙ্গিত৷
অ্যানিমেশন প্রসারিত করুন
আমাদের অ্যানিমেশন একটি 'তথ্য' আইকন ট্যাপে সক্রিয় হবে, একটি বিশদ বিভাগ সম্প্রসারণ টগল করে।
আমরা শুধুমাত্র অ্যানিমেশন কোডের উপর ফোকাস করার অনুমতি দেওয়ার জন্য একটি একক কার্যকলাপের মধ্যে এটি করব। একটি বাস্তব বিশ্বের অ্যাপে, এটি প্রয়োগ করা দৃশ্যটি তার নিজস্ব খণ্ড বা পুনর্ব্যবহারকারী দৃশ্যের মধ্যে হওয়ার সম্ভাবনা বেশি, তাই আরও বিমূর্ত কোড কাঠামো ব্যবহার করা হবে।
একজন শ্রোতা সেট করা
আমাদের উদাহরণের মধ্যে কার্যকলাপের 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()
}
যেহেতু ভিউটি প্রাথমিকভাবে আঁকা হয় তার উচ্চতা 0 হয়, তাই আমাদের অবশ্যই এর লেআউট পুনরায় পরিমাপ করে এর নতুন আকার গণনা করতে হবে।
Android ভিউ লেআউট ডক্সে বর্ণিত হিসাবে, আমরা measure()
ব্যবহার করতে পারি প্রতিবার তথ্য আইকনটি ট্যাপ করার সময় পুনরায় পরিমাপ করার জন্য আমরা ভিউতে নির্ধারিত লেআউট প্যারামগুলির সাথে।
সর্বাধিক উচ্চতা গণনা করতে আমাদের অবশ্যই উপরে এবং নীচের প্যাডিং যোগ করতে হবে, কারণ এগুলি পরিমাপিত উচ্চতায় অন্তর্ভুক্ত নয়৷
isToggled
এর উপর নির্ভর করে তারপর আমরা জানি যে আমরা 0 থেকে শুরু করছি নাকি প্রসারিত সর্বোচ্চ উচ্চতা থেকে শুরু করছি, এবং তাই বিপরীত লক্ষ্য উচ্চতা।
আমরা একটি ভ্যালু অ্যানিমেটর ব্যবহার করি প্রারম্ভিক মান থেকে টার্গেট এন্ড ভ্যালুতে যেতে, এবং সময়কাল ms-এ সেট করি। এই সময়কালটি সম্পূর্ণরূপে 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()
সমাপ্ত!
৷এবং সেখানে আমাদের এটি আছে, আমরা আমাদের চূড়ান্ত অ্যানিমেশনে পৌঁছেছি! মাত্র কয়েকটি অতিরিক্ত লাইনের সাহায্যে আমরা এর a11y কভারেজ ব্যাপকভাবে বৃদ্ধি করেছি এবং একটি কোটলিন এবং অ্যান্ড্রয়েড ব্যাজ প্রকাশ করে একটি মসৃণ প্রসারিত বিভাগ রয়েছে?
পড়ার জন্য ধন্যবাদ?
আমি সম্প্রতি লিখেছি এমন আরও কয়েকটি বিষয় এখানে রয়েছে:
- CodeceptJS E2E পরীক্ষা কাস্টমাইজ করা
- জেস্ট এবং এনজাইম II এর সাথে প্রতিক্রিয়া পরীক্ষা করা
উপযোগী অতিরিক্ত
- Android-এর জন্য KTX অন্বেষণে জো বার্চের দুর্দান্ত androidx পোস্ট
- Android অ্যাক্সেসিবিলিটি টিউটোরিয়াল:শুরু করা