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

একটি কম্পোজেবল যে প্রাথমিক তথ্য বহন করে, যেমন একটি Text কম্পোজেবলের টেক্সট স্ট্রিং, তা ছাড়াও UI এলিমেন্টগুলো সম্পর্কে আরও সম্পূরক তথ্য থাকা সহায়ক হতে পারে।

Compose-এ কোনও কম্পোনেন্টের অর্থ ও ভূমিকা সম্পর্কিত তথ্যকে সিম্যান্টিক্স বলা হয়, যা অ্যাক্সেসিবিলিটি, অটোফিল ও টেস্টিংয়ের মতো পরিষেবায় কম্পোজ করার উপযুক্ত আইটেম সম্পর্কে অতিরিক্ত প্রসঙ্গ প্রদান করার একটি উপায়। যেমন, কোনও ক্যামেরা আইকন দেখতে শুধু একটি ছবি মনে হতে পারে, কিন্তু এর অর্থগত অর্থ হতে পারে "ছবি তোলো"।

উপযুক্ত Compose API-এর সাথে উপযুক্ত সিম্যান্টিক্স একত্রিত করে, আপনি অ্যাক্সেসিবিলিটি পরিষেবাগুলিকে আপনার কম্পোনেন্ট সম্পর্কে যতটা সম্ভব তথ্য প্রদান করেন, যা তারপরে ব্যবহারকারীর কাছে এটি কীভাবে উপস্থাপন করতে হবে তা সিদ্ধান্ত নেয়।

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

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

কম্পোজ করার উপযুক্ত আইটেমের অর্থ সেম্যান্টিক প্রপার্টির মাধ্যমে বোঝা যায়। যেমন, Text কম্পোজ করার উপযুক্ত আইটেমে text সিমান্টিক প্রপার্টি থাকে, কারণ সেটিই হল সেই কম্পোজ করার উপযুক্ত আইটেমের অর্থ। Icon-এ একটি contentDescription প্রপার্টি থাকে (ডেভেলপার সেট করে থাকলে) যা টেক্সটে আইকনের অর্থ কী তা জানায়।

কীভাবে সিমান্টিক প্রপার্টি কোনও কম্পোজ করার উপযুক্ত আইটেমের অর্থ বোঝায় তা বিবেচনা করো। একটি Switch বিবেচনা করুন। ব্যবহারকারীর কাছে এটি এইভাবে দেখানো হয়:

ছবি ১. Switch-এর "চালু" ও "বন্ধ" অবস্থা।

এই এলিমেন্টের অর্থ বর্ণনা করতে, আপনি নিম্নলিখিত বিষয়গুলি বলতে পারেন: "এটি একটি সুইচ, যা 'চালু' অবস্থায় টগল করা যায় এমন একটি এলিমেন্ট। আপনি এটির সাথে ইন্টার‍্যাক্ট করতে এটিতে ক্লিক করতে পারেন।"

ঠিক এই কারণেই সিম্যান্টিক প্রপার্টি ব্যবহার করা হয়। এই Switch এলিমেন্টের সিনট্যাক্স নোডে নিম্নলিখিত প্রপার্টি থাকে, যা লেআউট ইন্সপেক্টরের মাধ্যমে দেখা যায়:

লেআউট ইন্সপেক্টর, Switch কম্পোজ করার উপযুক্ত আইটেমের সিম্যান্টিক প্রপার্টি দেখাচ্ছে
ছবি ২. Switch কম্পোজ করার উপযুক্ত
আইটেমের 'অর্থগত' প্রপার্টি লেআউট ইন্সপেক্টর দেখাচ্ছে।

Role এলিমেন্টের ধরন নির্দেশ করে। StateDescription-এ বর্ণনা করা আছে যে কীভাবে "চালু" স্ট্যাটাসকে রেফারেন্স করা উচিত। সাধারণত এটি "চালু" শব্দের লোকালাইজ করা ভার্সন হলেও, প্রসঙ্গ অনুযায়ী এটিকে আরও নির্দিষ্ট করা যেতে পারে (যেমন, "চালু করা হয়েছে")। ToggleableState হল Switch-এর বর্তমান অবস্থা। এই এলিমেন্টের সাথে ইন্টার‍্যাক্ট করার জন্য ব্যবহৃত পদ্ধতিকে OnClick প্রপার্টি রেফার করে।

