RecyclerView 焦点问题

Android TV项目要求为RecyclerView列表增加焦点循环与翻页功能,本身功能不难实现,就是滚动列表,然后重定位焦点即可;

不过实际测试发现经常有焦点丢失的问题,让人十分头痛。

先看其中一个现象:

现象:焦点位于第一个item,向上移动,焦点定位到最后一个item,向下移动,焦点重新回到第一个item,再次向上移动,虽然有滚动到列表的底部,但是焦点丢失了。

分析:通过log发现,第二次向上移动时,最后一个item没有跑Adapter的onBindViewHolder函数,导致onBindViewHoler中设置焦点的代码没有跑

if (selectedPosition == position) {
    holder.container.requestFocus();
}

再分析log发现,第一次打开列表,会跑11次onBindViewHoler函数(page size为8),第一次移动到底部时onBindViewHoler也跑了11次,但第二次就只有8次了,最后的三个item没有跑onBindViewHoler来加载。

结论:RecyclerView自带的缓存机制导致这种问题,RecyclerView会预加载前后一定数量的item,这些item在显示时不会再次调用onBindViewHoler

对策:总之这是由于RecyclerView本身的机制导致,那么找一个item显示时一定会跑的方法来设置焦点即可

@Override
public void onViewAttachedToWindow(MyViewHolder holder) {
    super.onViewAttachedToWindow(holder);

    MyLinearLayoutManager ll = (MyLinearLayoutManager) mRecyclerView.getLayoutManager();
    int position = ll.getPosition(holder.container);
    if (needSetSelect && position == selectedPosition && !holder.container.isFocused()) {
        holder.container.requestFocus();
        needSetSelect = false;
    }
}

public void setSelection(int position) {
    selectedPosition = position;
    needSetSelect = true;
}

Item显示一定会跑onViewAttachedToWindow方法,在这里再设置焦点即可。

 

补充1:

之前测试漏了一种情况,当前pageSize=8,如果list只有10项,初始显示范围0-7,翻页后为2-9,焦点在2上,但是ITEM 2在视图1中已经显示了,所以既不会跑onBindViewHoler,也不会跑onViewAttachedToWindow,所以又会出现焦点丢失的问题

直接给对策,重写LinearLayoutManager,在onInterceptFocusSearch中处理焦点问题:

@Override
public View onInterceptFocusSearch(View focused, int direction) {
    if (mRecyclerView != null) {
        int count = getItemCount();
        int fromPos = getPosition(focused);
        int firtVisibleItemPos = findFirstVisibleItemPosition();
        int lastVisibleItemPos = findLastVisibleItemPosition();
        int lastPos = 0;

        MyAdapter adapter = (MyAdapter) mRecyclerView.getAdapter();
        switch (direction) {
            case View.FOCUS_RIGHT:
                if (count > SIZE_PAGE) {
                    fromPos = firtVisibleItemPos + SIZE_PAGE;
                    lastPos = lastVisibleItemPos + SIZE_PAGE;
                    if (fromPos >= count) {
                        fromPos = 0;
                    } else if (lastPos >= count) {
                        fromPos = count - SIZE_PAGE;
                    }
                    scrollToPositionWithOffset(fromPos, 0);

                    // 如果焦点在上一个视图内,用getChildAt来获取ITEM,并使其获取焦点
                    if (fromPos <= lastVisibleItemPos
                            && fromPos >= firtVisibleItemPos) {
                        mRecyclerView.getChildAt(fromPos - firtVisibleItemPos)
                                .requestFocus();
                        break;
                    }

                    adapter.setSelection(fromPos);
                }
                break;
            case View.FOCUS_LEFT:
                if (count > SIZE_PAGE) {
                    fromPos = firtVisibleItemPos - SIZE_PAGE;
                    lastPos = lastVisibleItemPos - SIZE_PAGE;
                    if (lastPos < 0) {
                        fromPos = count - SIZE_PAGE;
                    } else if (fromPos < 0) {
                        fromPos = 0;
                    }
                    scrollToPositionWithOffset(fromPos, 0);

                    // 如果焦点在上一个视图内,用getChildAt来获取ITEM,并使其获取焦点
                    if (fromPos <= lastVisibleItemPos
                            && fromPos >= firtVisibleItemPos) {
                        mRecyclerView.getChildAt(fromPos - firtVisibleItemPos)
                                .requestFocus();
                        break;
                    }

                    adapter.setSelection(fromPos);
                }
                break;
            case View.FOCUS_DOWN:
                if (fromPos < firtVisibleItemPos
                        || fromPos > lastVisibleItemPos) {
                    return focused;
                }

                fromPos++;
                if (fromPos >= count) {
                    fromPos = 0;
                    scrollToPositionWithOffset(fromPos, 0);
                    // 焦点跳转时清除焦点
                    focused.clearFocus();

                    adapter.setSelection(fromPos);
                }
                break;
            case View.FOCUS_UP:
                if (fromPos < firtVisibleItemPos
                        || fromPos > lastVisibleItemPos) {
                    return focused;
                }

                fromPos--;
                if (fromPos < 0) {
                    fromPos = count - 1;
                    scrollToPositionWithOffset(fromPos, SIZE_PAGE - 1);
                    // 焦点跳转时清除焦点
                    focused.clearFocus();

                    adapter.setSelection(fromPos);
                }
                break;
        }
    }
    return super.onInterceptFocusSearch(focused, direction);
}

总之:

当焦点不可见时:在Adapter的onViewAttachedToWindow中设置焦点

当焦点可见时:在onInterceptFocusSearch中设置焦点

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值