解决RecyclerView聚焦时滚动到起始处的问题

最近在做EPG布局界面时,采用了一个Linearlayout布局嵌套多个RecyclerView来实现EPG通道信息展示的功能,但是在测试的时候发现,当RecyclerView左边子Item显示不全时,焦点从右侧移入时,RecyclerView会莫名其妙滚动到第一项,经过一番测试,发现是父布局LinearLayout聚焦在执行requestChildFocus函数时,聚焦的focusChild的index是-1,也就是没找到需要聚焦的子View,导致了RecyclerView莫名滚动到第一项,下面我们先从Android的焦点查找讲起。

当dispatchKeyEvent没有消耗掉KeyEvent,那么会由安卓系统来处理焦点移动,系统会通过view的focusSearch方法找到下一个获取焦点的View,然后调用requestFocus设置焦点。

 

1.view的focusFocus函数,不会直接去查找,而是会交给其parent(也就是ViewGroup)的focusSearch方法

public View focusSearch(@FocusRealDirection int direction) {
    if (mParent != null) {
        return mParent.focusSearch(this, direction);
    } else {
        return null;
    }
}

ViewGroup的focusSearch()方法:

public View focusSearch(View focused, int direction) {
    if (isRootNamespace()) {
        return FocusFinder.getInstance().findNextFocus(this, focused, direction);
    } else if (mParent != null) {
        return mParent.focusSearch(focused, direction);
    }
    return null;
}

这里会判断是否为根布局,也就是顶层布局(DecoderView),如果是最后交给FocusFinder去查找

2.FocusFinder的findNextFocus函数

位于顶层的ViewGroup把自己和当前焦点(View)以及方向传入

public final View findNextFocus(ViewGroup root, View focused, int direction) {
    return findNextFocus(root, focused, null, direction);
}

private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
    View next = null;
    if (focused != null) {
        //1.优先从xml或者代码中指定focusid的View中找
        next = findNextUserSpecifiedFocus(root, focused, direction);
    }
    if (next != null) {
        return next;
    }
    ArrayList<View> focusables = mTempList;
    try {
        focusables.clear();
        root.addFocusables(focusables, direction);
        if (!focusables.isEmpty()) {
            // 2.其次,根据算法去找,原理就是找在方向上最近的View
            next = findNextFocus(root, focused, focusedRect, direction, focusables);
        }
    } finally {
        focusables.clear();
    }
    return next;
}

到这里,我们简单罗列下焦点的查找步骤:

focusSearch-》findNextFocus-》addFocusables-》findNextFocus

在没有消耗 dispatchKeyEvent的情况下:

FocusSearch 一层层上去,调用 FocusFinder.getInstance().findNextFocus… … 后,在 …addFocusables 下,将所有带焦点属性的 view 全部加到数组里面去,然后通用方向,位置等查找相近的view.

介绍到这里,我们已经有思路了,可以在LinearLayout再次聚焦时手动指定要聚焦的View,这样RecyclerView就不会莫名其妙滚动到第一项了,具体是重写addFocusables函数

@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
    //加一个标志位,用于手动聚焦操作
    if (needFocus) {
        needFocus = false;
        int childCount = getChildCount();
        if (childCount > focusIndex) {
            View view = getChildAt(focusIndex);
            if (view instanceof RecyclerView) {
                RecyclerView rv = (RecyclerView) view;
                RecyclerView.Adapter adapter = rv.getAdapter();
                if (adapter != null) {
                    views.add(view);
                    return;
                }
            }
        }
    }
    super.addFocusables(views, direction, focusableMode);
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值