Unable use a TextField at the bottom of the screen in a LazyColumn - android-jetpack-compose

I have this code
setContent {
val items = mutableListOf<Int>().apply {
(1..100).forEach { add(it) }
}
LazyColumn {
items(items) { item ->
TextField("$item", {})
}
}
}
With android:windowSoftInputMode="adjustResize" in my AndroidManifest.xml.
If I click on a TextField at the top of the list, I can enter text fine.
If I click on a TextField near the bottom of the screen, the keyboard appears momentarily, then disappears quickly after, and prevents me from entering text.
How can I enter text when the TextField is at the bottom of the screen? Thanks!

Your keyboard disappears after having appeared for a brief, shiny moment. Here's the reason:
You tap the TextField, it calls it's built-in focus requestor, and requests the focus from the OS by calling appropriate (or inappropriate, who's to say) internal methods, as a result of which, the keyboard pops out (again, built-in mechanism.) and you can type all the more you want... AS LONG AS THE FIELD IS VISIBLE; or in Compose terms, long as the Composable holding the requestor, is in composition.
It should be clear, that once the Composable that owns the focus requestor goes off the screen, it is destroyed (well not every time, there are only certain specific cases when it is destroyed..., but yours is one of them), and when the Composable is destroyed, the focus requestor is destroyed. No focus requestor, no focus owner - the keyboard vanishes.
Now this is really important because you are using a LazyColumn, a lazy Composable, infamous for its merciless slaughtering of the Composables that are no longer visible to the user. HENCE, as long as it is the top (or other "visible") textfield that is in concern, it stays and works as expected. However, the bottom textfield, as the keyboard pops up, goes so much out of the visible bounds, that it gets slaughtered (recycled is the technical term, but what fun is that?), taking away the purpose of the keyboard.
Now, the lazy column, like any criminal, left a clue behind, and didn't notice it, which lead us to catch him red-handed. It is quite interesting, really, to see the column in action.
Now, workarounds include using a column, instead of a lazy column; creating your own lazy column using a custom lazy Composable, and manually handling the heap size for the Composable so the final field doesn't go out of composition.
That's it, you're cured.

Just leaving another answer, since OP didn't verify other purpose of him specifying adjustResize,if whether removing or changing it will be fine, but another solution aside from the mentioned comment about different API version is specifying adjustPan, which worked in my case, either
via AndroidManifest
android:windowSoftInputMode="adjustPan"
or programmatically
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Same code base but with adjustPan

This issue is fixed in the current alpha version 1.4.0-alpha05
https://issuetracker.google.com/issues/179203700

Related

Jetpack Compose - Change default Visible % in ModalBottomSheetLayout

I have a ModalBottomSheetLayout with a list of items in my Compose view, which can be showed by some UI interaction.
By default, when bottomSheetState.show() is called, the visible ratio for the BottomSheet is 50%. However, this is not ideal from a UX perspective as the user will have to physically pull up the bottom sheet to see all the contents in the list.
Extremely frustrating is the fact that bottomSheetState.show() does not take in any parameters, and that the 50% value seems to be hard coded in. According to the declaration in androix.compose.material:
suspend fun show() {
val targetValue =
if (isHalfExpandedEnabled) HalfExpanded
else Expanded
animateTo(targetValue = targetValue)
}
I would like to instead show a custom value, say 75%, when the bottom sheet is showed, but so far I haven't found a way to do so. Is there a workaround to this?
I am not sure if you can make it 75% visible but you can show it expanded
bottomSheetState.animateTo(ModalBottomSheetValue.Expanded)

Visual focus of Skip links

