ফোকাস আচরণ পরিবর্তন করুন

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

ফোকাস গ্রুপের সাথে সুসংগত নেভিগেশন প্রদান করুন

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

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

জেটপ্যাক কম্পোজ এক-দিকনির্দেশক নেভিগেশনের জন্য আপনি যে পথে আশা করেন তাতে চালিয়ে যাওয়ার পরিবর্তে, নীচে দেখানো হিসাবে, পর্দার শুরুর সবচেয়ে কাছাকাছি পরবর্তী আইটেমটিতে ফোকাস করার সিদ্ধান্ত নিতে পারে:

একটি অ্যাপের অ্যানিমেশন একটি শীর্ষ অনুভূমিক নেভিগেশন এবং নীচের আইটেমগুলির একটি তালিকা দেখাচ্ছে৷
চিত্র 1 । একটি অ্যাপের অ্যানিমেশন একটি শীর্ষ অনুভূমিক নেভিগেশন এবং নীচের আইটেমগুলির একটি তালিকা দেখাচ্ছে৷

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

একটি অ্যাপের অ্যানিমেশন একটি শীর্ষ অনুভূমিক নেভিগেশন এবং নীচের আইটেমগুলির একটি তালিকা দেখাচ্ছে৷
চিত্র ২ । একটি অ্যাপের অ্যানিমেশন একটি শীর্ষ অনুভূমিক নেভিগেশন এবং নীচের আইটেমগুলির একটি তালিকা দেখাচ্ছে৷

এমন পরিস্থিতিতে যেখানে এটা গুরুত্বপূর্ণ যে কম্পোজেবলের একটি গোষ্ঠী পর্যায়ক্রমে ফোকাস লাভ করে, যেমন আগের উদাহরণ থেকে ট্যাব সারিতে, আপনাকে Composable এমন একটি প্যারেন্টে মোড়ানো দরকার যার focusGroup() সংশোধক রয়েছে:

LazyVerticalGrid(columns = GridCells.Fixed(4)) {
    item(span = { GridItemSpan(maxLineSpan) }) {
        Row(modifier = Modifier.focusGroup()) {
            FilterChipA()
            FilterChipB()
            FilterChipC()
        }
    }
    items(chocolates) {
        SweetsCard(sweets = it)
    }
}

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

FocusGroup একটি সম্পূর্ণ গোষ্ঠীকে ফোকাসের পরিপ্রেক্ষিতে একটি একক সত্তার মতো দেখায়, কিন্তু গোষ্ঠীটি নিজেই ফোকাস পাবে না— পরিবর্তে, সবচেয়ে কাছের শিশুটি ফোকাস পাবে। এইভাবে, নেভিগেশন গ্রুপ ছেড়ে যাওয়ার আগে সম্পূর্ণরূপে দৃশ্যমান নয় এমন আইটেমে যেতে জানে।

এই ক্ষেত্রে, FilterChip এর তিনটি দৃষ্টান্ত SweetsCard আইটেমগুলির আগে ফোকাস করা হবে, এমনকি যখন SweetsCards সম্পূর্ণরূপে ব্যবহারকারীর কাছে দৃশ্যমান হয় এবং কিছু FilterChip লুকানো থাকতে পারে। এটি ঘটে কারণ focusGroup মডিফায়ার ফোকাস ম্যানেজারকে আইটেমগুলিকে ফোকাস করার ক্রম সামঞ্জস্য করতে বলে যাতে UI এর সাথে নেভিগেশন সহজ এবং আরও সুসংগত হয়।

focusGroup মডিফায়ার ছাড়া, যদি FilterChipC দৃশ্যমান না হয়, ফোকাস নেভিগেশন এটিকে শেষ পর্যন্ত তুলে নেবে। যাইহোক, এই ধরনের একটি সংশোধক যোগ করলে এটি শুধুমাত্র আবিষ্কারযোগ্য নয়, এটি FilterChipB ঠিক পরে ফোকাসও অর্জন করবে, যেমন ব্যবহারকারীরা আশা করবেন।

