API ডিফল্ট

Material, Compose UI এবং Foundation API ডিফল্ট হিসেবে অনেক অ্যাক্সেসিবল প্র্যাক্টিস প্রয়োগ ও অফার করে। এগুলিতে বিল্ট-ইন সেম্যান্টিক থাকে যা সেগুলির নির্দিষ্ট ভূমিকা ও ফাংশন মেনে চলে। এর অর্থ হল, খুব কম বা কোনও অতিরিক্ত কাজ ছাড়াই বেশিরভাগ অ্যাক্সেসিবিলিটি সহায়তা প্রদান করা হয়।

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

Compose API-তে ডিফল্ট অ্যাক্সেসিবিলিটি সিম্যান্টিক্স ও প্যাটার্ন সম্পর্কে জানলে, অ্যাক্সেসিবিলিটি মাথায় রেখে সেগুলি ব্যবহার করতে সুবিধা হয়। এছাড়াও, এটি আপনাকে আরও কাস্টম কম্পোনেন্টে অ্যাক্সেসিবিলিটি সংক্রান্ত সহায়তা প্রদান করে।

ন্যূনতম টাচ টার্গেট সাইজ

স্ক্রিনে দেখানো এমন যেকোনও এলিমেন্ট যাতে কেউ ক্লিক, টাচ বা ইন্টার‍্যাক্ট করতে পারেন, সেটি নির্ভরযোগ্য ইন্টার‍্যাকশনের জন্য যথেষ্ট বড় হতে হবে। এইসব এলিমেন্টের সাইজ নির্ধারণ করার সময়, মেটেরিয়াল ডিজাইন অ্যাক্সেসিবিলিটি নির্দেশিকা সঠিকভাবে মেনে চলতে, ন্যূনতম সাইজ ৪৮dp সেট করতে ভুলবেন না।

Checkbox, RadioButton, Switch, Slider এবং Surface-এর মতো মেটেরিয়াল কম্পোনেন্ট—ইন্টার্নালি এই ন্যূনতম সাইজ সেট করে, কিন্তু শুধুমাত্র যখন কম্পোনেন্ট ব্যবহারকারীর অ্যাকশন গ্রহণ করতে পারে। যেমন, কোনও Checkbox-এর onCheckedChange প্যারামিটার কোনও নন-নাল ভ্যালুতে সেট করা থাকলে, চেকবক্সে অন্তত ৪৮ dp প্রস্থ ও উচ্চতা পাওয়ার জন্য প্যাডিং যোগ করা হয়।

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

৪৮ dp প্রস্থ ও উচ্চতা সহ ডিফল্ট প্যাডিং সহ একটি চেকবক্স।
ছবি ১. ডিফল্ট প্যাডিং সহ চেকবক্স।

onCheckedChange প্যারামিটারটি নাল হিসেবে সেট করা হলে, প্যাডিং অন্তর্ভুক্ত করা হয় না, কারণ কম্পোনেন্টের সাথে সরাসরি ইন্টার‍্যাক্ট করা যায় না।

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

প্যাডিং নেই এমন চেকবক্স।
ছবি ২. প্যাডিং ছাড়া চেকবক্স।

Switch, RadioButton বা Checkbox-এর মতো বেছে নেওয়ার কন্ট্রোল প্রয়োগ করার সময়, আপনি সাধারণত কম্পোজেবলে ক্লিক কলব্যাক null-এ সেট করে এবং পেরেন্ট কম্পোজেবলে toggleable বা selectable মডিফায়ার যোগ করে ক্লিক করা যায় এমন আচরণকে পেরেন্ট কন্টেনারে লিফ্ট করেন।

@Composable
private fun CheckableRow() {
    MaterialTheme {
        var checked by remember { mutableStateOf(false) }
        Row(
            Modifier
                .toggleable(
                    value = checked,
                    role = Role.Checkbox,
                    onValueChange = { checked = !checked }
                )
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Option", Modifier.weight(1f))
            Checkbox(checked = checked, onCheckedChange = null)
        }
    }
}

