'RecyclerView items lose focus

In my fire tv app I'm using a recyclerview with horizontal layout.

Scrolling with dpad works and also items are gaining focus.

But when I hold the button it scrolls very fast because many keydown events are triggered, and items are losing their focus and it's not possible to scroll anymore because another Textview above my recyclerview is gaining the focus.

It looks like a bug. Is there any workaround for this?



Solution 1:[1]

I think the answer by @tmm1 is the best one so far. I have successfully implemented this workaround for the issue of elements loaded in a RecyclerView loosing focus if user uses the DPAD too quickly to scroll.

In my RecyclerView's adapter I use a LayoutInflater to inflate my element views like this

@Override
public ListItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_keyboard_key, viewGroup, false);
    return new ListItemViewHolder(itemView);
}

My item_keyboard_key.xml were originally defined like this

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_keyboard_key_layout"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_keyboard_key_text"
        android:text="A"/>

</FrameLayout>

Then I created a new custom FrameLayout class (FocusFixFrameLayout) extending FrameLayout and using this as my root layout. My item_keyboard_key.xml then became

<?xml version="1.0" encoding="utf-8"?>
<no.bluebit.views.FocusFixFrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_keyboard_key_layout"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_keyboard_key_text"
        android:text="A"/>

</no.bluebit.views.FocusFixFrameLayout>

and my FocusFixFrameLayout class looks like this

public class FocusFixFrameLayout extends FrameLayout {

    public FocusFixFrameLayout(@NonNull Context context) {
        super(context);
    }

    public FocusFixFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public FocusFixFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FocusFixFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
}

    @Override
    public void clearFocus() {
        if(this.getParent() != null)
            super.clearFocus();
    }
}

Solution 2:[2]

I face the same problem, what i do in my project likes below in my Activity (containing RecyclerView):

    private long mLastKeyDownTime = 0;
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        long current = System. currentTimeMillis();
        boolean res = false;
        if (current - mLastKeyDownTime < 300 ) {
            res = true;
        } else {
            res = super.onKeyDown(keyCode, event);
            mLastKeyDownTime = current;
        }
        return res;
    }

which avoids the fast scrolling when you hold the button on the dpad, and the focus works fine in my RecyclerView.

Solution 3:[3]

Looks like this is a bug.

See https://stackoverflow.com/a/33190656/332798 and https://issuetracker.google.com/issues/37067220


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()
}

Solution 4:[4]

Try to Use notifyItemChanged(position) It's Work from me. I have call adapter inside adapter and my child adapter has multiple edittext which lose focus when i call notifydatasetchange. So i replace with notifyItemChanged(Position).

notifyItemChanged(position);

Solution 5:[5]

RecyclerView on android has a bug on losing focus when using a KeyListener like a dpad, controller or keyboard.

The work around Ive found is to make a function to disable all focusable elements you have on your activity. Example:

    private void defineFocus(boolean trueOrFalse){

    ImageView downloadsSmall = (ImageView)findViewById(R.id.small_downloads_img);
    downloadsSmall.clearFocus();
    ImageView settingsSmall = (ImageView)findViewById(R.id.small_settings_img);
    settingsSmall.clearFocus();

    downloadsSmall.setFocusable(trueOrFalse);
    settingsSmall.setFocusable(trueOrFalse);

}

When I want to scroll through my recyclerview horizontal or vertical doesnt matter i first call defineFocus(false); and later if i want to break free of my RecyclerView/adapter i will first defineFocus(true); and then request focus on the button/image/edittext/textview or whatever i want outside of the recyclerview.

Solution 6:[6]

I had the same issue when I used notifyItemChanged(position). Solved with sending an empty payload in addition to position like this notifyItemChanged(position, Any())

Solution 7:[7]

I have faced the same problem when was working on project for Android TV and leanback library.

The good news is that popular apps(YouTube and others) do not have this issue. I found informative resource. After investigation I made a solution that is convenient to me. The key is to use Presenter instead of Adapter There is the simplest example

//Presenter class
public class ItemViewPresenter extends Presenter {

    @Override
    public final ViewHolder onCreateViewHolder(ViewGroup parent) {

        //I use data binding
        ItemBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item, parent, false);
        return new MyViewHolder(binding);
    }

    @Override
    public final void onBindViewHolder(ViewHolder viewHolder, Object item) {
        String text = (String) item;
        ((MyViewHolder) viewHolder).bind(text);
    }

    @Override
    public final void onUnbindViewHolder(ViewHolder viewHolder) {
    }

    private class MyViewHolder extends Presenter.ViewHolder {

        private final ItemBinding binding;

        private DiscoveryViewHolder(ItemBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }

        private void bind(String text) {

            binding.textView.setText(text);
        }
    }
}

//... 
private void setupRecyclerView() {

    RecyclerView recyclerView = _baseLayout.findViewById(R.id.recyclerView);

    ArrayObjectAdapter arrayObjectAdapter = new ArrayObjectAdapter(new ItemViewPresenter());

    ItemBridgeAdapter bridgeAdapter = new ItemBridgeAdapter(arrayObjectAdapter);

    recyclerView.setAdapter(bridgeAdapter);

    LinearLayoutManager layoutManager = new LinearLayoutManager(_baseLayout.getContext(), LinearLayoutManager.HORIZONTAL, false);
    recyclerView.setLayoutManager(layoutManager);

    List<Strings> textList = new ArrayList(1);
    textList.add("1");

    //add objects
    arrayObjectAdapter.addAll(0, textList);

    //clear objects
    //arrayObjectAdapter.clear();
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 HÃ¥vard Bakke
Solution 2 Xiaozou
Solution 3
Solution 4 Nimesh Patel
Solution 5 Adrian M Cavazos
Solution 6 Eeone Thunder
Solution 7