একটি কম্পোজেবল ফোকাসযোগ্য করা

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

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

একটি কম্পোজেবল unfocusable করা

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

var checked by remember { mutableStateOf(false) }

Switch(
    checked = checked,
    onCheckedChange = { checked = it },
    // Prevent component from being focused
    modifier = Modifier
        .focusProperties { canFocus = false }
)

FocusRequester দিয়ে কীবোর্ড ফোকাসের অনুরোধ করুন

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

প্রথম কাজটি হল একটি FocusRequester অবজেক্টকে কম্পোজেবলের সাথে যুক্ত করা যা আপনি কীবোর্ড ফোকাসকে সরাতে চান। নিম্নলিখিত কোড স্নিপেটে, Modifier.focusRequester নামক একটি সংশোধক সেট করে একটি FocusRequester অবজেক্ট একটি TextField এর সাথে যুক্ত করা হয়েছে:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

আপনি প্রকৃত ফোকাস অনুরোধ পাঠাতে FocusRequester এর requestFocus পদ্ধতিতে কল করতে পারেন। আপনি একটি Composable প্রেক্ষাপটের বাইরে এই পদ্ধতিটি চালু করতে হবে (অন্যথায়, এটি প্রতিটি পুনর্গঠনে পুনরায় কার্যকর করা হয়)। নীচের স্নিপেটটি দেখায় যে বোতামটি ক্লিক করার সময় কীবোর্ড ফোকাস সরানোর জন্য কীভাবে সিস্টেমকে অনুরোধ করতে হয়:

val focusRequester = remember { FocusRequester() }
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.focusRequester(focusRequester)
)

Button(onClick = { focusRequester.requestFocus() }) {
    Text("Request focus on TextField")
}

ক্যাপচার এবং ফোকাস ছেড়ে

আপনার অ্যাপের কাজটি সম্পাদন করার জন্য আপনার ব্যবহারকারীদের সঠিক ডেটা প্রদানের জন্য গাইড করার জন্য আপনি ফোকাস ব্যবহার করতে পারেন—উদাহরণস্বরূপ, একটি বৈধ ইমেল ঠিকানা বা ফোন নম্বর পাওয়া। যদিও ত্রুটির অবস্থাগুলি আপনার ব্যবহারকারীদের কী ঘটছে সে সম্পর্কে অবহিত করে, তবে এটি ঠিক না হওয়া পর্যন্ত ফোকাস থাকার জন্য আপনার ভুল তথ্য সহ ফিল্ডের প্রয়োজন হতে পারে৷

ফোকাস ক্যাপচার করার জন্য, আপনি captureFocus() পদ্ধতিটি চালু করতে পারেন এবং এর পরিবর্তে freeFocus() পদ্ধতির সাথে এটিকে ছেড়ে দিতে পারেন, যেমন নিম্নলিখিত উদাহরণে:

val textField = FocusRequester()

TextField(
    value = text,
    onValueChange = {
        text = it

        if (it.length > 3) {
            textField.captureFocus()
        } else {
            textField.freeFocus()
        }
    },
    modifier = Modifier.focusRequester(textField)
)

ফোকাস মডিফায়ারের অগ্রাধিকার

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

ধারণাটি আরও স্পষ্ট করতে, নিম্নলিখিত কোডটি দেখুন:

Modifier
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

এই ক্ষেত্রে, item2 সঠিক ফোকাস হিসাবে নির্দেশ করে focusProperties ব্যবহার করা হবে না, কারণ এটি পূর্ববর্তীটিতে রয়েছে; সুতরাং, item1 ব্যবহার করা হবে।

এই পদ্ধতির ব্যবহার করে, একজন অভিভাবকও FocusRequester.Default ব্যবহার করে আচরণকে ডিফল্টে পুনরায় সেট করতে পারেন:

Modifier
    .focusProperties { right = Default }
    .focusProperties { right = item1 }
    .focusProperties { right = item2 }
    .focusable()

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