আপনার অ্যাপের প্রতিটি কম্পোজ করার মতো আইটেমের সিমেন্টিক প্রপার্টি ট্র্যাক করলে অনেক শক্তিশালী সম্ভাবনা আনলক করা যায়:

  • অ্যাক্সেসিবিলিটি পরিষেবা, স্ক্রিনে দেখানো UI-কে প্রতিনিধিত্ব করতে প্রপার্টি ব্যবহার করে এবং ব্যবহারকারীদের সাথে ইন্টার‍্যাক্ট করতে দেয়। Switch কম্পোজ করার মতো আইটেমের জন্য, টকব্যাক এগুলি ঘোষণা করতে পারে: "চালু আছে; সুইচ করুন; টগল করতে ডবল ট্যাপ করুন"। ব্যবহারকারী স্ক্রিনে ডবল ট্যাপ করে বন্ধ করার বিকল্প টগল করতে পারবেন।
  • নোড খুঁজে পেতে, সেগুলির সাথে ইন্টার‍্যাক্ট করতে এবং অ্যাসারশন তৈরি করতে টেস্টিং ফ্রেমওয়ার্ক প্রপার্টি ব্যবহার করে:
    val mySwitch = SemanticsMatcher.expectValue(
        SemanticsProperties.Role, Role.Switch
    )
    composeTestRule.onNode(mySwitch)
        .performClick()
        .assertIsOff()

Compose ফাউন্ডেশন লাইব্রেরির উপরে তৈরি কম্পোজ করার মতো আইটেম ও মডিফায়ার আগে থেকেই আপনার জন্য প্রাসঙ্গিক প্রপার্টি ডিফল্ট হিসেবে সেট করে দেয়। বিকল্প হিসেবে, নির্দিষ্ট ব্যবহারের ক্ষেত্রে অ্যাক্সেসিবিলিটি সংক্রান্ত সহায়তা উন্নত করতে অথবা আপনার কম্পোজেবল আইটেমের মার্জ করা বা ক্লিয়ার করার স্ট্র্যাটেজি পরিবর্তন করতে, আপনি এইসব প্রপার্টি ম্যানুয়ালি পরিবর্তন করতে পারেন।

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

সিমান্টিক প্রপার্টির সম্পূর্ণ তালিকা দেখতে, SemanticsProperties অবজেক্ট দেখুন। সম্ভাব্য অ্যাক্সেসিবিলিটি অ্যাকশনের সম্পূর্ণ তালিকা পেতে, SemanticsActions অবজেক্ট দেখুন।

শিরোনাম

অ্যাপে প্রায়ই টেক্সট-নির্ভর কন্টেন্ট সহ স্ক্রিন থাকে, যেমন, বড় নিবন্ধ বা খবরের পৃষ্ঠা, যেগুলি সাধারণত শিরোনাম সহ বিভিন্ন উপবিভাগে বিভক্ত থাকে:

স্ক্রল করা যায় এমন কন্টেনারে নিবন্ধের টেক্সট সহ ব্লগ পোস্ট।
ছবি ৩. স্ক্রল করা যায় এমন কন্টেনারে নিবন্ধের টেক্সট সহ ব্লগ পোস্ট।

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

@Composable
private fun Subsection(text: String) {
    Text(
        text = text,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier.semantics { heading() }
    )
}

সতর্কতা ও পপ-আপ

আপনার কম্পোনেন্ট যদি Snackbar-এর মতো কোনও অ্যালার্ট বা পপ-আপ হয়, তাহলে আপনি অ্যাক্সেসিবিলিটি পরিষেবাকে এই সিগন্যাল দিতে পারেন যে কন্টেন্টে নতুন স্ট্রাকচার বা আপডেট ব্যবহারকারীদের জানানো যেতে পারে।

liveRegion সিম্যান্টিক্স প্রপার্টির সাহায্যে অ্যালার্টের মতো কম্পোনেন্ট চিহ্নিত করা যেতে পারে। এটি অ্যাক্সেসিবিলিটি পরিষেবাগুলিকে এই কম্পোনেন্ট বা এর চাইল্ড কম্পোনেন্টে পরিবর্তন সম্পর্কে ব্যবহারকারীকে অটোমেটিক বিজ্ঞপ্তি পাঠাতে দেয়:

PopupAlert(
    message = "You have a new message",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Polite
    }
)

বেশিরভাগ ক্ষেত্রে, যেখানে ব্যবহারকারীর মনোযোগ শুধুমাত্র স্ক্রিনে দেখানো অ্যালার্ট বা গুরুত্বপূর্ণ পরিবর্তন হওয়া কন্টেন্টের দিকেই অল্প সময়ের জন্য আকর্ষণ করা উচিত, সেখানে আপনার liveRegionMode.Polite ব্যবহার করা উচিত।

বাধা সৃষ্টিকারী ফিডব্যাক এড়াতে আপনাকে liveRegion.Assertive কম ব্যবহার করতে হবে। এটি এমন পরিস্থিতিতে ব্যবহার করা উচিত যেখানে সময়-সংবেদনশীল কন্টেন্ট সম্পর্কে ব্যবহারকারীদের সচেতন করা অত্যন্ত গুরুত্বপূর্ণ:

PopupAlert(
    message = "Emergency alert incoming",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Assertive
    }
)

লাইভ অঞ্চলগুলি এমন কন্টেন্টে ব্যবহার করা উচিত নয় যা ঘন ঘন আপডেট হয়, যেমন কাউন্টডাউন টাইমার, যাতে ব্যবহারকারীদের ক্রমাগত ফিডব্যাক দিয়ে অভিভূত না করা যায়।

জানলার মতো কম্পোনেন্ট

ModalBottomSheet-এর মতো উইন্ডো-সদৃশ কাস্টম কম্পোনেন্টকে আশেপাশের কন্টেন্ট থেকে আলাদা করতে অতিরিক্ত সিগন্যালের প্রয়োজন। এর জন্য, আপনি paneTitle সিম্যান্টিক্স ব্যবহার করতে পারেন, যাতে কোনও প্রাসঙ্গিক উইন্ডো বা প্যানে পরিবর্তন হলে তা অ্যাক্সেসিবিলিটি পরিষেবা দ্বারা যথাযথভাবে উপস্থাপন করা যায়, সাথে এর মূল সিম্যান্টিক তথ্যও থাকে:

ShareSheet(
    message = "Choose how to share this photo",
    modifier = Modifier
        .fillMaxWidth()
        .align(Alignment.TopCenter)
        .semantics { paneTitle = "New bottom sheet" }
)

রেফারেন্স হিসেবে, Material 3 কীভাবে এর কম্পোনেন্টের জন্য paneTitle ব্যবহার করে তা দেখুন।

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

অন্যান্য কন্টেন্টের ক্ষেত্রে, যেমন, কোনও সমস্যার মতো কম্পোনেন্ট, অ্যাক্সেসিবিলিটি সংক্রান্ত প্রয়োজনীয়তা আছে এমন ব্যবহারকারীদের জন্য মূল শব্দার্থ সংক্রান্ত তথ্য আরও বিস্তারিতভাবে দিতে পারেন। সমস্যার স্ট্যাটাস নির্ধারণ করার সময়, আপনি অ্যাক্সেসিবিলিটি পরিষেবাকে এর error সিম্যান্টিক্স সম্পর্কে জানাতে এবং সমস্যার মেসেজিংয়ের আরও বিস্তারিত বিবরণ দিতে পারবেন।

এই উদাহরণে, টকব্যাক মূল সমস্যার টেক্সট সংক্রান্ত তথ্য পড়ে শোনাবে, তারপরে আরও বিস্তারিত মেসেজ পড়ে শোনাবে:

Error(
    errorText = "Fields cannot be empty",
    modifier = Modifier
        .semantics {
            error("Please add both email and password")
        }
)

প্রগ্রেস ট্র্যাকিং কম্পোনেন্ট

প্রোগ্রেস ট্র্যাক করে এমন কাস্টম কম্পোনেন্টের ক্ষেত্রে, আপনি ব্যবহারকারীদের তাদের প্রোগ্রেস পরিবর্তন সম্পর্কে বিজ্ঞপ্তি দিতে চাইতে পারেন, এর মধ্যে বর্তমান প্রোগ্রেস ভ্যালু, এর রেঞ্জ এবং স্টেপ সাইজ অন্তর্ভুক্ত। আপনি progressBarRangeInfo সিম্যান্টিক্সের সাহায্যে এটি করতে পারেন―এটি নিশ্চিত করে যে অ্যাক্সেসিবিলিটি পরিষেবাগুলি প্রগ্রেস পরিবর্তন সম্পর্কে সচেতন এবং সেই অনুযায়ী ব্যবহারকারীদের আপডেট করতে পারে। এছাড়াও, বিভিন্ন সহায়ক প্রযুক্তিতে প্রগ্রেস বাড়ানো ও কমানোর ইঙ্গিত দেওয়ার আলাদা আলাদা উপায় থাকতে পারে।

ProgressInfoBar(
    modifier = Modifier
        .semantics {
            progressBarRangeInfo =
                ProgressBarRangeInfo(
                    current = progress,
                    range = 0F..1F
                )
        }
)

তালিকা ও আইটেম সম্পর্কিত তথ্য

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

এই দীর্ঘ তালিকায়, collectionInfo ও collectionItemInfo শব্দার্থ ব্যবহার করে, অ্যাক্সেসিবিলিটি পরিষেবা টেক্সচুয়াল শব্দার্থ সংক্রান্ত তথ্যের পাশাপাশি, ব্যবহারকারীদের জানাতে পারে যে তারা মোট সংগ্রহে কত নম্বর আইটেমে আছেন:

MilkyWayList(
    modifier = Modifier
        .semantics {
            collectionInfo = CollectionInfo(
                rowCount = milkyWay.count(),
                columnCount = 1
            )
        }
) {
    milkyWay.forEachIndexed { index, text ->
        Text(
            text = text,
            modifier = Modifier.semantics {
                collectionItemInfo =
                    CollectionItemInfo(index, 0, 0, 0)
            }
        )
    }
}

রাজ্যের বিবরণ

একটি কম্পোজেবল, সিম্যান্টিক্সের জন্য stateDescription নির্ধারণ করতে পারে, যা কম্পোজেবল যে স্টেটে আছে তা রিড-আউট করার জন্য Android ফ্রেমওয়ার্ক ব্যবহার করে। যেমন, টগল করা যায় এমন কম্পোজ করার উপযুক্ত আইটেম "চেক করা" বা "চেক করা নেই" এমন অবস্থায় থাকতে পারে। কিছু ক্ষেত্রে, Compose-এর ব্যবহার করা ডিফল্ট স্টেট বিবরণ লেবেল ওভাররাইড করতে হতে পারে। টগল করা যায় এমন কম্পোজ করার আগে, আপনি স্পষ্টভাবে স্টেট বিবরণ লেবেল নির্দিষ্ট করে এটি করতে পারেন:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
    val stateSubscribed = stringResource(R.string.subscribed)
    val stateNotSubscribed = stringResource(R.string.not_subscribed)
    Row(
        modifier = Modifier
            .semantics {
                // Set any explicit semantic properties
                stateDescription = if (selected) stateSubscribed else stateNotSubscribed
            }
            .toggleable(
                value = selected,
                onValueChange = { onToggle() }
            )
    ) {
        /* ... */
    }
}

কাস্টম অ্যাকশন

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

