মার্জ করা ও মুছে দেওয়া

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

সেম্যান্টিক্স মার্জ করা

আপনি কোনও পেরেন্ট কম্পোজ করার উপযুক্ত আইটেমে clickable মডিফায়ার প্রয়োগ করলে, Compose এর অধীনে থাকা সব চাইল্ড এলিমেন্ট অটোমেটিক মার্জ হয়ে যায়। ডিফল্ট হিসেবে ইন্টার‍্যাক্টিভ Compose Material ও Foundation কম্পোনেন্ট কীভাবে মার্জিং স্ট্র্যাটেজি ব্যবহার করে তা বুঝতে, ইন্টার‍্যাক্টিভ এলিমেন্ট বিভাগ দেখুন।

কোনও কম্পোনেন্টে একাধিক কম্পোজেবল থাকা খুবই সাধারণ ব্যাপার। এইসব কম্পোজ করার মতো আইটেম একটি লজিক্যাল গ্রুপ তৈরি করতে পারে এবং প্রত্যেকটিতে গুরুত্বপূর্ণ তথ্য থাকতে পারে, কিন্তু আপনি এখনও অ্যাক্সেসিবিলিটি পরিষেবাগুলিকে সেগুলি একটি এলিমেন্ট হিসেবে দেখাতে চাইতে পারেন।

যেমন, এমন একটি কম্পোজ করার মতো আইটেমের কথা ভাবুন যা ব্যবহারকারীর অবতার, তার নাম এবং কিছু অতিরিক্ত তথ্য দেখায়:

UI এলিমেন্টের একটি গ্রুপ যার মধ্যে ব্যবহারকারীর নাম অন্তর্ভুক্ত। নামটি বেছে নেওয়া হয়েছে।
ছবি ১. UI এলিমেন্টের একটি গ্রুপ যার মধ্যে ব্যবহারকারীর নাম অন্তর্ভুক্ত। নামটি বেছে নেওয়া হয়েছে।

আপনি সিম্যান্টিক্স মডিফায়ারে mergeDescendants প্যারামিটার ব্যবহার করে এইসব এলিমেন্ট মার্জ করার জন্য কম্পোজ করার সুবিধা চালু করতে পারবেন। এইভাবে, অ্যাক্সেসিবিলিটি পরিষেবা কম্পোনেন্টকে একটি এন্টিটি হিসেবে বিবেচনা করে এবং ডিসেন্ডেন্টদের সমস্ত সিম্যান্টিক প্রপার্টি মার্জ করা হয়:

@Composable
private fun PostMetadata(metadata: Metadata) {
    // Merge elements below for accessibility purposes
    Row(modifier = Modifier.semantics(mergeDescendants = true) {}) {
        Image(
            imageVector = Icons.Filled.AccountCircle,
            contentDescription = null // decorative
        )
        Column {
            Text(metadata.author.name)
            Text("${metadata.date} • ${metadata.readTimeMinutes} min read")
        }
    }
}

অ্যাক্সেসিবিলিটি পরিষেবা এখন কন্টেনারের উপর ফোকাস করে এবং এর কন্টেন্ট মার্জ করে:

UI এলিমেন্টের একটি গ্রুপ যার মধ্যে ব্যবহারকারীর নাম অন্তর্ভুক্ত। সব এলিমেন্ট একসাথে বেছে নেওয়া হয়েছে।
ছবি ২. UI এলিমেন্টের একটি গ্রুপ যার মধ্যে ব্যবহারকারীর নাম অন্তর্ভুক্ত। সব এলিমেন্ট একসাথে বেছে নেওয়া হয়েছে।

প্রতিটি সিম্যান্টিক প্রপার্টির জন্য মার্জ করার কৌশল নির্দিষ্ট করা আছে। যেমন, ContentDescription প্রপার্টি, তালিকায় সব ডিসেন্ডেন্ট ContentDescription ভ্যালু যোগ করে। আপনি কোনও সিম্যান্টিক্স প্রপার্টির মার্জ করার স্ট্র্যাটেজি SemanticsProperties.kt-তে এর mergePolicy প্রয়োগ চেক করে দেখতে পারেন। প্রপার্টিতে পেরেন্ট বা চাইল্ড ভ্যালু থাকতে পারে, ভ্যালুগুলিকে একটি লিস্ট বা স্ট্রিংয়ে মার্জ করা যেতে পারে, মার্জ করার অনুমতি নাও থাকতে পারে এবং পরিবর্তে একটি ব্যতিক্রম দেখাতে পারে অথবা অন্য কোনও কাস্টম মার্জিং স্ট্র্যাটেজি থাকতে পারে।

