I have a RecyclerView with usually two columns and up to eight. We use a lot of D-PAD navigation. We have a problem when scrolling, the focused item will jump from left to right. See photos:
I noticed that if the items that are coming up next are cached, there is no focus problem when scrolling. Another problem I have is my focus item can appear below my sticky header. This is not desired. So I feel like if I made it so when scrolling it would have a sort of "threshold". This way when the focus is within one item of being off screen, it will scroll. This way the focus is never at the very bottom, or the very top.
With that in mind, I tried this approach with no luck:
RecyclerView rv;
@Override
public void onFocusChange(View v, boolean hasFocus) {
if(!v.hasFocus()) {
Log.w(TAG, "View v did not have focus");
return;
}
final int index = rv.getChildPosition(v); //adapter pos
if(index == NO_POSITION) {
Log.w(TAG, "Recycler view did not have view");
return;
}
int position = rv.indexOfChild(v); // layout pos
int lastPos = rv.getChildCount(); // layout pos
int span = getLayoutManager().getSpanCount();
int threshold = 2 * span;
Log.d(TAG, String.format("Position: %1$d. lastPos: %2$d. span: %3$d. threshold: %4$d", position, lastPos, span, threshold));
if (position >= (lastPos - threshold)) {
//scroll down if possible
int bottomIndex = rv.getChildPosition(rv.getChildAt(lastPos));
if (bottomIndex < getItemCount()) {
//scroll down
int scrollBy = v.getHeight();
recycler.scrollBy(0, scrollBy);
Log.d(TAG, String.format("Scrolling down by %d", scrollBy));
}
} else if (position <= threshold) {
//scroll up if possible
int topIndex = rv.getChildPosition(rv.getChildAt(0));
if (topIndex > 0) {
//scroll up
int scrollBy = v.getHeight();
recycler.scrollBy(0, -scrollBy);
Log.d(TAG, String.format("Scrolling up by %d", -scrollBy));
}
}
}
I am open to any ideas on how to manage the focus when scrolling.
I reported this bug to AOSP issue tracker: issue 190526
As I can see from source the issue is because GridLayoutManager
uses LinearLayoutManager
's implementation of onFocusSearchFailed()
which is called when focus approaches the inner border of RecyclerView
. LinearLayoutManager
's implementation just offers first/last (depends on scrolling direction) element. Hence focus jumps to first/last element of new row.
My workaround for this issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With