Both ListView (or AbsListView to be precise) and RecyclerView call setFocusableInTouchMode(true)
in their constructors.
In my understanding (based partially off this ancient post by Romain Guy) and (limited) experience in building keyboard accessible apps, it's very rare that an app will need to enable this property.
In the post, he explains:
This special mode was created for widgets that receive text input, like EditText or, when filtering is enabled, ListView.
I understand the EditText, which can gain focus when the user touches it (focusable in touch mode) so that key inputs can be directed to the correct View (this is easy to comprehend if you consider the case with multiple EditTexts on a single screen).
I don't follow what is meant by "filtering" (and consequently why that needs this property enabled), nor why it's set by default in the constructor.
The issue I faced (to prompt this question) was when I was trying to enable keyboard navigation in a ViewPager. This is what I ended up with:
Each of the pages is a RecyclerView. Each item View in the RecyclerView is focusable, and has correct focus and press states as seen in the gif.
Ideally (and my outcome is not ideal), if the user is focused on position X, and navigates right, then they should be in the corresponding row of the next column:
[ ][ ]|[ ][ ] [ ][ ]|[ ][ ]
[ ][X]|[ ][ ] -> [ ][ ]|[X][ ]
[ ][ ]|[ ][ ] [ ][ ]|[ ][ ]
By default, because the RecyclerView is focusable in touch mode (and thus focusable by definition), the entire RecyclerView on the right gains focus, but the user has no idea what happened. They have to press down and the RecyclerView will then give focus to any available children:
[ ][ ]|[ ][ ] [ ][ ]|[ ][ ] [ ][ ]|[X][ ]
[ ][X]|[ ][ ] -> [ ][ ]|[ ][ ] v [ ][ ]|[ ][ ]
[ ][ ]|[ ][ ] [ ][ ]|[ ][ ] [ ][ ]|[ ][ ]
where it ends up in the first view that is laid out.
So you can either set the android:descendantFocusability
property to afterDescendants
on the RecyclerViews or call setFocusable(false)
on the RecyclerView (can't do it in XML as it's set before the constructor call to setFocusableInTouchMode(true)
), meaning it'll go directly to the first view that is laid out:
[ ][ ]|[ ][ ] [ ][ ]|[X][ ]
[ ][X]|[ ][ ] -> [ ][ ]|[ ][ ]
[ ][ ]|[ ][ ] [ ][ ]|[ ][ ]
(If pressing left, going from the right page to the left, then it'll go to the last view that is laid out.)
I opted for the descendantFocusability
property and although this works well enough (but not ideally as diagrammed), I don't understand why are AbsListView and RecyclerView focusable (/focusable in touch mode).