'বিকল্প' টেক্সটের পাশে একটি চেকবক্স যা বেছে নেওয়া ও বেছে না নেওয়া হচ্ছে।
ছবি ৩. ক্লিক করা যায় এমন আচরণ সহ চেকবক্স।

ক্লিক করা যায় এমন কম্পোজ করার মতো আইটেমের সাইজ ন্যূনতম টাচ টার্গেট সাইজের চেয়ে ছোট হলেও, Compose এখনও টাচ টার্গেট সাইজ বাড়িয়ে দেয়। এটি কম্পোজেবলের সীমানার বাইরে টাচ টার্গেটের সাইজ বাড়িয়ে এটি করে।

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

@Composable
private fun SmallBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .size(1.dp)
        )
    }
}

খুব ছোট ক্লিক করা যায় এমন বক্স, যেটি বক্সের পাশে ট্যাপ করে বড় টাচ টার্গেটে প্রসারিত করা হয়।
ছবি ৪. খুব ছোট ক্লিক করা যায় এমন বক্স যা বড় টাচ টার্গেটে প্রসারিত করা হয়েছে।

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

@Composable
private fun LargeBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
        )
    }
}

আগের উদাহরণের খুব ছোট বক্সটি বড় টাচ টার্গেট তৈরি করার জন্য আকারে বাড়ানো হয়েছে।
ছবি ৫. বড় বক্স টাচ টার্গেট।

গ্রাফিক এলিমেন্ট

আপনি কোনও Image বা Icon কম্পোজ করার মতো আইটেম ডিফাইন করলে, Android ফ্রেমওয়ার্কের কাছে অ্যাপ কী দেখাচ্ছে তা বোঝার কোনও অটোমেটিক উপায় থাকে না। আপনাকে গ্রাফিক এলিমেন্টের টেক্সচুয়াল বিবরণ দিতে হবে।

এমন একটি স্ক্রিনের কথা ভাবুন যেখানে ব্যবহারকারী বন্ধুদের সাথে বর্তমান পৃষ্ঠা শেয়ার করতে পারবেন। এই স্ক্রিনে ক্লিক করা যায় এমন শেয়ার আইকন আছে:

ক্লিক করা যায় এমন চারটি আইকনের একটি স্ট্রিপ, যেখানে 'শেয়ার করুন' আইকনটি হাইলাইট করা আছে।
ছবি ৬. 'শেয়ার করুন' আইকন বেছে নেওয়া হয়েছে এমন ক্লিক করা যায় এমন আইকনের একটি সারি।

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

contentDescription প্যারামিটার একটি গ্রাফিক এলিমেন্ট বর্ণনা করে। লোকালাইজ করা স্ট্রিং ব্যবহার করুন, কারণ এটি ব্যবহারকারীর কাছে দৃশ্যমান।

@Composable
private fun ShareButton(onClick: () -> Unit) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.Share,
            contentDescription = stringResource(R.string.label_share)
        )
    }
}

কিছু গ্রাফিক এলিমেন্ট শুধুমাত্র সাজসজ্জার জন্য ব্যবহার করা হয় এবং আপনি হয়ত সেগুলি ব্যবহারকারীকে জানাতে চাইবেন না। আপনি contentDescription প্যারামিটার null হিসেবে সেট করলে, Android ফ্রেমওয়ার্ককে জানান যে এই এলিমেন্টের সাথে কোনও অ্যাকশন বা স্টেট যুক্ত নেই।

@Composable
private fun PostImage(post: Post, modifier: Modifier = Modifier) {
    val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1)

    Image(
        painter = image,
        // Specify that this image has no semantic meaning
        contentDescription = null,
        modifier = modifier
            .size(40.dp, 40.dp)
            .clip(MaterialTheme.shapes.small)
    )
}

