RecyclerView 项目失去焦点

Posted

技术标签:

【中文标题】RecyclerView 项目失去焦点【英文标题】:RecyclerView items lose focus 【发布时间】:2016-03-20 13:07:55 【问题描述】:

在我的 fire tv 应用中,我使用的是 recyclerviewhorizontal layout

使用 dpad 滚动作品以及项目正在获得关注。

但是当我按住按钮时,它滚动得非常快,因为触发了许多 keydown 事件,并且项目失去了焦点,并且无法再滚动,因为我的 recyclerview 上方的另一个 Textview 正在获得焦点。

它看起来像一个错误。有什么解决方法吗?

【问题讨论】:

对于那些将面临同样问题的人。我找到了解决方法。我不再使用 RecyclerView。我已经切换到这个 Horizo​​ntalListView 库:github.com/sephiroth74/HorizontalVariableListView,这足以满足我的需求。 【参考方案1】:

我认为@tmm1 的答案是迄今为止最好的答案。如果用户使用 DPAD 滚动太快,我已经成功地实现了这个解决方法,解决了 RecyclerView 中加载的元素失去焦点的问题。

在我的 RecyclerView 的适配器中,我使用 LayoutInflater 来像这样膨胀我的元素视图

@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);

我的 item_keyboard_key.xml 最初是这样定义的

<?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_
    android:layout_>

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

</FrameLayout>

然后我创建了一个新的自定义 FrameLayout 类 (FocusFixFrameLayout) 扩展 FrameLayout 并将其用作我的根布局。我的 item_keyboard_key.xml 然后变成了

<?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_
    android:layout_>

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

</no.bluebit.views.FocusFixFrameLayout>

我的 FocusFixFrameLayout 类看起来像这样

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

【讨论】:

嗨,哈佛。它确实解决了我的问题。非常非常感谢你。你是救生员)) 这解决了某些设备上的问题,但在其他设备上仍然存在(例如 API 21 上的 Fire OS 5 设备) 我收到“java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法'boolean android.view.ViewGroup.getTouchscreenBlocksFocus()'”。自发布此答案以来有什么变化吗? 我尝试使用我的 ConstraintLayout,但它不起作用。在某些时候,回收站视图和整个屏幕会完全失去焦点。 尝试了我的 RelativeLayout 和 recyclerview 开始比以前更频繁地失去焦点。【参考方案2】:

我面临同样的问题,我在我的项目中所做的就像下面的我的 Activity(包含 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;
    

当您按住 dpad 上的按钮时避免快速滚动,并且焦点在我的 RecyclerView 中工作正常。

【讨论】:

我在我的项目中对此进行了测试,但它并没有真正起作用。它会减少问题,但它仍然存在 抱歉,没用(( 如何在不使用Activity的情况下,在某些片段或视图的关键监听器中使用它?【参考方案3】:

看起来这是一个错误。

见https://***.com/a/33190656/332798和https://issuetracker.google.com/issues/37067220


我可以通过在我的项目视图上取消 clearFocus() 来解决此错误,因此失去焦点的分离视图不会将焦点移到 RecyclerView 之外:

override fun clearFocus() 
    if (parent != null)
        super.clearFocus()

【讨论】:

parent 来自哪里? parent 定义在 FrameLayout/ViewGroup/View 我认为它没有在任何地方定义,我的意思是有一个 getParent() 方法但没有公共/受保护的 parent 变量。 我正在使用 Kotlin,所以 parent 正在调用 getParent() 嗨!对我来说,焦点并没有将焦点移到 RecyclerView 之外,但在某些时候,没有一个项目有焦点,它似乎完全重置了。【参考方案4】:

尝试使用 notifyItemChanged(position) 这是我的工作。我在适配器内部调用了适配器,而我的子适配器有多个edittext,当我调用notifydatasetchange 时它们会失去焦点。所以我用 notifyItemChanged(Position) 替换。

notifyItemChanged(position);

【讨论】:

【参考方案5】:

Android 上的 RecyclerView 在使用诸如 dpad、控制器或键盘之类的 KeyListener 时存在失去焦点的错误。

我发现的解决方法是创建一个函数来禁用您在活动中拥有的所有可聚焦元素。示例:

    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);


当我想水平或垂直滚动​​我的 recyclerview 时,我首先调用 defineFocus(false);稍后,如果我想摆脱我的 RecyclerView/adapter,我将首先定义Focus(true);然后请求关注 button/image/edittext/textview 或任何我想要的 recyclerview 之外的东西。

【讨论】:

【参考方案6】:

我在为 Android TVleanback library 开发项目时遇到了同样的问题。

好消息是流行的应用程序(YouTube 和其他)没有这个问题。 我找到了信息丰富的resource。经过调查,我提出了一个对我来说方便的解决方案。 关键是使用Presenter 而不是Adapter 有最简单的例子

//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();

【讨论】:

问题是关于 reycler-view 而不是leanback-library

以上是关于RecyclerView 项目失去焦点的主要内容,如果未能解决你的问题,请参考以下文章

Dpad 导航无法在 Appbarlayout 下方的 recyclerview 的电视上工作(recycler view 无法通过 dpad 获得焦点)

Android Tv RecyclerView 设置其项目的下一个焦点

kotlin Recycler查看分页

如何突出显示 Recycler View 的选定项目?

为啥 onBindViewHolder 索引在 Recycler View 中没有增加?

Android TV 中的 RecyclerView 项目焦点