@Composable
fun FancyButton(modifier: Modifier = Modifier) {
    Row(modifier.focusProperties { canFocus = false }) {
        Text("Click me")
        Button(onClick = { }) { Text("OK") }
    }
}

একজন ব্যবহারকারী canFocus true সেট করে এই বোতামটিকে আবার ফোকাসযোগ্য করে তুলতে পারেন:

FancyButton(Modifier.focusProperties { canFocus = true })

প্রতিটি Modifier মতো, ফোকাস-সম্পর্কিতরা আপনি যে ক্রম ঘোষণা করেন তার উপর ভিত্তি করে ভিন্নভাবে আচরণ করে। উদাহরণস্বরূপ, নিচের মত কোডটি Box ফোকাসযোগ্য করে তোলে, কিন্তু FocusRequester এই ফোকাসযোগ্য এর সাথে যুক্ত নয় যেহেতু এটি ফোকাস করার পরে ঘোষণা করা হয়।

Box(
    Modifier
        .focusable()
        .focusRequester(Default)
        .onFocusChanged {}
)

এটা মনে রাখা গুরুত্বপূর্ণ যে একটি focusRequester প্রথম ফোকাসযোগ্য এর সাথে ক্রমানুসারে যুক্ত থাকে, তাই এই focusRequester প্রথম ফোকাসযোগ্য সন্তানের দিকে নির্দেশ করে। কোনটি উপলব্ধ না হলে, এটি কিছু নির্দেশ করবে না। যাইহোক, যেহেতু Box ফোকাসযোগ্য ( focusable() সংশোধককে ধন্যবাদ), আপনি দ্বিমুখী নেভিগেশন ব্যবহার করে এটিতে নেভিগেট করতে পারেন।

অন্য একটি উদাহরণ হিসেবে, নিচের যেকোনো একটি কাজ করবে, কারণ onFocusChanged() modifier প্রথম ফোকাসযোগ্য উপাদানকে বোঝায় যা focusable() বা focusTarget() মডিফায়ারের পরে প্রদর্শিত হয়।

Box(
    Modifier
        .onFocusChanged {}
        .focusRequester(Default)
        .focusable()
)
Box(
    Modifier
        .focusRequester(Default)
        .onFocusChanged {}
        .focusable()
)

প্রবেশ বা প্রস্থানের উপর ফোকাস পুনর্নির্দেশ করুন

কখনও কখনও, আপনাকে একটি খুব নির্দিষ্ট ধরণের নেভিগেশন প্রদান করতে হবে, যেমন নীচের অ্যানিমেশনে দেখানো হয়েছে:

একটি স্ক্রীনের অ্যানিমেশন যা দুটি কলাম পাশাপাশি রাখা বোতাম এবং একটি কলাম থেকে অন্য কলামে অ্যানিমেটিং ফোকাস দেখায়।
চিত্র 3 । একটি স্ক্রিনের অ্যানিমেশন দুটি কলামের বোতাম পাশাপাশি রাখা এবং এক কলাম থেকে অন্য কলামে অ্যানিমেটিং ফোকাস দেখায়

এটি কীভাবে তৈরি করা যায় সে সম্পর্কে আমরা ডুব দেওয়ার আগে, ফোকাস অনুসন্ধানের ডিফল্ট আচরণ বোঝা গুরুত্বপূর্ণ। কোনো পরিবর্তন ছাড়াই, একবার ফোকাস অনুসন্ধান Clickable 3 আইটেমে পৌঁছে গেলে, ডি-প্যাডের (বা সমতুল্য তীর কী) DOWN চাপলে ফোকাসটি Column নীচে প্রদর্শিত যাই হোক না কেন, গ্রুপ থেকে প্রস্থান করা এবং ডানদিকে থাকা একটি উপেক্ষা করা হবে। . যদি কোন ফোকাসযোগ্য আইটেম উপলব্ধ না থাকে, তাহলে ফোকাসটি কোথাও সরে না, কিন্তু Clickable 3 তে থাকে।