contentDescription মূলত গ্রাফিক এলিমেন্টের জন্য ব্যবহার করা হয়, যেমন ছবি। Button বা Text-এর মতো মেটেরিয়াল কম্পোনেন্ট এবং clickable বা toggleable-এর মতো অ্যাকশনযোগ্য আচরণ, অন্যান্য পূর্বনির্ধারিত অর্থগত দিক সহ আসে যা তাদের অন্তর্নিহিত আচরণ বর্ণনা করে এবং অন্যান্য Compose API-এর মাধ্যমে পরিবর্তন করা যেতে পারে।

ইন্টার‌্যাক্টিভ এলিমেন্ট

Material ও Foundation Compose API এমন UI এলিমেন্ট তৈরি করে যেগুলির সাথে ব্যবহারকারীরা clickable ও toggleable মডিফায়ার API-এর মাধ্যমে ইন্টার‍্যাক্ট করতে পারেন। কারণ, ইন্টার‍্যাক্ট করা যায় এমন কম্পোনেন্টে একাধিক এলিমেন্ট থাকতে পারে, clickable এবং toggleable ডিফল্ট হিসেবে তাদের চিলড্রেনদের সিম্যান্টিক মার্জ করে, যাতে কম্পোনেন্টকে একটি লজিক্যাল এন্টিটি হিসেবে বিবেচনা করা হয়।

যেমন, কোনও মেটেরিয়ালে Button একটি বাচ্চার আইকন ও কিছু টেক্সট থাকতে পারে। বাচ্চাদের আলাদা আলাদা সত্তা হিসেবে না দেখে, Material Button ডিফল্ট হিসেবে এর চাইল্ড সিম্যান্টিককে মার্জ করে, যাতে অ্যাক্সেসিবিলিটি পরিষেবা সেই অনুযায়ী তাদের গ্রুপ করতে পারে:

মার্জ না করা বনাম মার্জ করা চিলড্রেন সিম্যান্টিক্স সহ বোতাম।
ছবি ৭. মার্জ না করা বনাম মার্জ করা চিলড্রেন সিম্যান্টিক্স সহ বোতাম।

একইভাবে, clickable মডিফায়ার ব্যবহার করলে কম্পোজ করার উপযুক্ত আইটেম এর ডিসেন্ডেন্টদের সিম্যান্টিক্স একটি এন্টিটিতে মার্জ হয়ে যায়, যা সংশ্লিষ্ট অ্যাকশন উপস্থাপনার সাথে অ্যাক্সেসিবিলিটি পরিষেবায় পাঠানো হয়:

Row(
    // Uses `mergeDescendants = true` under the hood
    modifier = Modifier.clickable { openArticle() }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open",
    )
    Text("Accessibility in Compose")
}

এছাড়াও, অ্যাক্সেসিবিলিটি পরিষেবাতে অতিরিক্ত তথ্য প্রদান করতে এবং অ্যাকশনের আরও পরিমার্জিত প্রতিনিধিত্ব অফার করতে, আপনি ক্লিক করা যায় এমন প্যারেন্টে একটি নির্দিষ্ট onClickLabel সেট করতে পারেন:

Row(
    modifier = Modifier
        .clickable(onClickLabel = "Open this article") {
            openArticle()
        }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open"
    )
    Text("Accessibility in Compose")
}

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

অ্যাকশনের ধরনের উপর নির্ভর করে এই মতামত পরিবর্তন হয়। বেশিক্ষণ ক্লিক করলে টকব্যাক "ডবল ট্যাপ করে ধরে রাখুন" সংক্রান্ত ইঙ্গিত দেবে, তারপরে লেবেল:

Row(
    modifier = Modifier
        .combinedClickable(
            onLongClickLabel = "Bookmark this article",
            onLongClick = { addToBookmarks() },
            onClickLabel = "Open this article",
            onClick = { openArticle() },
        )
) {}