এমন অন্যান্য পরিস্থিতিও রয়েছে যেখানে আপনি আশা করেন যে বাচ্চাদের সিম্যান্টিক্সকে অভিভাবকের সাথে মার্জ করা হবে, কিন্তু তা হয় না। নিচের উদাহরণে, আমাদের কাছে clickable চাইল্ড এলিমেন্ট সহ লিস্ট আইটেম পেরেন্ট আছে এবং আমরা আশা করতে পারি যে পেরেন্ট সেগুলিকে মার্জ করে দেবে:

ছবি, কিছু টেক্সট ও বুকমার্ক আইকন সহ তালিকা আইটেম
ছবি ৩. ছবি, কিছু টেক্সট ও বুকমার্ক আইকন সহ তালিকা আইটেম।

@Composable
private fun ArticleListItem(
    openArticle: () -> Unit,
    addToBookmarks: () -> Unit,
) {

    Row(modifier = Modifier.clickable { openArticle() }) {
        // Merges with parent clickable:
        Icon(
            painter = painterResource(R.drawable.ic_logo),
            contentDescription = "Article thumbnail"
        )
        ArticleDetails()

        // Defies the merge due to its own clickable:
        BookmarkButton(onClick = addToBookmarks)
    }
}

ব্যবহারকারী clickable আইটেম Row টিপলে, নিবন্ধটি খুলে যায়। নেস্ট করা ভিতরে, নিবন্ধটি বুকমার্ক করার জন্য একটি BookmarkButton আছে। এই নেস্টেড বোতামটি আনমার্জড হিসেবে দেখাচ্ছে, তবে সারির মধ্যে থাকা বাকি চাইল্ড কন্টেন্ট মার্জ করা আছে:

মার্জ করা ট্রি-তে Row নোডের মধ্যে একটি তালিকায় একাধিক টেক্সট থাকে। আনমার্জড ট্রি-তে প্রতিটি টেক্সট কম্পোজ করার মতো আইটেমের জন্য আলাদা নোড থাকে।
ছবি ৪. মার্জ করা ট্রি-তে Row নোডের মধ্যে একটি তালিকায় একাধিক টেক্সট থাকে। আনমার্জ করা ট্রি-তে প্রতিটি Text কম্পোজ করার উপযুক্ত
আইটেমের জন্য আলাদা নোড থাকে।

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

চাইল্ড এলিমেন্টগুলি যখন তাদের পেরেন্টের অধীনে একটি লজিক্যাল ও বোধগম্য গ্রুপ তৈরি করে, তখন মার্জিং ব্যবহার করুন । কিন্তু নেস্টেড চিলড্রেনদের নিজস্ব সিম্যান্টিক যদি ম্যানুয়ালি অ্যাডজাস্ট বা সরিয়ে দিতে হয়, তাহলে অন্যান্য API আপনার প্রয়োজন আরও ভালোভাবে পূরণ করতে পারে (যেমন, clearAndSetSemantics)।

সিনট্যাক্স মুছে দিয়ে সিম্যান্টিক্স সেট করা

সম্পূর্ণভাবে মুছে ফেলতে বা ওভাররাইট করতে হলে, ব্যবহার করার জন্য একটি শক্তিশালী API হল clearAndSetSemantics।

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

মনে রাখবেন, খালি ল্যাম্বডা দিয়ে ক্লিয়ার করার সময়, ক্লিয়ার করা শব্দার্থ কোনও কনজিউমারকে পাঠানো হয় না, যেমন অ্যাক্সেসিবিলিটি, অটোফিল বা টেস্টিং। clearAndSetSemantics{/*semantic information*/}-এর মাধ্যমে কন্টেন্ট ওভাররাইট করার সময়, নতুন সিম্যান্টিক্স এলিমেন্ট ও তার ডিসেন্ডেন্টদের সব আগের সিম্যান্টিক্সকে পাল্টে দেয়।

নিচে কাস্টম টগল কম্পোনেন্টের একটি উদাহরণ দেওয়া হল, যা একটি আইকন ও টেক্সট সহ ইন্টার‍্যাক্ট করা যায় এমন সারি দ্বারা দেখানো হয়েছে:

// Developer might intend this to be a toggleable.
// Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied,
// a custom description is set, and a Role is applied.

@Composable
fun FavoriteToggle() {
    val checked = remember { mutableStateOf(true) }
    Row(
        modifier = Modifier
            .toggleable(
                value = checked.value,
                onValueChange = { checked.value = it }
            )
            .clearAndSetSemantics {
                stateDescription = if (checked.value) "Favorited" else "Not favorited"
                toggleableState = ToggleableState(checked.value)
                role = Role.Switch
            },
    ) {
        Icon(
            imageVector = Icons.Default.Favorite,
            contentDescription = null // not needed here

        )
        Text("Favorite?")
    }
}

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

