फ़ोकस का व्यवहार बदलें

कभी-कभी एलिमेंट के डिफ़ॉल्ट फ़ोकस व्यवहार को बदलना ज़रूरी हो जाता है किस तरह का है. उदाहरण के लिए, हो सकता है कि आप कॉम्पोनेबल ग्रुप, को रोकना फ़ोकस पर एक खास कंपोज़ेबल पर, साफ़ तौर पर फ़ोकस करने का अनुरोध करें एक पर, कैप्चर या रिलीज़ फ़ोकस या फ़ोकस रीडायरेक्ट का इस्तेमाल, पेज पर आने या बाहर जाने के लिए किया जा सकता है. यह सेक्शन में बताया गया है कि जब डिफ़ॉल्ट सेटिंग आपकी पसंद के मुताबिक न हों, तो फ़ोकस के व्यवहार को कैसे बदलें ज़रूरत.

फ़ोकस ग्रुप की मदद से, एक ही जगह पर आसानी से नेविगेट करें

कभी-कभी, Jetpack Compose, उसके लिए सही अगले आइटम का तुरंत अनुमान नहीं लगा पाता टैब वाला नेविगेशन, खास तौर पर तब, जब कॉम्प्लेक्स पैरंट Composables, जैसे कि टैब और सूचियां काम करती हैं.

आम तौर पर, फ़ोकस सर्च Composables के एलान के क्रम के हिसाब से होता है. कुछ मामलों में ऐसा करना नामुमकिन है, जैसे कि जब Composables में से कोई क्रम के मुताबिक पेज को हॉरिज़ॉन्टल तरीके से स्क्रोल किया जा सकता है, जो पूरी तरह से नहीं दिखता. यह इसमें दिखाया गया है नीचे दिया गया उदाहरण देखें.

ऐसा हो सकता है कि Jetpack Compose, यात्रा की शुरुआत में अगले आइटम पर फ़ोकस करे देखें, जैसा कि नीचे दिखाया गया है. एक-तरफ़ा नेविगेशन:

एक ऐप्लिकेशन का ऐनिमेशन, जिसमें ऊपर का हॉरिज़ॉन्टल नेविगेशन और नीचे दिए गए आइटम की सूची दिख रही है.
पहली इमेज. एक ऐप्लिकेशन का ऐनिमेशन, जिसमें ऊपर का हॉरिज़ॉन्टल नेविगेशन और नीचे दी गई आइटम की सूची दिख रही है

इस उदाहरण में, यह साफ़ तौर पर बताया गया है कि डेवलपर का मकसद चॉकलेट टैब से नीचे दी गई पहली इमेज पर जाएं और फिर वापस पेस्ट्री टैब. इसके बजाय, वे चाहते थे कि टैब पर तब तक फ़ोकस जारी रहे जब तक आखिरी टैब में जाकर, अंदरूनी कॉन्टेंट पर फ़ोकस करें:

एक ऐप्लिकेशन का ऐनिमेशन, जिसमें ऊपर का हॉरिज़ॉन्टल नेविगेशन और नीचे दिए गए आइटम की सूची दिख रही है.
दूसरी इमेज. एक ऐप्लिकेशन का ऐनिमेशन, जिसमें ऊपर का हॉरिज़ॉन्टल नेविगेशन और नीचे दी गई आइटम की सूची दिख रही है

उन स्थितियों में ज़रूरी है कि कंपोज़ेबल के ग्रुप पर फ़ोकस किया जाए और पिछले उदाहरण की तरह टैब पंक्ति में, आपको सभी पंक्तियों को एक साथ माता-पिता में से 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 मॉडिफ़ायर, फ़ोकस मैनेजर को आइटम का क्रम बदलने के लिए कहता है फ़ोकस किए गए हों, ताकि नेविगेट करना आसान हो और यूज़र इंटरफ़ेस (यूआई) के साथ बेहतर तरीके से काम कर सके.

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")
}

कंपोज़ेबल को फ़ोकस न करने लायक बनाना

ऐसी स्थितियां हो सकती हैं जिनमें आपके कुछ एलिमेंट को शामिल नहीं करना चाहिए फ़ोकस में हैं. इन खास अवसरों पर, canFocus property का फ़ायदा लिया जा सकता है ताकि Composable पर फ़ोकस न किया जा सके.

var checked by remember { mutableStateOf(false) }

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

FocusRequester की मदद से, कीबोर्ड फ़ोकस का अनुरोध करें

कुछ मामलों में, हो सकता है कि आप साफ़ तौर पर उपयोगकर्ता इंटरैक्शन. उदाहरण के लिए, किसी उपयोगकर्ता से यह पूछा जा सकता है कि उसे रीस्टार्ट करना है या नहीं फ़ॉर्म भरने और अगर वे "हां" दबाएं, तो आप पहले फ़ील्ड पर फिर से फ़ोकस करना चाहते हों फ़ॉर्म भरें.

सबसे पहले, किसी FocusRequester ऑब्जेक्ट को जिसमें आपको कीबोर्ड के फ़ोकस को ले जाना हो. इस कोड में तो FocusRequester ऑब्जेक्ट, TextField Modifier.focusRequester नाम का मॉडिफ़ायर:

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() के तौर पर इनमें से कोई एक काम करेगा मॉडिफ़ायर फ़ोकस करने लायक उस पहले एलिमेंट को कहते हैं जो फ़ोकस करने लायक पहले एलिमेंट के बाद दिखता है focusable() या focusTarget() मॉडिफ़ायर.

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

एंट्री या एग्ज़िट पर फ़ोकस को रीडायरेक्ट करना

कभी-कभी, आपको एक बहुत ही खास तरह का नेविगेशन देना पड़ता है, जैसे कि नीचे ऐनिमेशन में दिखाया गया है:

एक स्क्रीन का ऐनिमेशन, जिसमें दो कॉलम अगल-बगल रखे हुए हैं और फ़ोकस को एक से दूसरे कॉलम पर ऐनिमेट किया गया है.
तीसरी इमेज. एक स्क्रीन का ऐनिमेशन, जिसमें दो कॉलम अगल-बगल रखे हुए हैं और फ़ोकस को एक से दूसरे कॉलम पर ऐनिमेट किया गया है

इसे बनाने के तरीके के बारे में विस्तार से बात करने से पहले, यह जानना ज़रूरी है कि डिफ़ॉल्ट फ़ोकस सर्च का व्यवहार. खोज पर एक बार फ़ोकस करके, बिना किसी बदलाव के डी-पैड पर DOWN दबाकर, Clickable 3 आइटम तक पहुंचता है या इसके बराबर ऐरो बटन) का इस्तेमाल करके, फ़ोकस को Column के नीचे दिखाई गई किसी भी चीज़ पर ले जाया जाएगा, ग्रुप से बाहर निकलें और दाईं ओर वाले हिस्से को अनदेखा करें. अगर कोई फ़ोकस करने लायक आइटम उपलब्ध हैं, फ़ोकस कहीं भी नहीं जाता, लेकिन फ़ोकस में रहता है Clickable 3.

इस व्यवहार में बदलाव करने और सही नेविगेशन देने के लिए, focusProperties मॉडिफ़ायर, जिससे आपको यह मैनेज करने में मदद मिलती है कि फ़ोकस होने पर क्या होता है खोज Composable में प्रवेश करता है या बाहर निकालता है:

val otherComposable = remember { FocusRequester() }

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

इसके अंदर, किसी खास Composable पर फ़ोकस किया जा सकता है या पदानुक्रम के किसी खास हिस्से से बाहर निकल जाता है— उदाहरण के लिए, जब आपके यूज़र इंटरफ़ेस (यूआई) में दो कॉलम में है और आप यह सुनिश्चित करना चाहते हैं कि जब भी पहला वाला संसाधित हो, फ़ोकस दूसरे पर स्विच होता है:

एक स्क्रीन का ऐनिमेशन, जिसमें दो कॉलम अगल-बगल रखे हुए हैं और फ़ोकस को एक से दूसरे कॉलम पर ऐनिमेट किया गया है.
चौथी इमेज. एक स्क्रीन का ऐनिमेशन, जिसमें दो कॉलम अगल-बगल रखे हुए हैं और फ़ोकस को एक से दूसरे कॉलम पर ऐनिमेट किया गया है

इस GIF में, जब फ़ोकस Column 1 में Clickable 3 Composable पर पहुंच जाता है, अगले आइटम पर फ़ोकस किया जा रहा है, जो दूसरे Column में Clickable 4 है. यह व्यवहार focusDirection को enter और exit के साथ जोड़कर हासिल किया जा सकता है वैल्यू को focusProperties मॉडिफ़ायर में बदला जा सकता है. उन दोनों को एक लैम्डा की ज़रूरत होती है, जो एक पैरामीटर के रूप में उस दिशा से जहां से फ़ोकस आ रहा है और FocusRequester. यह लैम्डा तीन तरह से काम कर सकता है: लौटने वाले FocusRequester.Cancel फ़ोकस को जारी रखने से रोकता है, जबकि FocusRequester.Default अपने व्यवहार में बदलाव नहीं करता है. इसके बजाय, FocusRequester को किसी दूसरे Composable से अटैच करने पर, फ़ोकस सीधे उस पर जाता है खास Composable.

फ़ोकस को बेहतर बनाने की दिशा बदलें

फ़ोकस को अगले आइटम पर या किसी सटीक दिशा पर ले जाने के लिए, onPreviewKey मॉडिफ़ायर का इस्तेमाल करें और LocalFocusManager को moveFocus मॉडिफ़ायर की मदद से फ़ोकस को बेहतर बनाएं.

इस उदाहरण में, फ़ोकस करने के डिफ़ॉल्ट तरीके के बारे में बताया गया है: जब कोई 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() फ़ंक्शन फ़ोकस को आगे बढ़ाकर आइटम में बताया गया है या फ़ंक्शन पैरामीटर में बताई गई दिशा तक.