কিছু ক্ষেত্রে, আপনি হয়ত clickable মডিফায়ার সরাসরি অ্যাক্সেস করতে পারবেন না (যেমন, এটি যদি কোনও লোয়ার নেস্টেড লেয়ারে সেট করা থাকে),কিন্তু তা সত্ত্বেও আপনি ডিফল্ট থেকে ঘোষণা লেবেল পরিবর্তন করতে চান। এটি করতে, অ্যাকশন উপস্থাপনা পরিবর্তন করতে, semantics মডিফায়ার ব্যবহার করে ঘোষণা পরিবর্তন করার clickable থেকে সেটিংস আলাদা করুন এবং সেখানে ক্লিক লেবেল সেট করুন:

@Composable
private fun ArticleList(openArticle: () -> Unit) {
    NestedArticleListItem(
        // Clickable is set separately, in a nested layer:
        onClickAction = openArticle,
        // Semantics are set here:
        modifier = Modifier.semantics {
            onClick(
                label = "Open this article",
                action = {
                    // Not needed here: openArticle()
                    true
                }
            )
        }
    )
}

আপনাকে ক্লিক অ্যাকশন দু'বার পাস করতে হবে না। আগে থেকে থাকা Compose API, যেমন clickable বা Button, আপনার হয়ে এটি ম্যানেজ করে। মার্জ করার লজিক যাচাই করে দেখে যে সবচেয়ে বাইরের মডিফায়ার লেবেল ও অ্যাকশন সেই তথ্যের জন্য নেওয়া হয়েছে যা উপস্থিত আছে। আগের উদাহরণে, NestedArticleListItem অটোমেটিক openArticle() ক্লিক অ্যাকশনকে এর clickable সিম্যান্টিক্সে পাস করে। আপনি দ্বিতীয় সিম্যান্টিক্স মডিফায়ার অ্যাকশনে ক্লিক অ্যাকশন নাল রাখতে পারেন। তবে, ক্লিক লেবেলটি দ্বিতীয় সিম্যান্টিক্স মডিফায়ার থেকে নেওয়া হয়েছে onClick(label = "Open this document") কারণ এটি প্রথমটিতে ছিল না।

এমন পরিস্থিতি হতে পারে যেখানে আপনি আশা করছেন যে বাচ্চাদের সিম্যান্টিক্স অভিভাবকের সিম্যান্টিক্সের সাথে মার্জ করা হবে, কিন্তু তা হয় না। আরও বিস্তারিত তথ্য পেতে, মার্জ করা ও মুছে দেওয়া লিঙ্ক দেখুন।

কাস্টম কম্পোনেন্ট

কাস্টম কম্পোনেন্ট তৈরি করার সময়, Material লাইব্রেরি বা অন্যান্য Compose লাইব্রেরিতে একই ধরনের কম্পোনেন্টের প্রয়োগ পর্যালোচনা করুন। তারপর, উপযুক্তভাবে এর অ্যাক্সেসিবিলিটি আচরণ অনুকরণ বা পরিবর্তন করুন। যেমন, আপনি যদি নিজের ইমপ্লিমেন্টেশনের মাধ্যমে Material Checkbox পরিবর্তন করেন, তাহলে আগে থেকে থাকা Checkbox ইমপ্লিমেন্টেশন দেখলে triStateToggleable মডিফায়ার যোগ করার কথা মনে পড়বে, যা কম্পোনেন্টের অ্যাক্সেসিবিলিটি প্রপার্টি ম্যানেজ করে। এছাড়াও, Foundation মডিফায়ার প্রচুর পরিমাণে ব্যবহার করুন, কারণ এর মধ্যে বিল্ট-ইন অ্যাক্সেসিবিলিটি সংক্রান্ত বিবেচনা এবং এই বিভাগে কভার করা বর্তমান Compose প্র্যাক্টিস অন্তর্ভুক্ত।

এছাড়াও, আপনি স্পষ্ট ও সেট সিমান্টিক্স বিভাগে কাস্টম টগল কম্পোনেন্টের একটি উদাহরণ এবং API নির্দেশিকায় কাস্টম কম্পোনেন্টে কীভাবে অ্যাক্সেসিবিলিটি সাপোর্ট করবেন সেই সম্পর্কে আরও বিস্তারিত তথ্য পাবেন।