焦点
来源:互联网 发布:在淘宝搜不到我的宝贝 编辑:程序博客网 时间:2024/04/28 21:02
- [Android学习笔记]理解焦点处理原理的相关记录
- 焦点处理相关记录以下所涉及的焦点部分,只是按键移动部分,不明确包含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;}}...}}由此方法可以看出,最主要的两个核心过程:12View v = focused.focusSearch(direction);v.requestFocus(direction, mTempRect)接下来详细的分析下,看看过程中进行了什么操作具体分析在具体分析前,首先我们先明确下相关变量的定义View mView : 主体View[DecorView]//一般把主View“DecorView”添加到WindowManagerImpl中(通过addView)//WindowManagerImpl.javaprivate void addView(View view...) {ViewRootImpl root;root = new ViewRootImpl(view.getContext());...root.setView(view, wparams, panelParentView);...}<br>//ViewRootImpl.javapublic void setView(View view....) {synchronized (this) {if (mView == null) {mView = view;...}...}}所以mView是一个DecorView类型的变量.View focused :View focused = mView.findFocus();<br>//PhoneWindow.javaprivate final class DecorView extends FrameLayout implements RootVie.... {...}<br>//FrameLayout.javapublic class FrameLayout extends ViewGroup {...}<br>//ViewGroup.java//mFocused记录的是当前被焦点选中的view@Overridepublic 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.javapublic View focusSearch(int direction) {//如果存在父控件,则执行父控件的focusSearch方法if (mParent != null) {return mParent.focusSearch(this, direction);} else {return null;}}//ViewGroup.javapublic 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 focusView userSetNextFocus = focused.findUserSetNextFocus(root, direction);if (userSetNextFocus != null && userSetNextFocus.isFocusable()&& (!userSetNextFocus.isInTouchMode()|| userSetNextFocus.isFocusableInTouchMode())) {return userSetNextFocus;}return null;}<br>//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>() {@Overridepublic 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 focusedfocused.getFocusedRect(focusedRect);root.offsetDescendantRectToMyCoords(focused, focusedRect);} else {if (focusedRect == null) {focusedRect = mFocusedRect;// make up a rect at top left or bottom right of rootswitch (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
0 0