گاهی اوقات لازم است رفتار فوکوس پیش فرض عناصر روی صفحه نمایش خود را نادیده بگیرید. به عنوان مثال، ممکن است بخواهید موارد ترکیبسازی را گروهبندی کنید ، از تمرکز بر روی یک قابل ترکیب خاص جلوگیری کنید ، صراحتاً فوکوس روی یکی را درخواست کنید ، فوکوس را بگیرید یا رها کنید، یا فوکوس را به ورود یا خروج تغییر جهت دهید . این بخش نحوه تغییر رفتار فوکوس را در زمانی که پیشفرضها آن چیزی که نیاز دارید نیست، توضیح میدهد.
ناوبری منسجمی را با گروه های کانونی ارائه دهید
گاهی اوقات، Jetpack Compose فوراً مورد بعدی صحیح را برای پیمایش برگهها حدس نمیزند، بهویژه زمانی که Composables
والد پیچیده مانند برگهها و فهرستها وارد بازی میشوند.
در حالی که جستجوی فوکوس معمولاً از ترتیب اعلان Composables
پیروی می کند، این در برخی موارد غیرممکن است، مانند زمانی که یکی از Composables
در سلسله مراتب یک اسکرول افقی است که به طور کامل قابل مشاهده نیست. این در مثال زیر نشان داده شده است.
Jetpack Compose ممکن است تصمیم بگیرد که به جای ادامه مسیری که برای پیمایش یک جهته انتظار دارید، مورد بعدی را که نزدیکترین نقطه به شروع صفحه نمایش نشان داده شده است، متمرکز کند.
در این مثال، واضح است که توسعه دهندگان قصد نداشتند که فوکوس از تب Chocolates به اولین تصویر زیر و سپس به تب Pastries برگردد. در عوض، آنها میخواستند که تمرکز روی برگهها تا آخرین برگه ادامه داشته باشد و سپس روی محتوای درونی تمرکز کند:
در شرایطی که مهم است که گروهی از composable ها به صورت متوالی فوکوس کنند، مانند ردیف Tab از مثال قبلی، باید 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
به آن متصل است. اگر می خواهید به طور خاص رفتار قابل تمرکز را به یک composable اضافه کنید، از اصلاح کننده 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
با تنظیم یک اصلاح کننده به نام Modifier.focusRequester
با یک TextField
مرتبط می شود:
val focusRequester = remember { FocusRequester() } var text by remember { mutableStateOf("") } TextField( value = text, onValueChange = { text = it }, modifier = Modifier.focusRequester(focusRequester) )
برای ارسال درخواستهای فوکوس واقعی میتوانید با متد requestFocus
FocusRequester تماس بگیرید. شما باید این روش را خارج از یک زمینه 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()
در این حالت، focusProperties
که item2
را به عنوان فوکوس مناسب نشان می دهد، استفاده نمی شود، همانطور که در مورد قبلی موجود است. بنابراین، item1
مورد استفاده خواهد بود.
با استفاده از این رویکرد، والدین همچنین می توانند با استفاده از FocusRequester.Default
، رفتار را به حالت پیش فرض بازنشانی کنند:
Modifier .focusProperties { right = Default } .focusProperties { right = item1 } .focusProperties { right = item2 } .focusable()
لازم نیست والد بخشی از یک زنجیره اصلاح کننده باشد. یک Composable والد می تواند یک ویژگی تمرکز از یک فرزند composable را بازنویسی کند. به عنوان مثال، این 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() ) |
تغییر جهت تمرکز هنگام ورود یا خروج
گاهی اوقات، شما باید نوع بسیار خاصی از ناوبری را ارائه دهید، مانند آنچه در انیمیشن زیر نشان داده شده است:
قبل از اینکه به نحوه ایجاد آن بپردازیم، مهم است که رفتار پیشفرض جستجوی فوکوس را درک کنیم. بدون هیچ تغییری، هنگامی که جستجوی فوکوس به آیتم Clickable 3
رسید، با فشار دادن DOWN
روی D-Pad (یا کلید پیکان معادل) تمرکز را به هر چیزی که در زیر Column
نمایش داده می شود منتقل می کند، از گروه خارج می شود و مورد سمت راست نادیده می گیرد. . اگر آیتم های قابل فوکوس در دسترس نباشد، فوکوس به جایی منتقل نمی شود، اما روی Clickable 3
باقی می ماند.
برای تغییر این رفتار و ارائه پیمایش مورد نظر، میتوانید از اصلاحکننده focusProperties
استفاده کنید، که به شما کمک میکند هنگام ورود یا خروج جستجوی فوکوس Composable
مدیریت کنید که چه اتفاقی میافتد:
val otherComposable = remember { FocusRequester() } Modifier.focusProperties { exit = { focusDirection -> when (focusDirection) { Right -> Cancel Down -> otherComposable else -> Default } } }
ممکن است هر زمان که یک Composable به بخش خاصی از سلسله مراتب وارد شد یا از آن خارج شد، فوکوس را به سمت یک Composable
خاص هدایت کرد - به عنوان مثال، زمانی که UI شما دو ستون دارد و می خواهید مطمئن شوید که هر زمان که اولین مورد پردازش شد، فوکوس به دوم:
در این گیف، هنگامی که فوکوس Clickable 3 Composable
در Column
1 رسید، مورد بعدی که تمرکز می شود، Clickable 4
در Column
دیگر است. این رفتار را می توان با ترکیب focusDirection
با مقادیر enter
و exit
درون اصلاح کننده focusProperties
بدست آورد. هر دوی آنها به یک لامبدا نیاز دارند که به عنوان پارامتر جهتی را که فوکوس از آن میآید گرفته و یک FocusRequester
برمیگرداند. این لامبدا می تواند به سه روش مختلف رفتار کند: بازگشت FocusRequester.Cancel
از ادامه فوکوس جلوگیری می کند، در حالی که FocusRequester.Default
رفتار آن را تغییر نمی دهد. در عوض ارائه FocusRequester
متصل به Composable
دیگر باعث میشود تمرکز به آن Composable
خاص بپرد.
جهت پیشبرد تمرکز را تغییر دهید
برای پیشبرد فوکوس به مورد بعدی یا به سمت یک جهت دقیق، میتوانید از اصلاحکننده onPreviewKey
استفاده کنید و LocalFocusManager
برای پیشبرد فوکوس با Modifier 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()
فوکوس را به آیتم مشخص شده یا جهتی که در پارامتر تابع ذکر شده است، پیش می برد.
برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- به تمرکز واکنش نشان دهید
- تمرکز در نوشتن
- ترتیب پیمایش فوکوس را تغییر دهید