Настройка
Я занимаюсь переносом существующего приложения с XML на Compose. Наше приложение изначально было создано талантливыми разработчиками Node, работавшими над своим первым приложением для Android, поэтому мы пользуемся этой возможностью, чтобы применить знания некоторых опытных разработчиков, впервые попавших в команду, и извлеченные уроки. Для меня очень важно, чтобы мы не вносили значительных долгов и не использовали Hacks
Задача
У нас есть библиотека стилевых компонентов, созданная другой командой, которую нам настоятельно рекомендуется использовать в нашем приложении. Как правило, преимущество отсутствия необходимости беспокоиться о стиле большую часть времени перевешивает любые нарекания. Прямо сейчас я работаю над довольно простым нижним листом, на котором есть один текстовый ввод и две кнопки. Легко!
Иерархия компонентов выглядит примерно так:
Главный экран -> ModalBottomSheetLayout (из Material Compose) -> MainContent (основной контент, поверх которого появляется нижний лист) -> Содержимое нижнего листа -> CustomTextInputComponent (из внешней библиотеки) → InternalTextField (внутреннее для библиотеки) -> BasicTextField (из Compose Core) При такой настройке вы должны передать FocusRequester и обратный вызов onFocusChanged в CustomTextInputComponent в качестве аргументов, которые затем получают передается в InternalTextField в качестве аргументов и в конечном итоге устанавливается в модификатор BasicTextView.
modifier = Модификатор .fillMaxWidth() .align(Выравнивание.CenterStart) .then(if (focusRequester != null) Modifier.focusRequester(focusRequester) else Модификатор) .focusable(истина) .onFocusChanged { onFocusChanged (оно) } Мой BottomSheetContent выглядит примерно так:
@Composable весело BottomSheetContent( onContinue: (String) -> Единица измерения, onCancel: () -> Единица измерения ) { val textField по памяти {mutableStateOf(TextFieldValue()) } val focusRequester = запомнить { FocusRequester() } val showDebugToast, запомнив {mutableStateOf(false) } Столбец { CustomTextInputComposable( модификатор = Модификатор... текст = текстовое поле, onTextChanged = { textFieldValue -> textField = значение textField }, onFocusChanged = { hasFocus -> showDebugToast = hasFocus }, focusRequester = фокусреквестер ) CustomButton( текст = «Отмена» ) { onCancel() } CustomButton( текст = «Продолжить» ) { onContinue(textField.text) } если (showDebugToast) { CustomToast(message = "HAS FOCUS!! ※\(^o^)/※", type = ToastType.SUCCESS) } } } Где я застрял
Я пытаюсь установить фокус на ввод текста, когда отображается нижний лист. Один текстовый ввод на нижнем листе — единственный текстовый ввод на экране. Я знаю, что onFocusChanged вызывается обратно из правильного места, потому что когда я вручную касаюсь текстового поля, обратный вызов срабатывает полностью обратно к BottomSheetContent и всплывающему сообщению. отображается.
Я не знаю, как подать сигнал BasicTextField, чтобы программно запросить фокус. Я перепробовал все, что пришло в мой (жареный) мозг, и все идеи, возникшие за несколько часов поиска в Google, в моем BottomSheetContent, а именно:
[*]Вызов focusRequester.requestFocus() из отовсюду. Я вызвал его из прослушивателя кликов одной из моих кнопок. Я испробовал многие из обычно предписываемых подходов с побочными эффектами, с задержками и без них, вверху, внизу и везде между ними:
LaunchedEffect(focusRequester) { focusRequester.requestFocus() } LocalView.current.viewTreeObserver.addOnWindowFocusChangeListener { если (оно) focusRequester.requestFocus() } val windowInfo = LocalWindowInfo.current LaunchedEffect (windowInfo) { snapshotFlow { windowInfo.isWindowFocused }.collect { isWindowFocused -> если (isWindowFocused) { focusRequester.requestFocus() } } } [*]Я попытался переместить focusRequester на MainScreen, чтобы запросить фокус после отображения нижнего листа, и везде, где я только мог подумать. [*]Я даже скопировал два составных объекта из библиотеки и изменил их, чтобы посмотреть, что смогу найти. Я попытался установить focusRequester непосредственно на все, что его принимает, в обоих составных компонентах библиотеки, включая все внутри decorationBox в BasicTextField. Мне не удалось найти составной объект, который получил фокус, когда я коснулся ввода вручную. Заключительные мысли
Я подозреваю, что реальная проблема здесь двоякая: focusRequester.requestFocus() ведет себя не так, как я ожидаю, и я не могу определить тот составной объект, который получает фокус, когда я касаюсь ввод текста. Единственное, что я могу проверить, это то, что когда я захватываю фокус вручную одним касанием, вызов focusRequester.requestFocus() убирает фокус. Я трижды проверил, что не устанавливаю его в качестве запросчика фокуса для каких-либо других составных объектов, и проследил его через библиотеку, чтобы убедиться, что они также устанавливают его только для одного составного объекта.
Я совершенно озадачен, если есть есть способ сделать это, и мне интересно, является ли CustomTextInputComposable безнадежным делом. По правде говоря, ничто не сделало бы меня более счастливым, чем обнаружить, что есть что-то, чего я просто в корне не понимаю во всем этом. Надеюсь, вы все меня исправите.