Android焦点分发基础
来源:互联网 发布:淘宝第三方促销平台 编辑:程序博客网 时间:2024/06/18 13:51
目录(?)[+]
本文转载自:http://www.cnblogs.com/myzh/p/3664544.html?utm_source=tuicool&utm_medium=referral 请尊重原创
焦点处理相关记录
以下所涉及的焦点部分,只是按键移动部分,不明确包含Touch Focus部分
需解决问题
控件的下一个焦点是哪?
分析思路
当用户通过按键(遥控器等)触发焦点切换时,事件指令会通过底层进行一系列处理。
在ViewRootImpl.java中有一个方法,deliverKeyEventPostIme(…),因为涉及到底层代码,所以没有详细的跟踪分析此方法的调用逻辑,根据网上的资料,按键相关的处理会经过此方法。
private void deliverKeyEventPostIme(QueuedInputEvent q) { ... // Handle automatic focus changes. if (event.getAction() == KeyEvent.ACTION_DOWN) { int direction = 0; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_LEFT: if (event.hasNoModifiers()) { direction = View.FOCUS_LEFT; } break; case KeyEvent.KEYCODE_DPAD_RIGHT: if (event.hasNoModifiers()) { direction = View.FOCUS_RIGHT; } break; ... } if (direction != 0) { View focused = mView.findFocus(); if (focused != null) { View v = focused.focusSearch(direction); if (v != null && v != focused) { ..... if (v.requestFocus(direction, mTempRect)) { ...finishInputEvent(q, true); return; } } ... } }
由此方法可以看出,最主要的两个核心过程:
View v = focused.focusSearch(direction);v.requestFocus(direction, mTempRect)
接下来详细的分析下,看看过程中进行了什么操作
具体分析
在具体分析前,首先我们先明确下相关变量的定义
View mView : 主体View[DecorView]
//一般把主View“DecorView”添加到WindowManagerImpl中(通过addView) //WindowManagerImpl.java private void addView(View view...) { ViewRootImpl root; root = new ViewRootImpl(view.getContext()); ... root.setView(view, wparams, panelParentView); ... } //ViewRootImpl.java public void setView(View view....) { synchronized (this) { if (mView == null) { mView = view; ... } ... } }
所以mView是一个DecorView类型的变量.
View focused :
View focused = mView.findFocus(); //PhoneWindow.java private final class DecorView extends FrameLayout implements RootVie.... { ... } //FrameLayout.java public class FrameLayout extends ViewGroup { ... } //ViewGroup.java //mFocused记录的是当前被焦点选中的view @Override public View findFocus() { if (DBG) { System.out.println("Find focus in " + this + ": flags=" + isFocused() + ", child=" + mFocused); } if (isFocused()) { return this; } if (mFocused != null) { return mFocused.findFocus(); } return null;}
所以最终得到的focused为当前页面中得到焦点的view.
在明确的相关变量后,我们开始View v = focused.focusSearch(direction)的具体分析.
//View.java
public View focusSearch(int direction) {
//如果存在父控件,则执行父控件的focusSearch方法
if (mParent != null) {
return mParent.focusSearch(this, direction);
} else {
return null;
}
}
//ViewGroup.java
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;
}
说明在这个过程中,其实是从里层开始一直遍历到最外层布局,然后在最外层布局将处理交给了FocusFinder中的方法.
FocusFinder.getInstance().findNextFocus(this, focused, direction);
那我们来看看此方法具体做了什么操作
//FocusFinder.javapublic final View findNextFocus(ViewGroup root, View focused, int direction) { return findNextFocus(root, focused, null, direction);}//FocusFinder.javaprivate View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) { View next = null; if (focused != null) { next = findNextUserSpecifiedFocus(root, focused, direction); } if (next != null) { return next; } ArrayList<View> focusables = mTempList; try { focusables.clear(); root.addFocusables(focusables, direction); if (!focusables.isEmpty()) { next = findNextFocus(root, focused, focusedRect, direction, focusables); } } finally { focusables.clear(); } return next;}
发现在findNextFocus的执行过程的开始,先执行了findNextUserSpecifiedFocus(…)方法,由代码可以看出,此方法先去判断特定Id值是否存在,若存在则查询出Id对应的view.其实这些Id就是xml里通过android:nextFocusUp=”…”等或者代码特别指定的焦点顺序.所以在此过程先判断,若存在,说明下个焦点已经找到,直接返回.
//FocusFinder.javaprivate View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) { // check for user specified next focus View userSetNextFocus = focused.findUserSetNextFocus(root, direction); if (userSetNextFocus != null && userSetNextFocus.isFocusable() && (!userSetNextFocus.isInTouchMode() || userSetNextFocus.isFocusableInTouchMode())) { return userSetNextFocus; } return null;} //View.javaView findUserSetNextFocus(View root, int direction) { switch (direction) { case FOCUS_LEFT: if (mNextFocusLeftId == View.NO_ID) return null; return findViewInsideOutShouldExist(root, mNextFocusLeftId); case FOCUS_RIGHT: if (mNextFocusRightId == View.NO_ID) return null; return findViewInsideOutShouldExist(root, mNextFocusRightId); case FOCUS_UP: if (mNextFocusUpId == View.NO_ID) return null; return findViewInsideOutShouldExist(root, mNextFocusUpId); case FOCUS_DOWN: if (mNextFocusDownId == View.NO_ID) return null; return findViewInsideOutShouldExist(root, mNextFocusDownId); case FOCUS_FORWARD: if (mNextFocusForwardId == View.NO_ID) return null; return findViewInsideOutShouldExist(root, mNextFocusForwardId); case FOCUS_BACKWARD: { if (mID == View.NO_ID) return null; final int id = mID; return root.findViewByPredicateInsideOut(this, new Predicate<View>() { @Override public boolean apply(View t) { return t.mNextFocusForwardId == id; } }); } } return null;}
如果上面过程没有查询到,则会执行到findNextFocus(…)方法.在这个方法中,先通过offsetDescendantRectToMyCoords(…)方法获得焦点控件的位置矩阵.然后通过比较得到下一个焦点的控件。具体的比较规则可以查看findNextFocusInRelativeDirection(…)方法与findNextFocusInAbsoluteDirection(…)方法.
//FocusFinder.javaprivate View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction, ArrayList<View> focusables) { if (focused != null) { if (focusedRect == null) { focusedRect = mFocusedRect; } // fill in interesting rect from focused focused.getFocusedRect(focusedRect); root.offsetDescendantRectToMyCoords(focused, focusedRect); } else { if (focusedRect == null) { focusedRect = mFocusedRect; // make up a rect at top left or bottom right of root switch (direction) { case View.FOCUS_RIGHT: case View.FOCUS_DOWN: setFocusTopLeft(root, focusedRect); break; case View.FOCUS_FORWARD: if (root.isLayoutRtl()) { setFocusBottomRight(root, focusedRect); } else { setFocusTopLeft(root, focusedRect); } break; case View.FOCUS_LEFT: case View.FOCUS_UP: setFocusBottomRight(root, focusedRect); break; case View.FOCUS_BACKWARD: if (root.isLayoutRtl()) { setFocusTopLeft(root, focusedRect); } else { setFocusBottomRight(root, focusedRect); break; } } } } switch (direction) { case View.FOCUS_FORWARD: case View.FOCUS_BACKWARD: return findNextFocusInRelativeDirection(focusables, root, focused, focusedRect, direction); case View.FOCUS_UP: case View.FOCUS_DOWN: case View.FOCUS_LEFT: case View.FOCUS_RIGHT: return findNextFocusInAbsoluteDirection(focusables, root, focused, focusedRect, direction); default: throw new IllegalArgumentException("Unknown direction: " + direction); }}
结论
查找焦点的过程,主要是从View的focusSearch(…)方法开始,从当前焦点开始逐层往外,最终在最外层布局执行FocusFinder中的核心方法来获得下个焦点所在的视图view.
如果需要指定跳转,可以在逐层focusSearch(…)的时候,返回特定的view
- Android焦点分发基础
- Android焦点分发基础
- Android焦点分发基本流程
- Android焦点分发方法介绍
- android 焦点分发源码解析
- 图解android焦点传递,事件分发
- Android事件分发与焦点争夺
- Android TV 焦点分发原理解析
- Android焦点事件分发与传递机制
- android基础(事件分发)
- android key事件分发与焦点事件的传递
- Android事件分发的View的焦点捕捉事件
- android tv基础之焦点(二)
- Android 事件分发拦截(基础篇)
- android焦点分发无法获取到Down事件,却有Move和Up
- android焦点
- Android界面设计基础:控件焦点4个步骤
- Android界面设计基础:控件焦点4个步骤
- 【Python】爬虫小结
- NOIP 2012 开车旅行
- xshell添加按钮。
- HDU 2021 发工资咯:)
- Android之网络丢包事件
- Android焦点分发基础
- RestFul WebService的创建和使用实例
- MySQL详解--锁
- Jprofile(三) - 查看线程CPU耗时
- Android Studio常用手册
- Gulp Error: Cannot find module 'jshint/src/cli';
- Python: 在Unicode和普通字符串之间转换
- Android NDK之JNI陷阱
- 协方差