এই আচরণটি পরিবর্তন করতে এবং অভিপ্রেত নেভিগেশন প্রদান করতে, আপনি focusProperties মডিফায়ার ব্যবহার করতে পারেন, যা ফোকাস অনুসন্ধান যখন Composable প্রবেশ করে বা প্রস্থান করে তখন কী ঘটবে তা পরিচালনা করতে সহায়তা করে:

val otherComposable = remember { FocusRequester() }

Modifier.focusProperties {
    exit = { focusDirection ->
        when (focusDirection) {
            Right -> Cancel
            Down -> otherComposable
            else -> Default
        }
    }
}

যখনই এটি অনুক্রমের একটি নির্দিষ্ট অংশে প্রবেশ করে বা প্রস্থান করে তখনই একটি নির্দিষ্ট Composable দিকে ফোকাসকে নির্দেশিত করা সম্ভব—উদাহরণস্বরূপ, যখন আপনার UI তে দুটি কলাম থাকে এবং আপনি নিশ্চিত করতে চান যে যখনই প্রথমটি প্রক্রিয়া করা হবে, তখন ফোকাস স্যুইচ করে দ্বিতীয়:

একটি স্ক্রীনের অ্যানিমেশন যা দুটি কলাম পাশাপাশি রাখা বোতাম এবং একটি কলাম থেকে অন্য কলামে অ্যানিমেটিং ফোকাস দেখায়।
চিত্র 4 । একটি স্ক্রিনের অ্যানিমেশন দুটি কলামের বোতাম পাশাপাশি রাখা এবং এক কলাম থেকে অন্য কলামে অ্যানিমেটিং ফোকাস দেখায়

এই জিআইএফ-এ, একবার ফোকাস Column 1-এ Clickable 3 Composable পৌঁছে, পরবর্তী আইটেমটি হল অন্য Column Clickable 4 । এই আচরণটি focusProperties মডিফায়ারের ভিতরে enter এবং exit মানগুলির সাথে focusDirection একত্রিত করে অর্জন করা যেতে পারে। তাদের উভয়েরই একটি ল্যাম্বডা প্রয়োজন যা একটি প্যারামিটার হিসাবে যে দিক থেকে ফোকাস আসছে এবং একটি FocusRequester প্রদান করে। এই ল্যাম্বডা তিনটি ভিন্ন উপায়ে আচরণ করতে পারে: FocusRequester.Cancel ফেরত দিলে ফোকাসটি চালিয়ে যাওয়া বন্ধ হয়, যখন FocusRequester.Default এর আচরণ পরিবর্তন করে না। পরিবর্তে অন্য একটি Composable সাথে সংযুক্ত FocusRequester প্রদান করলে ফোকাস সেই নির্দিষ্ট Composable চলে যায়।

ফোকাস অগ্রসর দিক পরিবর্তন করুন

ফোকাসকে পরবর্তী আইটেমের দিকে বা একটি সুনির্দিষ্ট দিকনির্দেশের দিকে অগ্রসর করতে, আপনি onPreviewKey মডিফায়ারের সুবিধা নিতে পারেন এবং moveFocus মডিফায়ারের সাহায্যে ফোকাসকে অগ্রসর করতে LocalFocusManager বোঝাতে পারেন।

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

val focusManager = LocalFocusManager.current
var text by remember { mutableStateOf("") }

TextField(
    value = text,
    onValueChange = { text = it },
    modifier = Modifier.onPreviewKeyEvent {
        when {
            KeyEventType.KeyUp == it.type && Key.Tab == it.key -> {
                focusManager.moveFocus(FocusDirection.Next)
                true
            }

            else -> false
        }
    }
)

এই নমুনায়, focusManager.moveFocus() ফাংশনটি নির্দিষ্ট আইটেমের দিকে বা ফাংশন প্যারামিটারে উহ্য নির্দেশে ফোকাসকে অগ্রসর করে।

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %}