1.ViewRootImpl
1.连接WindowManager和DecorView的纽带
2.完成view的measure,layout,draw
3.向DecorView分发按键、触摸事件等。
关于按键事件和焦点寻找:
先判断是否有按键事件处理
1.若返回true,则打断该方向上的焦点寻找。
2.若返回fasle,则根据指定的方向寻找最近且可获取焦点的view
2.1判断view的类型,是否为ViewGroup。
final class ViewPostImeInputStage extends InputStage {public ViewPostImeInputStage(InputStage next) {super(next);}@Overrideprotected int onProcess(QueuedInputEvent q) {if (q.mEvent instanceof KeyEvent) {return processKeyEvent(q); //若是按键事件,则走该方法处理按键和焦点} else {final int source = q.mEvent.getSource();if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {return processPointerEvent(q); //若是触摸事件,则走该方法处理} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {return processTrackballEvent(q);} else {return processGenericMotionEvent(q);}}}@Overrideprotected void onDeliverToNext(QueuedInputEvent q) {//此处不分析,省略N行...........}private int processKeyEvent(QueuedInputEvent q) {final KeyEvent event = (KeyEvent)q.mEvent;//首先由dispatchKeyEvent进行分发,如果返回true的,则不再继续。//1.此处的mView是DecorView,即是个ViewGroup对象,则调用的是其dispatchKeyEvent方法//2.若未被处理,则以下流程,比如,按上下键,则需要寻找下一个焦点等等if (mView.dispatchKeyEvent(event)) {return FINISH_HANDLED;}if (shouldDropInputEvent(q)) {return FINISH_NOT_HANDLED;}// If the Control modifier is held, try to interpret the key as a shortcut.if (event.getAction() == KeyEvent.ACTION_DOWN&& event.isCtrlPressed()&& event.getRepeatCount() == 0&& !KeyEvent.isModifierKey(event.getKeyCode())) {//首先会将事件进行分发,如果没有被消费,则开始寻找下一个可获取焦点的view。//如果被消费,则不再继续。if (mView.dispatchKeyShortcutEvent(event)) {return FINISH_HANDLED;}if (shouldDropInputEvent(q)) {return FINISH_NOT_HANDLED;}}//若未被子view拦截,开始处理按键,根据direction进行处理if (event.getAction() == KeyEvent.ACTION_DOWN) {int direction = 0;switch (event.getKeyCode()) {//根据code设置direction的值...............}if (direction != 0) {//在根视图层中寻找当前有焦点的viewView focused = mView.findFocus();if (focused != null) {========= 1. 展开分析focused.focusSearch() ===========View v = focused.focusSearch(direction);if (v != null && v != focused) ;focused.getFocusedRect(mTempRect);//若是ViewGroup类型,则当前被聚焦的view,计算出一个坐标矩形。并将其赋值给可聚焦viewif (mView instanceof ViewGroup) {((ViewGroup) mView).offsetDescendantRectToMyCoords(focused, mTempRect);((ViewGroup) mView).offsetRectIntoDescendantCoords(v, mTempRect);}========= 2.此处下面展开分析 =========if (v.requestFocus(direction, mTempRect)) {playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));return FINISH_HANDLED;}}if (mView.dispatchUnhandledMove(focused, direction)) {return FINISH_HANDLED;}} else {========= 3.如果focused为null,以下展开分析 ===========View v = focusSearch(null, direction);if (v != null && v.requestFocus(direction)) {return FINISH_HANDLED;}}}}return FORWARD;
1.1展开分析ViewRootImpl内部的focusSearch(),调用了View.requestFocus()
作用:1.寻找指定方向上的view
2.判断是否有mParent,即其父view。
public View focusSearch(@FocusRealDirection int direction) {if (mParent != null) {return mParent.focusSearch(this, direction);} else {return null;}}
关于mParent的由来,如果view存在mParent,则其父view是ViewGroup。<br /> 子view被add在ViewGrop中,调用addView()---->addViewInne()时,会为子view赋值parent为this。
private void addViewInner(View child, int index, LayoutParams params,boolean preventRequestLayout) {addInArray(child, index);//此处省略N行.......if (preventRequestLayout) {child.assignParent(this);} else {child.mParent = this;}//此处省略N行.......}
1.2展开分析ViewRootImpl内部调用View的requestFocus()
requestFocus() ——> requestFocusNoSearch() ——->handleFocusGainInternal()
1.调用mParent.requestChildFocus()通知父控件,即将获取焦点。
2.通知其他部件,焦点即将发生变化。
3.通知回调。
4.强制布局更新绘制。
void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {mPrivateFlags |= PFLAG_FOCUSED;View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;if (mParent != null) {//此时调用view的parent的requestChildFocus的回调,//可重写RecyclerView的requestChildFocus做一些处理。mParent.requestChildFocus(this, this);updateFocusedInCluster(oldFocus, direction);}if (mAttachInfo != null) {//调用globalFocus回调mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);}onFocusChanged(true, direction, previouslyFocusedRect);refreshDrawableState();}}
1.3展开分析当mView没有找到focused时,ViewRootImp调用自身requestFocus
1.检查线程
2.判断mView是否为ViewGroup ——>mView是什么?????
3.使用FocusFinder寻找焦点 ——->后文会分析如何寻找焦点
public View focusSearch(View focused, int direction) {checkThread();if (!(mView instanceof ViewGroup)) {return null;}return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);}
2.ViewGroup内的dispatchKeyEvent方法
规则:
1.如果这个viewGroup持有焦点, 那么就会直接调用super.dispatchKeyEvent()
2.如果是它的子控件持有焦点, 那么就会调用子控件的view.dispatchKeyEvetn()
关于其分发策略的标记:
FOCUS_BLOCK_DESCENDANTS: 拦截焦点, 直接自己尝试获取焦点
FOCUS_BEFORE_DESCENDANTS: 首先自己尝试获取焦点, 如果自己不能获取焦点, 则尝试让子控件获取焦点
FOCUS_AFTER_DESCENDANTS: 首先尝试把焦点给子控件, 如果所有子控件都不要, 则自己尝试获取焦点
@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {//mInputEventConsistencyVerifier是调试用的,暂时不理会。if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onKeyEvent(event, 1);}if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))== (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {//如果viewgroup持有焦点,先调用其自身的dispacthKeyevent()if (super.dispatchKeyEvent(event)) {return true;}} else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)== PFLAG_HAS_BOUNDS) {//如果子view持有焦点,先将事件传给子view。if (mFocused.dispatchKeyEvent(event)) {return true;}}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);}return false;}
3.View内的dispatchKeyEvent方法
1.先处理当前view的onKey监听
2.再处理其他监听的回调
public boolean dispatchKeyEvent(KeyEvent event) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onKeyEvent(event, 0);}ListenerInfo li = mListenerInfo;//判断view是否注册了onKeyListener监听,先判断其返回值,若为true,则事件处理到此为止。if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {return true;}if (event.dispatch(this, mAttachInfo != null? mAttachInfo.mKeyDispatchState : null, this)) {return true;}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);}return false;}
处理其他的回调事件,判断是否被拦截处理。
public final boolean dispatch(Callback receiver, DispatcherState state,Object target) {switch (mAction) {case ACTION_DOWN: {mFlags &= ~FLAG_START_TRACKING;if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state+ ": " + this);boolean res = receiver.onKeyDown(mKeyCode, this);if (state != null) {if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {if (DEBUG) Log.v(TAG, " Start tracking!");state.startTracking(this, target);} else if (isLongPress() && state.isTracking(this)) {try {if (receiver.onKeyLongPress(mKeyCode, this)) {if (DEBUG) Log.v(TAG, " Clear from long press!");state.performedLongPress(this);res = true;}} catch (AbstractMethodError e) {}}}return res;}case ACTION_UP:if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state+ ": " + this);if (state != null) {state.handleUpEvent(this);}return receiver.onKeyUp(mKeyCode, this);case ACTION_MULTIPLE:final int count = mRepeatCount;final int code = mKeyCode;if (receiver.onKeyMultiple(code, count, this)) {return true;}if (code != KeyEvent.KEYCODE_UNKNOWN) {mAction = ACTION_DOWN;mRepeatCount = 0;boolean handled = receiver.onKeyDown(code, this);if (handled) {mAction = ACTION_UP;receiver.onKeyUp(code, this);}mAction = ACTION_MULTIPLE;mRepeatCount = count;return handled;}return false;}return false;}
4.FocusFinder
===实现根据给定的按键方向,通过已获取焦点的View,查找下一个获取焦点的view这样算法的类。
焦点没有被拦截的情况下,Android框架焦点的查找最终都是通过FocusFinder类来实现的。======
关键方法findNextFocus,通过给定的矩形坐标,寻找根视图的子view中可以获取focus的view
规则:
1.优先寻找用户在direction上已经指定获取focus的view。
如果有,则直接返回该view。如果不存在,则进入2.
2.把根root中所有可以获取focus的view添加到focusables列表中。
根root一般是viewGroup,则调用其addFocusablse,其会遍历所有child,调用child的addFocusable。
===== 这里有一个误区,认为会取direction方向上的view,实际上未以direction来确定添加=====
3.根据现有focused,所有可focus的focusables,寻找下一个合适的view
private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {View next = null;//若当前focus的不为空if (focused != null) {//优先一层一层寻找该用户已经指定的可获取焦点的view//执行当前focus的view的findUserSetNextFocus方法//如果该方法返回的View不为空,且isFocusable = true && isInTouchMode() = true的话。//FocusFinder找到的焦点就是findNextUserSpecifiedFocus()返回的View。next = findNextUserSpecifiedFocus(root, focused, direction);}if (next != null) {//若能找到,则直接返回。return next;}ArrayList<View> focusables = mTempList;try {//赋值后,先清空该对象的历史值focusables.clear();//添加任何可聚焦的view,这些view是root的子view(可能)//包括这个视图,如果它本身可以聚焦到视图。如果我们处于触摸模式,添加在触摸模式中也是可聚焦的视图。root.addFocusables(focusables, direction);if (!focusables.isEmpty()) {//根据root,当前focus的view,其坐标矩形,按键方向,所有可获取焦点的view,寻找下一个符合条件的viewnext = findNextFocus(root, focused, focusedRect, direction, focusables);}} finally {focusables.clear();}return next;}
4.1优先寻找用户在direction上已经指定获取focus的view
private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {View userSetNextFocus = focused.findUserSetNextFocus(root, direction);if (userSetNextFocus != null && userSetNextFocus.isFocusable()&& (!userSetNextFocus.isInTouchMode()|| userSetNextFocus.isFocusableInTouchMode())) {return userSetNextFocus;}return null;}
调用view中的findUserSetNextFocus()
View findUserSetNextFocus(View root, @FocusDirection 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;}
4.2寻找所有可focus的child中合适的view规则
具体没有获取指定view后,寻找该方向上,可获取view的findNextFocus方法
private 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);}}
5.小结
ViewRootImpl接收按键事件,并对其进行分发处理。
DecorView会调用dispatchKey逐层进行焦点的分发,若某个view的dispatchKeyEvent方法返回true,
则按键不再传递,焦点都不再继续处理。(可对其设置OnKeyListener监听,返true即可)
如果焦点没有被拦截的话,那么焦点就会交给系统来处理,还是会继续分发,直到找到那个获取焦点的View。
focusSearch内部其实是通过FocusFinder来查找焦点的。FocusFinder会优先通过View在XML布局设置的下一个焦点的ID来查找焦点。
若没有找到,又会根据按键的方向,执行focusSearch方法来寻找下一个将要获取焦点的View
最终如果找到将要获取焦点的View,就让其requestFocus。如果请求无效,将其放在onWindowFocusChanged()这个方法中去请求。这是在Activity寻找到焦点的时候。