সোয়াইপ করে বাতিল করার জেসচার আরও অ্যাক্সেসযোগ্য করে তুলতে, আপনি এটিকে একটি কাস্টম অ্যাকশনের সাথে লিঙ্ক করতে পারেন, বাতিল করার অ্যাকশন এবং লেবেল সেখানে পাস করতে পারেন:

SwipeToDismissBox(
    modifier = Modifier.semantics {
        // Represents the swipe to dismiss for accessibility
        customActions = listOf(
            CustomAccessibilityAction(
                label = "Remove article from list",
                action = {
                    removeArticle()
                    true
                }
            )
        )
    },
    state = rememberSwipeToDismissBoxState(),
    backgroundContent = {}
) {
    ArticleListItem()
}

তারপর টকব্যাকের মতো অ্যাক্সেসিবিলিটি পরিষেবা কম্পোনেন্ট হাইলাইট করে এবং ইঙ্গিত দেয় যে এর মেনুতে আরও অ্যাকশন উপলভ্য আছে, যা সোয়াইপ করে ডিসমিস করার অ্যাকশনকে বোঝায়:

টকব্যাক অ্যাকশন মেনুর ভিজ্যুয়ালাইজেশন
ছবি ৪. টকব্যাক অ্যাকশন মেনুর ভিজ্যুয়ালাইজেশন।

কাস্টম অ্যাকশনের আরেকটি ব্যবহার হল, উপলভ্য অ্যাকশন বেশি থাকা আইটেমের দীর্ঘ তালিকা। কারণ, প্রতিটি আইটেমের জন্য আলাদা আলাদাভাবে প্রতিটি অ্যাকশন ইটারেট করা ব্যবহারকারীদের পক্ষে ক্লান্তিকর হতে পারে:

=স্ক্রিনে সুইচ অ্যাক্সেস নেভিগেশনের ভিজ্যুয়ালাইজেশন
ছবি ৫. স্ক্রিনে সুইচ অ্যাক্সেস নেভিগেশনের ভিজ্যুয়ালাইজেশন।

নেভিগেশন অভিজ্ঞতা উন্নত করতে, যা বিশেষ করে সুইচ অ্যাক্সেস বা ভয়েস অ্যাক্সেসের মতো ইন্টার‍্যাকশন-ভিত্তিক সহায়ক প্রযুক্তির জন্য সহায়ক, আপনি কন্টেনারে কাস্টম অ্যাকশন ব্যবহার করতে পারেন যাতে আলাদা আলাদা ট্রাভার্সাল থেকে অ্যাকশন সরিয়ে এনে একটি আলাদা অ্যাকশন মেনুতে রাখা যায়:

ArticleListItemRow(
    modifier = Modifier
        .semantics {
            customActions = listOf(
                CustomAccessibilityAction(
                    label = "Open article",
                    action = {
                        openArticle()
                        true
                    }
                ),
                CustomAccessibilityAction(
                    label = "Add to bookmarks",
                    action = {
                        addToBookmarks()
                        true
                    }
                ),
            )
        }
) {
    Article(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = openArticle,
    )
    BookmarkButton(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = addToBookmarks,
    )
}

এইসব ক্ষেত্রে, clearAndSetSemantics মডিফায়ার ব্যবহার করে মূল চিলড্রেন সিম্যান্টিক্স ম্যানুয়ালি মুছে দিতে ভুলবেন না, কারণ আপনি সেগুলি কাস্টম অ্যাকশনে সরাচ্ছেন।

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

সুইচ অ্যাক্সেস ব্যবহার করে নিবন্ধের তালিকা আইটেম হাইলাইট করা
ছবি ৬. Switch Access-এর মাধ্যমে নিবন্ধের তালিকা আইটেম হাইলাইট করা।
'অ্যাক্সেস পাল্টান' অ্যাকশন মেনুর ভিজ্যুয়ালাইজেশন।
ছবি ৭. 'অ্যাক্সেস পাল্টান' অ্যাকশন মেনুর ভিজ্যুয়ালাইজেশন।

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

কম্পোজিশন আপনার অ্যাপের UI বর্ণনা করে এবং কম্পোজ করার উপযুক্ত আইটেম রান করানোর মাধ্যমে এটি তৈরি করা হয়। কম্পোজিশন হল একটি ট্রি-স্ট্রাকচার যা আপনার UI-এর বিবরণ দেয় এমন কম্পোজ করার উপযুক্ত আইটেম নিয়ে গঠিত।

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

একটি সাধারণ UI হায়ারার্কি ও তার সেম্যান্টিক ট্রি
ছবি ৮. একটি সাধারণ UI হায়ারার্কি ও তার সিম্যান্টিক ট্রি।

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

যেমন, এই কাস্টম ক্যালেন্ডার কম্পোজ করার উপযুক্ত আইটেমটি বিবেচনা করুন:

বেছে নেওয়ার উপযুক্ত দিনের এলিমেন্ট সহ কাস্টম ক্যালেন্ডার কম্পোজ করার উপযুক্ত
ছবি ৯. বেছে নেওয়ার উপযুক্ত দিনের এলিমেন্ট সহ কাস্টম ক্যালেন্ডার কম্পোজ করার উপযুক্ত।

এই উদাহরণে, সম্পূর্ণ ক্যালেন্ডারটি একটি সিঙ্গল লো-লেভেল কম্পোজ করার উপযুক্ত হিসেবে প্রয়োগ করা হয়েছে, এর জন্য Layout কম্পোজ করার উপযুক্ত এবং সরাসরি Canvas-এ ড্রয়িং ব্যবহার করা হয়েছে। আপনি আর কিছু না করলে, অ্যাক্সেসিবিলিটি পরিষেবা কম্পোজ করা যায় এমন কন্টেন্ট এবং ক্যালেন্ডারের মধ্যে ব্যবহারকারীর বেছে নেওয়া বিকল্প সম্পর্কে পর্যাপ্ত তথ্য পাবে না। যেমন, কোনও ব্যবহারকারী যদি ১৭ তারিখের উপর ক্লিক করেন, তাহলে অ্যাক্সেসিবিলিটি ফ্রেমওয়ার্ক শুধুমাত্র সম্পূর্ণ ক্যালেন্ডার কন্ট্রোলের জন্য বর্ণনার তথ্য পায়। এই ক্ষেত্রে, টকব্যাক অ্যাক্সেসিবিলিটি পরিষেবা "ক্যালেন্ডার" অথবা, সামান্য ভালো করে বললে, "এপ্রিল ক্যালেন্ডার" বলে ঘোষণা করবে এবং ব্যবহারকারীকে ভাবতে হবে যে কোন দিনটি বেছে নেওয়া হয়েছে। এই কম্পোজ করার উপযুক্ত আইটেমটিকে আরও অ্যাক্সেসিবল করতে, আপনাকে ম্যানুয়ালি সিম্যান্টিক তথ্য যোগ করতে হবে।

মার্জ করা ও আনমার্জ করা ট্রি

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

এই ধরনের কম্পোজ করার উপযুক্ত আইটেমের একটি উদাহরণ হল Button। আপনি একটি বোতামকে সিঙ্গেল এলিমেন্ট হিসেবে বিবেচনা করতে পারেন, যদিও এতে একাধিক চাইল্ড নোড থাকতে পারে:

Button(onClick = { /*TODO*/ }) {
    Icon(
        imageVector = Icons.Filled.Favorite,
        contentDescription = null
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Like")
}

সিম্যান্টিক্স ট্রি-তে, বোতামের ডিসেন্ডেন্টদের প্রপার্টি মার্জ করা হয়, এবং বোতামটিকে ট্রি-তে একটি নোড হিসেবে দেখানো হয়:

মার্জ করা সিঙ্গেল লিফ সেম্যান্টিক্সের উপস্থাপনা
ছবি ১০. মার্জ করা সিঙ্গেল লিফ সেম্যান্টিক্সের উপস্থাপনা।

কম্পোজেবল ও মডিফায়াররা Modifier.semantics (mergeDescendants = true) {} কল করে তাদের ডিসেন্ডেন্টদের সিমান্টিক প্রপার্টি মার্জ করতে চান বলে ইঙ্গিত করতে পারেন। এই প্রপার্টিকে true হিসেবে সেট করলে বোঝা যায় যে সিমান্টিক প্রপার্টি মার্জ করতে হবে। Button উদাহরণে, Button কম্পোজ করার উপযুক্ত আইটেম ইন্টার্নালি clickable মডিফায়ার ব্যবহার করে যার মধ্যে এই semantics মডিফায়ার অন্তর্ভুক্ত। তাই, বোতামের ডিসেন্ডেন্ট নোড মার্জ করা হয়। আপনার কম্পোজ করার উপযুক্ত আইটেমে কখন মার্জ করার আচরণ পরিবর্তন করা উচিত সেই সম্পর্কে আরও জানতে অ্যাক্সেসিবিলিটি ডকুমেন্টেশন পড়ুন।

Foundation এবং Material Compose লাইব্রেরিতে বেশ কিছু মডিফায়ার ও কম্পোজেবল এই প্রপার্টি সেট করে। যেমন, clickable এবং toggleable মডিফায়ার অটোমেটিক তাদের ডিসেন্ডেন্ট মার্জ করবে। এছাড়াও, ListItem কম্পোজ করার উপযুক্ত আইটেম তার ডিসেন্ডেন্টদের সাথে মার্জ করবে।

গাছটি পরীক্ষা করে দেখুন

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

আপনি printToLog() পদ্ধতি ব্যবহার করে দুটি ট্রিই পরিদর্শন করতে পারবেন। ডিফল্ট হিসেবে এবং আগের উদাহরণে দেখানো মতো, মার্জ করা ট্রি লগ করা হয়। এর পরিবর্তে, আনমার্জ করা ট্রি প্রিন্ট করতে, onRoot() ম্যাচারের useUnmergedTree প্যারামিটারকে true হিসেবে সেট করুন:

composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")

লেআউট ইন্সপেক্টর আপনাকে মার্জ করা ও মার্জ না করা, দুটিই সিম্যান্টিক ট্রি দেখাতে দেয়, এর জন্য ভিউ ফিল্টার থেকে পছন্দমতো একটি বেছে নিন:

লেআউট ইন্সপেক্টর ভিউ বিকল্প, যা মার্জ করা ও মার্জ না করা সেম্যান্টিক্স ট্রি, দুটিই দেখানোর অনুমতি দেয়
ছবি ১১. লেআউট ইন্সপেক্টর ভিউ বিকল্প, যা মার্জ করা ও মার্জ না করা সেম্যান্টিক্স ট্রি, দুটিই দেখানোর অনুমতি দেয়।

আপনার ট্রি-তে থাকা প্রতিটি নোডের জন্য, লেআউট ইন্সপেক্টর প্রপার্টি প্যানেলে মার্জ করা সিম্যান্টিক্স এবং সেই নোডে সেট করা সিম্যান্টিক্স, দুটিই দেখায়:

সিম্যান্টিক প্রপার্টি মার্জ ও সেট করা হয়েছে
ছবি ১২. সেম্যান্টিক প্রপার্টি মার্জ ও সেট করা হয়েছে।

সাধারণত, টেস্টিং ফ্রেমওয়ার্কে ম্যাচ করার জন্য মার্জ করা সেম্যান্টিক ট্রি ব্যবহার করা হয়। সেই জন্য, Button-এর মধ্যে দেখানো টেক্সট মিলিয়ে আপনি এটির সাথে ইন্টার‍্যাক্ট করতে পারবেন:

composeTestRule.onNodeWithText("Like").performClick()

onRoot ম্যাচারের মতো, ম্যাচারের useUnmergedTree প্যারামিটারকে true হিসেবে সেট করে এই আচরণ ওভাররাইড করুন।

ট্রি অ্যাডজাস্ট করা

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