উপরের স্নিপেটটি কাস্টম টগল কম্পোনেন্ট তৈরি করে বলে, আপনাকে টগল করার ক্ষমতা যোগ করতে হবে, সেই সাথে stateDescription, toggleableState এবং role সিম্যান্টিক্সও যোগ করতে হবে। এইভাবে, কম্পোনেন্ট স্ট্যাটাস ও সংশ্লিষ্ট অ্যাকশন উপলভ্য থাকে—যেমন, টকব্যাক "অ্যাক্টিভেট করতে ডবল ট্যাপ করুন" বলার পরিবর্তে "টগল করতে ডবল ট্যাপ করুন" বলে।

আসল সিম্যান্টিক্স মুছে দিয়ে নতুন, আরও বর্ণনামূলক সিম্যান্টিক্স সেট করার মাধ্যমে, অ্যাক্সেসিবিলিটি পরিষেবা এখন দেখতে পাচ্ছে যে এটি একটি টগল করা যায় এমন কম্পোনেন্ট যা স্টেট পরিবর্তন করতে পারে।

clearAndSetSemantics ব্যবহার করার সময়, নিম্নলিখিত বিষয়গুলি বিবেচনা করুন:

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

শব্দের অর্থ লুকানো

কিছু কিছু ক্ষেত্রে, এলিমেন্ট অ্যাক্সেসিবিলিটি পরিষেবায় পাঠানোর প্রয়োজন হয় না—সম্ভবত সেগুলির অতিরিক্ত তথ্য অ্যাক্সেসিবিলিটির জন্য অপ্রয়োজনীয়, অথবা এটি শুধুমাত্র ভিজ্যুয়ালি সাজসজ্জামূলক এবং ইন্টার‍্যাক্টিভ নয়। এইসব ক্ষেত্রে, আপনি hideFromAccessibility API-এর সাহায্যে এলিমেন্ট লুকাতে পারেন।

নিচে দেওয়া উদাহরণে এমন কম্পোনেন্ট দেখানো হয়েছে যা লুকানোর প্রয়োজন হতে পারে: কম্পোনেন্ট জুড়ে থাকা অপ্রয়োজনীয় ওয়াটারমার্ক এবং তথ্যকে সাজিয়ে আলাদা করার জন্য ব্যবহৃত অক্ষর।

@Composable
fun WatermarkExample(
    watermarkText: String,
    content: @Composable () -> Unit,
) {
    Box {
        WatermarkedContent()
        // Mark the watermark as hidden to accessibility services.
        WatermarkText(
            text = watermarkText,
            color = Color.Gray.copy(alpha = 0.5f),
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .semantics { hideFromAccessibility() }
        )
    }
}

@Composable
fun DecorativeExample() {
    Text(
        modifier =
        Modifier.semantics {
            hideFromAccessibility()
        },
        text = "A dot character that is used to decoratively separate information, like •"
    )
}

এখানে hideFromAccessibility ব্যবহার করলে, অ্যাক্সেসিবিলিটি পরিষেবা থেকে ওয়াটারমার্ক ও সজ্জা লুকানো থাকে, কিন্তু অন্যান্য ব্যবহারের ক্ষেত্রে, যেমন টেস্টিংয়ের জন্য সেগুলির সিম্যান্টিক্স বজায় থাকে।

ব্যবহারের উদাহরণ

কীভাবে আগের API-গুলির মধ্যে স্পষ্টভাবে পার্থক্য করতে হয় তা বোঝার জন্য ব্যবহারের ধরনগুলির একটি সংক্ষিপ্তসার নিচে দেওয়া হল:

  • কন্টেন্ট অ্যাক্সেসিবিলিটি পরিষেবার ব্যবহারের জন্য উপযুক্ত না হলে:
    • কন্টেন্ট সম্ভবত সাজসজ্জামূলক বা অপ্রয়োজনীয় হলেও পরীক্ষা করে দেখতে হবে, এমন ক্ষেত্রে hideFromAccessibility ব্যবহার করুন।
    • সব পরিষেবার জন্য প্যারেন্ট ও চাইল্ড সিম্যান্টিক্স মুছে ফেলতে হলে, খালি ল্যাম্বডা সহ clearAndSetSemantics{} ব্যবহার করুন।
    • কোনও কম্পোনেন্টের সিম্যান্টিক্স ম্যানুয়ালি সেট করতে হলে, ল্যাম্বডার মধ্যে কন্টেন্টের সাথে clearAndSetSemantics{/*content*/} ব্যবহার করুন।
  • কন্টেন্টকে কখন একটি এন্টিটি হিসেবে বিবেচনা করতে হবে এবং সম্পূর্ণ করার জন্য এর সব চাইল্ডের তথ্য প্রয়োজন:
    • মার্জ করা সেম্যান্টিক ডিসেন্ডেন্ট ব্যবহার করুন।
আলাদা আলাদা API ব্যবহারের উদাহরণ সহ টেবিল।
ছবি ৫. API ব্যবহারের বিভিন্ন সম্ভাব্য পরিস্থিতি সহ টেবিল।