Is it important to place a focus indicator on a skip link destination even if the target is non interactive ? Do you believe that focus indicator should always be present in order to orient users to know where they are on a page?
No, anything that isn't focusable should not have a focus indicator.
Just make sure that when you do skip to a section that the page scrolls to that page correctly so it is at the top (or near to the top) of the page (i.e. if you have a position:fixed header make sure that wherever you scroll to is not obscured by it).
For a "sighted" user the page change and location will be evident, for a "non-sighted" user on a screen reader their screen reader will guide them as long as you have managed focus correctly onto the non-focusable item (so make sure you use tabindex="-1" so you don't break this pattern if doing this programatically rather than with an anchor).
I use "sighted" and "non-sighted" here as obviously some people use a screen reader who do not have a sight impairment but it is easier to use this for illustration purposes.
What this boils down to is "expected behaviour", which is a key part of accessibility.
It is expected that anything with a focus indicator is interactive, adding focus indicators to non-interactive elements is a bad idea for this reason.
Secondly it is expected behaviour that a skip link will scroll the page to the relevant section, heading etc. As long as this happens your users will be fine.
The only reference I can find on W3C on focus visible enhanced says:
Some elements can take focus (such as the target of a skip link), however, it is only when the element is operable by keyboard controls that this criterion applies.

VoiceOver : is 'accessibilityActivationPoint' really useful?

I tried and understood what could be the purpose of the accessibilityActivationPoint but in vain.
When a focused accessible element is activated, that property should indicate VoiceOver the specific area it's going to activate when a user double-taps the element (Apple reference) : for me, it's always the selected element itself.
I understood the selected element is considered as a block by VoiceOver, whatever the other elements inside. Once a double tap occurs to activate this block, VoiceOver calls accessibilityActivate to know what to perform (Apple reference).
1/. I've written many tests by creating a custom view including a switch control. Whatever the value of accessibilityActivationPoint inside (or outside on another switch control), the value of the switch control never changes. Is it a proper use case or am I totally wrong ?
2/. When we gather many elements inside one accessible element, how is VoiceOver able to activate one of them while they aren't accessible by definition ? Pointing one of them thanks to the accessibilityActivationPoint should work ?
Personally, I couldn't make it work and think that I'm really confusing accessibilityActivationPoint and accessibilityActivate.
Any help would be appreciated, thanks in advance.
Yes, you have the right idea with accessibilityActivate and accessibilityActivationPoint. Note that, in order for it to work, the accessibilityActivationPoint needs to be a point within the Control that you are trying to activate in on-screen coordinates (use the convert function!).
I think the short answer is "yes" to answer your second question, but, just to clear up confusion about when Accessibility Activation Point is useful, I'll go into more detail about it.
By default (aka, the default behavior for AcessibilityActivate()), when any view is activated by VoiceOver, VoiceOver will send a "tap gesture" to the center of the view. The position of this "tap gesture" can be changed by updating the accessibilityActivationPoint attribute on a view. Below, I have an example for how this property can be used.
Let's say you have a blank button (in the image below, the button is the gray box) next to some text:
For the purpose of accessibility, you may want to make the entire view that holds the button and text an Accessibility Element (so that VoiceOver users can easily understand that the button is associated with the text "Worldspace Attest"). In the image below, I am using Accessibility Inspector to show that the view holding both of these elements is an Accessibility Element.
Notice in these images that the button is not in the center of the view, but rather, it is to the right. When you activate this view using VoiceOver, the view will not select the button; instead, it will send a "tap" to the center of the view (which is the same as tapping the text, which does not do anything). In order to select the button, you have to set the view's accessibilityActivationPoint to be the on-screen coordinates of the button:
view.accessibilityActivationPoint = self.convert(button.center, to: UIApplication.shared.windows.first)
This should make it so that this button is usable by a VoiceOver user.
I hope this information clears up any confusion about the Accessibility Activation Point property. The example I used above can be found in this repository in the "Active Control Name" demo.

On 'next' or 'previous' virtual keyboard in iOS

iOS5+ shows next/prev buttons above the keyboard:
Because they change the focused element on the page, they can cause issues with my single page app's layout. The 'tab' key on normal keyboards can cause similar issues, but I can slightly change its functionality to only cycle around the inputs I want with a custom keydown event.
Is there any way to do something similar for those buttons?
I'm not really sure what your problem is.
My guess is that when you change focus with "Previous" and "next" button, something goes wrong with your design. And I'm also guessing that you click these when you are in an input (although it's pretty obvious).
When you change from an input to another (With tab key, using "previous/next"-buttons or by mouse), a blur event is triggered.
Now, you don't say what library, if any, you are using. But the fix is similar all around I would say.
if you use jQuery, do something similar to this:
//Selects all input elements, and binds a anonymous function to them. This function
//is run when the blur event is triggered, meaning when we change focus to something else
$("input").on("blur", function () { //Or live, depending on jQuery version
//Your code here, that I assume you already have
});

Keyboard pushes the whole view up in WP7

I have 3 grids:- Header,Body and Footer with Body having textboxes.
I have included the Body inside a scrollviewer so that user can type as much data as he wants in the texboxes of Body.
But when the Keyboard pops up the whole view is pushed at top and I cannot see the Header.
The textboxes have wrapping on and also accepts return.
Can the scrollview move up the text when keyboard pops up?
Thanks and Regards,
Kanaya
Not entirely sure if this will help. How about some XAML?
You could capturing the Focus event of the text box that brings the Keyboard up. In that event you could set the scrollViewer.HorizontalOffset property to some caluclated value to get whatever you want in view.
Edit: HorizontalOffest is only a getter user ScrollToHorizontalOffset instead
You probably can make it scroll up like you want, but it will appear very strange to seasoned users of the WP7 operating system. Expected behavior for text input is exactly what you described, the notion Microsoft (I believe) is trying to hit here is that when you want to type something in, you want to clear everything else out of view, and only focus on the textbox at hand.

Resources