Assigned
Status Update
Comments
al...@android.com <al...@android.com>
xc...@gmail.com <xc...@gmail.com> #2
same problems with 23.1.1 and LinearLayoutManager
yb...@google.com <yb...@google.com> #3
Hmm, this should actually work even w/o calling anything from RecyclerView.
I'll investigate, thanks.
I'll investigate, thanks.
yb...@google.com <yb...@google.com> #4
Actually this only happens if the root view is focusable. If not, it correctly selects the right view.
But still, this is obviously buggy. It is OK for GLM to rely on LLM to handle basic focus search failure handling except the step where it chooses the view. LLM chooses the child closest to the end but GLM also has to consider the distance to the current child.
I'm working on the fix.
But still, this is obviously buggy. It is OK for GLM to rely on LLM to handle basic focus search failure handling except the step where it chooses the view. LLM chooses the child closest to the end but GLM also has to consider the distance to the current child.
I'm working on the fix.
yb...@google.com <yb...@google.com> #5
Fixed. Should be available in the next release. (23.2+)
ch...@gmail.com <ch...@gmail.com> #6
[Comment deleted]
bo...@gmail.com <bo...@gmail.com> #7
How Can I use 23.2+ in android-studio? Cause can not compile right now
bo...@gmail.com <bo...@gmail.com> #8
Or can you show me the patch? Thanks.
ba...@gmail.com <ba...@gmail.com> #9
i think this issue is coming again on v. 24.1.1
My problem is not exactly the same as reported here, but quite similar.
I am very frustrated with this. after several hours of searching, I found the solution
for the moment, i solve it by calling rv.setFocusable(false); to my recyclerview
My problem is not exactly the same as reported here, but quite similar.
I am very frustrated with this. after several hours of searching, I found the solution
for the moment, i solve it by calling rv.setFocusable(false); to my recyclerview
yb...@google.com <yb...@google.com> #10
#10, if you can add more details with a sample app to reproduce, I'll re-open this ticket.
da...@gmail.com <da...@gmail.com> #11
I have the same issue as #10. I have a RecyclerView with LinearLayoutManager. When this line executes after a button click: editText.SetVisibilty(View.VISIBLE). Then it will get or tries to get focus and the list items will "jump" one itemHeight lower or higher (it switches everytime). When I put rv.seFocusable(false), then the list items will not "jump" after the button click and the EditText is shown. But when I then click on the EditText, it receives focus and then it will "jump".
I use 25.3.1
I use 25.3.1
in...@panaccess.com <in...@panaccess.com> #12
I have the same problem with buildToolsVersion "26.0.1" or any other I have tried.
I tracked the problem down to a nasty racing condition.
When pressing the right D-Pad, the current element is unfocused, then the next item is focused in what it seems two separate tasks on the UIThread Stack,
When holding the D-Pad right button, the button events come in such a quick succession that the right pressed event is in between those two tasks.
To make matters worse, the clearFocus function always calles rootViewRequestFocus which will select the first focusable element in the whole app it can find. So the next right pressed event is caught by that and voila you are suddenly somewhere else in your app.
Here a more detailed description what I think happens from my viewpoint.
1) The correct element (A) is focused.
UIThread: Nothing
2) Right pressed Event hits the UIThread
UIThread: RightKeyEvent
3) The RightKeyEvent gets executed and the unfocus task of element (A) is put on UI Thread (not the focus task yet it seems for some reason)
UIThread: Unfocus(A)
4) The element (A) gets unfocused and in the same action rootViewRequestFocus Focuses some other element (X) in your view
/**
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* <p>
* <strong>Note:</strong> When a View clears focus the framework is trying
* to give focus to the first focusable View from the top. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
* </p>
*/
public void clearFocus() {
if (DBG) {
System.out.println(this + " clearFocus()");
}
clearFocusInternal(null, true, true);
}
/**
* Clears focus from the view, optionally propagating the change up through
* the parent hierarchy and requesting that the root view place new focus.
*
* @param propagate whether to propagate the change up through the parent
* hierarchy
* @param refocus when propagate is true, specifies whether to request the
* root view place new focus
*/
void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
}
onFocusChanged(false, 0, null);
refreshDrawableState();
if (propagate && (!refocus || !rootViewRequestFocus())) {
notifyGlobalFocusCleared(this);
}
}
}
UIThread: Nothing
5) The next right key event comes in and is put on UIThread stack
UIThread: RightClickEvent
6) Only now it seems the Task for focusing the next correct element (B) is put on the UI Thread. Why this late?
UIThread: RightClickEvent, Focus element (B)
7) The right click event gets executed while element (X) is focused and puts a focus task on the UI Thread relative to the wrongly selected element to select some other element (Y)
UIThread: Focus element (B), Focus element (Y)
8) The correct element (B) gets focused
UIThread: Focus element (Y)
9 ) The wrong element (Y) gets focused
UIThread: Nothing
And from here on you are not in your RecyclerView anymore :-(
Beside you guys fixing this, any idea how I can mitigate this problem. Seems rather hard to do...
I tracked the problem down to a nasty racing condition.
When pressing the right D-Pad, the current element is unfocused, then the next item is focused in what it seems two separate tasks on the UIThread Stack,
When holding the D-Pad right button, the button events come in such a quick succession that the right pressed event is in between those two tasks.
To make matters worse, the clearFocus function always calles rootViewRequestFocus which will select the first focusable element in the whole app it can find. So the next right pressed event is caught by that and voila you are suddenly somewhere else in your app.
Here a more detailed description what I think happens from my viewpoint.
1) The correct element (A) is focused.
UIThread: Nothing
2) Right pressed Event hits the UIThread
UIThread: RightKeyEvent
3) The RightKeyEvent gets executed and the unfocus task of element (A) is put on UI Thread (not the focus task yet it seems for some reason)
UIThread: Unfocus(A)
4) The element (A) gets unfocused and in the same action rootViewRequestFocus Focuses some other element (X) in your view
/**
* Called when this view wants to give up focus. If focus is cleared
* {@link #onFocusChanged(boolean, int, android.graphics.Rect)} is called.
* <p>
* <strong>Note:</strong> When a View clears focus the framework is trying
* to give focus to the first focusable View from the top. Hence, if this
* View is the first from the top that can take focus, then all callbacks
* related to clearing focus will be invoked after which the framework will
* give focus to this view.
* </p>
*/
public void clearFocus() {
if (DBG) {
System.out.println(this + " clearFocus()");
}
clearFocusInternal(null, true, true);
}
/**
* Clears focus from the view, optionally propagating the change up through
* the parent hierarchy and requesting that the root view place new focus.
*
* @param propagate whether to propagate the change up through the parent
* hierarchy
* @param refocus when propagate is true, specifies whether to request the
* root view place new focus
*/
void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
mPrivateFlags &= ~PFLAG_FOCUSED;
if (propagate && mParent != null) {
mParent.clearChildFocus(this);
}
onFocusChanged(false, 0, null);
refreshDrawableState();
if (propagate && (!refocus || !rootViewRequestFocus())) {
notifyGlobalFocusCleared(this);
}
}
}
UIThread: Nothing
5) The next right key event comes in and is put on UIThread stack
UIThread: RightClickEvent
6) Only now it seems the Task for focusing the next correct element (B) is put on the UI Thread. Why this late?
UIThread: RightClickEvent, Focus element (B)
7) The right click event gets executed while element (X) is focused and puts a focus task on the UI Thread relative to the wrongly selected element to select some other element (Y)
UIThread: Focus element (B), Focus element (Y)
8) The correct element (B) gets focused
UIThread: Focus element (Y)
9 ) The wrong element (Y) gets focused
UIThread: Nothing
And from here on you are not in your RecyclerView anymore :-(
Beside you guys fixing this, any idea how I can mitigate this problem. Seems rather hard to do...
cy...@googlemail.com <cy...@googlemail.com> #13
Sorry was logged in the wrong account. /\ is me.
Greetings,
CySlider
Greetings,
CySlider
am...@getchannels.com <am...@getchannels.com> #14
I am also seeing focus jump randomly away from the RecyclerView, while using the DPAD to scroll quickly through the list.
I'm using build tools 27.0.2 and recyclerview-v7:27.0.2
I'm using build tools 27.0.2 and recyclerview-v7:27.0.2
am...@getchannels.com <am...@getchannels.com> #15
In my case, I am using a simple LinearLayoutManager. I was able to throw an exception when a view outside my RecyclerView got focused, and captured this backtrace:
at android.view.View.requestFocus(View.java:8986)
at android.view.View.rootViewRequestFocus(View.java:6078)
at android.view.View.clearFocusInternal(View.java:6064)
at android.view.View.clearFocus(View.java:6041)
at android.view.ViewGroup.clearFocus(ViewGroup.java:981)
at android.view.ViewGroup.removeDetachedView(ViewGroup.java:4994)
at android.support.v7.widget.RecyclerView.removeDetachedView(RecyclerView.java:3934)
at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:8650)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3792)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3447)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3988)
at android.view.View.requestFocus(View.java:8986)
at android.view.View.rootViewRequestFocus(View.java:6078)
at android.view.View.clearFocusInternal(View.java:6064)
at android.view.View.clearFocus(View.java:6041)
at android.view.ViewGroup.clearFocus(ViewGroup.java:981)
at android.view.ViewGroup.removeDetachedView(ViewGroup.java:4994)
at android.support.v7.widget.RecyclerView.removeDetachedView(RecyclerView.java:3934)
at android.support.v7.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:8650)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3792)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3447)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3988)
is...@gmail.com <is...@gmail.com> #16
Hello @here,
I can see that the status of the bug is Fixed, but it is still happening even in support library version 27 (as reported just above).
For me, any of the proposed solutions worked. I'm spending a lot of hours trying to find a solution for myself.
Is this ticket not being tracked and updated anymore?
Thnx.
I can see that the status of the bug is Fixed, but it is still happening even in support library version 27 (as reported just above).
For me, any of the proposed solutions worked. I'm spending a lot of hours trying to find a solution for myself.
Is this ticket not being tracked and updated anymore?
Thnx.
yb...@google.com <yb...@google.com>
am...@getchannels.com <am...@getchannels.com> #17
Based on the stack trace I captured above, I was able to work around this bug by neutering clearFocus() on my item views, so detached views losing focus did not move focus outside the RecyclerView:
override fun clearFocus() {
if (parent != null)
super.clearFocus()
}
override fun clearFocus() {
if (parent != null)
super.clearFocus()
}
ha...@gmail.com <ha...@gmail.com> #18
Did you override clearFocus in the constructor of the viewholder? Sorry not understanding where/how to override clearFocus
am...@getchannels.com <am...@getchannels.com> #19
The view that the viewholder wraps should be of a custom subclass with the custom clearFocus(). You can either instantiate your subclass directly, or via layoutinflater by referencing it in a layout.
ha...@gmail.com <ha...@gmail.com> #20
I'm trying to extend the View class and change the clearFocus() but am just getting crashes
"android.widget.LinearLayout cannot be cast to io.solarplayer.presentation.ui.utils.CustomView"
After extending the LinearLayout class instead I get this same error. Here is where I create the viewholder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CustomView view = (CustomView ) layoutInflater.inflate(ViewHolder.LayoutResId, parent, false);
return new ViewHolder(context, view);
}
"android.widget.LinearLayout cannot be cast to io.solarplayer.presentation.ui.utils.CustomView"
After extending the LinearLayout class instead I get this same error. Here is where I create the viewholder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CustomView view = (CustomView ) layoutInflater.inflate(ViewHolder.LayoutResId, parent, false);
return new ViewHolder(context, view);
}
mc...@gmail.com <mc...@gmail.com> #21
+ 1 Seeing this problem on recyclerView 26.1.0
mc...@gmail.com <mc...@gmail.com> #22
Some more details: I'm using a horizontal recycler view, and it would lose focus scrolling to the right, but not the left.
ma...@gmail.com <ma...@gmail.com> #23
also facing this issue on Android TV, Android FireStick TV and support libraries 27.0.2
sl...@gmail.com <sl...@gmail.com> #24
Same problem here. RecyclerView losing focus, but only when scrolling to the right.
dp...@google.com <dp...@google.com>
al...@google.com <al...@google.com> #25
Over to RV owner per go/androidx/androidx_library_list.md
ma...@gmail.com <ma...@gmail.com> #26
I am also seeing this targeting P. I had the com.android.support:recyclerview-v7:28.0.0 imported on Gradle. Sadly, I am experiencing losing focus when holding D-PAD's right button. Navigating (holding left button D-PAD) to the left doesn't seem to cause any issue for me.
el...@gmail.com <el...@gmail.com> #27
In my experience, losing/mishandling focus in RecyclerView happens because of 3 reasons: 1) FocusFinder sucks. 2) RecyclerView uses ephemeral Views. 3) RecyclerView easily allows focus to escape outside of itself.
FocusFinder just sucks. It is a singlton. It is unusable. It's default focus search algo is stupid, and AOSP people are somehow able to break it with each new release — always making it worse. The biggest downside of FocusFinder is that it readily returns Views outside of current ViewGroup, often in different Fragment, often in different Z-level, often not visible Views (isShown() returns false), and it happily allows ephemeral/zero-sized/obscured Views to take focus. AOSP devs tried to fix it (haha) by disqualifying "zero-sized" Views from receiving focus since API 28. But that only made things worse — they are still not checking for isShown(), so "visible" children of INVISIBLE Views are still qualified! And disqualifying zero-sized Views removed a guarantee, that focus transition always happens immediately (e.g. they resurrected an abomination which is PFLAG_WANTS_FOCUS). Oh, and instead of fixing FocusFinder, they did that change directly in source code of View.class
In theory, behaviour of FocusFinder may be adjusted by restricting scope of focus search via the first parameter if it's methods ("root"). That would allow to search for next candidate among children of RecyclerView before letting the focus jump outside. Unfortunately, that parameter is ignored since Android 9 (the details are explained inhttps://issuetracker.google.com/issues/145840681 ). This means, that findNextFocus will always return the element according to it's absolute screen position, even if focusing it makes zero sense from navigation standpoint.
[/The end of rant about FocusFinder]
The use of ephemeral Views by RecyclerView means, that it will often create focusable Views, then immediately remove them afterwards during the same layout pass. When that happens, RecyclerView as whole will lose focus, and the focus will either jump to some other View on screen (hopefully inside the same RecyclerView) or escape elsewhere. For example, in horizontal RecyclerView the act of focusing a rightmost visible View invalidates it's layout, which in turn forces RecyclerView to create an ephemeral item (see this article [1] for details) to the right of it. That item is ephemeral, but still focusable — since it is just ordinary item from adapter's viewpoint, — so it may gain focus and immediately lose it once RecyclerView removes it at the end of layout pass.
Ephemeral Views are considered an "implementation detail" (seriously?) so we, app developers, can not check if specific View is ephemeral before giving it focus. There is ViewHolder.isRecyclable(), but no isEphemeral() or anything like that.
Finally, the biggest problem is that RecyclerView does not care when it's children lose focus. But it MUST care!! Losing focus is a completely irreversible operation in Android! Once a View loses focus, it is no longer expected to get it back unless a user clicks directional key again and it is nominated by FocusFinder. In theory, you can call requestFocus on yourself, but once there is a non-trivial number of Views on screen, such behavior will lead to death-loops of perpetually stealing focus from each-other. And many ViewGroups are unable to cope with suddenly losing focus: I have seen layout of out-of-box Leanback fragments completely fall apart when anyone outside them calls requestFocus() even once. A focus, once lost, can not return. But RecyclerView and it's classes have no callbacks for handling focus loss. I can override removeDetachedView() and check if the View is focused, but inside that method the View is already detached and divorced from RecyclerView internals and it is invoked in the middle of layout pass, so I can not just give focus to something else — ALL of RecyclerView children may already be detached at that point! I can not call fill() in my code. And I can not pass focus to something which is not even visible on screen — I can not call fill() and I am not even sure if it is even possible to fill() Views located far outside of visible RecyclerView area.
Seriously, it feels like focus-related matters were complete afterthought in RecyclerView development. There is a couple of focus-related callbacks in LayoutManager: onFocusSearchFailed() and onInterceptFocusSearch(). But former is useless without access to fill() and other LayoutManager internals, and later is doubly useless, because it can not be used to prevent focus from moving (if it returns null, FocusFinder will just hand focus to some crap outside the RecyclerView). So we have 2 useless callbacks to handle the functionality, expressed by 20+ methods in ViewGroup. No onFocusLoss() callback. No way to pass a focus to View outside of visible area...
When can we expect a FocusManager or some other kind of proper API for controlling focus in RecyclerView?
1:https://medium.com/android-news/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-404ba3453714
FocusFinder just sucks. It is a singlton. It is unusable. It's default focus search algo is stupid, and AOSP people are somehow able to break it with each new release — always making it worse. The biggest downside of FocusFinder is that it readily returns Views outside of current ViewGroup, often in different Fragment, often in different Z-level, often not visible Views (isShown() returns false), and it happily allows ephemeral/zero-sized/obscured Views to take focus. AOSP devs tried to fix it (haha) by disqualifying "zero-sized" Views from receiving focus since API 28. But that only made things worse — they are still not checking for isShown(), so "visible" children of INVISIBLE Views are still qualified! And disqualifying zero-sized Views removed a guarantee, that focus transition always happens immediately (e.g. they resurrected an abomination which is PFLAG_WANTS_FOCUS). Oh, and instead of fixing FocusFinder, they did that change directly in source code of View.class
In theory, behaviour of FocusFinder may be adjusted by restricting scope of focus search via the first parameter if it's methods ("root"). That would allow to search for next candidate among children of RecyclerView before letting the focus jump outside. Unfortunately, that parameter is ignored since Android 9 (the details are explained in
[/The end of rant about FocusFinder]
The use of ephemeral Views by RecyclerView means, that it will often create focusable Views, then immediately remove them afterwards during the same layout pass. When that happens, RecyclerView as whole will lose focus, and the focus will either jump to some other View on screen (hopefully inside the same RecyclerView) or escape elsewhere. For example, in horizontal RecyclerView the act of focusing a rightmost visible View invalidates it's layout, which in turn forces RecyclerView to create an ephemeral item (see this article [1] for details) to the right of it. That item is ephemeral, but still focusable — since it is just ordinary item from adapter's viewpoint, — so it may gain focus and immediately lose it once RecyclerView removes it at the end of layout pass.
Ephemeral Views are considered an "implementation detail" (seriously?) so we, app developers, can not check if specific View is ephemeral before giving it focus. There is ViewHolder.isRecyclable(), but no isEphemeral() or anything like that.
Finally, the biggest problem is that RecyclerView does not care when it's children lose focus. But it MUST care!! Losing focus is a completely irreversible operation in Android! Once a View loses focus, it is no longer expected to get it back unless a user clicks directional key again and it is nominated by FocusFinder. In theory, you can call requestFocus on yourself, but once there is a non-trivial number of Views on screen, such behavior will lead to death-loops of perpetually stealing focus from each-other. And many ViewGroups are unable to cope with suddenly losing focus: I have seen layout of out-of-box Leanback fragments completely fall apart when anyone outside them calls requestFocus() even once. A focus, once lost, can not return. But RecyclerView and it's classes have no callbacks for handling focus loss. I can override removeDetachedView() and check if the View is focused, but inside that method the View is already detached and divorced from RecyclerView internals and it is invoked in the middle of layout pass, so I can not just give focus to something else — ALL of RecyclerView children may already be detached at that point! I can not call fill() in my code. And I can not pass focus to something which is not even visible on screen — I can not call fill() and I am not even sure if it is even possible to fill() Views located far outside of visible RecyclerView area.
Seriously, it feels like focus-related matters were complete afterthought in RecyclerView development. There is a couple of focus-related callbacks in LayoutManager: onFocusSearchFailed() and onInterceptFocusSearch(). But former is useless without access to fill() and other LayoutManager internals, and later is doubly useless, because it can not be used to prevent focus from moving (if it returns null, FocusFinder will just hand focus to some crap outside the RecyclerView). So we have 2 useless callbacks to handle the functionality, expressed by 20+ methods in ViewGroup. No onFocusLoss() callback. No way to pass a focus to View outside of visible area...
When can we expect a FocusManager or some other kind of proper API for controlling focus in RecyclerView?
1:
ae...@google.com <ae...@google.com>
gu...@gmail.com <gu...@gmail.com> #28
Seing the same issue now in 2021.. Using androidx.recyclerview.widget.RecyclerView
This only seems to happen when you have an image in your root view that is being loaded over URL.
If the images are cached, no problems occur
This only seems to happen when you have an image in your root view that is being loaded over URL.
If the images are cached, no problems occur
tr...@gmail.com <tr...@gmail.com> #29
Seeing the exact same issue on Android TV OS 11.
I found overriding clearFocus hack
override fun clearFocus() {
if (parent != null)
super.clearFocus()
}
To not work at all and just make things really screwed up. Whole RecyclerView would just disappear.
Found the issue could be mitigated slightly but not prevented by making your recyclerview as performant as possible. In particular increase view pool max recycled views size and item cache size.
gm...@gmail.com <gm...@gmail.com> #30
7 years still this bullshit exists.
j6...@gmail.com <j6...@gmail.com> #31
Thankfully this bullshit has a workaround
gi...@globant.com <gi...@globant.com> #32
Thankfully this bullshit has a workaround x2
ro...@google.com <ro...@google.com>
su...@innocrux.com <su...@innocrux.com> #33
check if your child view have <requestFocus/> tag
Description
Version used: 23.1.0
Theme used: default on target 23
Issue:
Use RecyclerView with GridLayoutManager (assume it is vertical) with columns number > 1, navigate with d-pad (or arrows on emulator). When approaching bottom, the focus always jumps to last element, even if previously focus was on the element from non-last column.
Similar behavior is observed in horizontal orientation. Also this happens when navigating to top/left except that focus jumps to first element in that case.
Same issue on stackoverflow:
As I can see, the issue is with GridLayoutManager which must override GridLayoutManager.onFocusSearchFailed() method. Instead it uses LinearLayoutManager implementation.
My workaround for this bug: