解决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);
}
上一篇: 实现自定义小圆圈加载