【本文是以Android sdk 25源码分析】

1.ViewRootImpl

1.连接WindowManager和DecorView的纽带
2.完成view的measure,layout,draw
3.向DecorView分发按键、触摸事件等。
关于按键事件和焦点寻找:
先判断是否有按键事件处理
1.若返回true,则打断该方向上的焦点寻找。
2.若返回fasle,则根据指定的方向寻找最近且可获取焦点的view
2.1判断view的类型,是否为ViewGroup。

  1. final class ViewPostImeInputStage extends InputStage {
  2. public ViewPostImeInputStage(InputStage next) {
  3. super(next);
  4. }
  5. @Override
  6. protected int onProcess(QueuedInputEvent q) {
  7. if (q.mEvent instanceof KeyEvent) {
  8. return processKeyEvent(q); //若是按键事件,则走该方法处理按键和焦点
  9. } else {
  10. final int source = q.mEvent.getSource();
  11. if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
  12. return processPointerEvent(q); //若是触摸事件,则走该方法处理
  13. } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
  14. return processTrackballEvent(q);
  15. } else {
  16. return processGenericMotionEvent(q);
  17. }
  18. }
  19. }
  20. @Override
  21. protected void onDeliverToNext(QueuedInputEvent q) {
  22. //此处不分析,省略N行...........
  23. }
  24. private int processKeyEvent(QueuedInputEvent q) {
  25. final KeyEvent event = (KeyEvent)q.mEvent;
  26. //首先由dispatchKeyEvent进行分发,如果返回true的,则不再继续。
  27. //1.此处的mView是DecorView,即是个ViewGroup对象,则调用的是其dispatchKeyEvent方法
  28. //2.若未被处理,则以下流程,比如,按上下键,则需要寻找下一个焦点等等
  29. if (mView.dispatchKeyEvent(event)) {
  30. return FINISH_HANDLED;
  31. }
  32. if (shouldDropInputEvent(q)) {
  33. return FINISH_NOT_HANDLED;
  34. }
  35. // If the Control modifier is held, try to interpret the key as a shortcut.
  36. if (event.getAction() == KeyEvent.ACTION_DOWN
  37. && event.isCtrlPressed()
  38. && event.getRepeatCount() == 0
  39. && !KeyEvent.isModifierKey(event.getKeyCode())) {
  40. //首先会将事件进行分发,如果没有被消费,则开始寻找下一个可获取焦点的view。
  41. //如果被消费,则不再继续。
  42. if (mView.dispatchKeyShortcutEvent(event)) {
  43. return FINISH_HANDLED;
  44. }
  45. if (shouldDropInputEvent(q)) {
  46. return FINISH_NOT_HANDLED;
  47. }
  48. }
  49. //若未被子view拦截,开始处理按键,根据direction进行处理
  50. if (event.getAction() == KeyEvent.ACTION_DOWN) {
  51. int direction = 0;
  52. switch (event.getKeyCode()) {
  53. //根据code设置direction的值...............
  54. }
  55. if (direction != 0) {
  56. //在根视图层中寻找当前有焦点的view
  57. View focused = mView.findFocus();
  58. if (focused != null) {
  59. ========= 1. 展开分析focused.focusSearch() ===========
  60. View v = focused.focusSearch(direction);
  61. if (v != null && v != focused) ;
  62. focused.getFocusedRect(mTempRect);
  63. //若是ViewGroup类型,则当前被聚焦的view,计算出一个坐标矩形。并将其赋值给可聚焦view
  64. if (mView instanceof ViewGroup) {
  65. ((ViewGroup) mView).offsetDescendantRectToMyCoords(
  66. focused, mTempRect);
  67. ((ViewGroup) mView).offsetRectIntoDescendantCoords(
  68. v, mTempRect);
  69. }
  70. ========= 2.此处下面展开分析 =========
  71. if (v.requestFocus(direction, mTempRect)) {
  72. playSoundEffect(SoundEffectConstants
  73. .getContantForFocusDirection(direction));
  74. return FINISH_HANDLED;
  75. }
  76. }
  77. if (mView.dispatchUnhandledMove(focused, direction)) {
  78. return FINISH_HANDLED;
  79. }
  80. } else {
  81. ========= 3.如果focusednull,以下展开分析 ===========
  82. View v = focusSearch(null, direction);
  83. if (v != null && v.requestFocus(direction)) {
  84. return FINISH_HANDLED;
  85. }
  86. }
  87. }
  88. }
  89. return FORWARD;

1.1展开分析ViewRootImpl内部的focusSearch(),调用了View.requestFocus()

作用:1.寻找指定方向上的view
2.判断是否有mParent,即其父view。

  1. public View focusSearch(@FocusRealDirection int direction) {
  2. if (mParent != null) {
  3. return mParent.focusSearch(this, direction);
  4. } else {
  5. return null;
  6. }
  7. }
  1. 关于mParent的由来,如果view存在mParent,则其父viewViewGroup。<br /> viewaddViewGrop中,调用addView()---->addViewInne()时,会为子view赋值parentthis
  1. private void addViewInner(View child, int index, LayoutParams params,
  2. boolean preventRequestLayout) {
  3. addInArray(child, index);
  4. //此处省略N行.......
  5. if (preventRequestLayout) {
  6. child.assignParent(this);
  7. } else {
  8. child.mParent = this;
  9. }
  10. //此处省略N行.......
  11. }

1.2展开分析ViewRootImpl内部调用View的requestFocus()

requestFocus() ——> requestFocusNoSearch() ——->handleFocusGainInternal()
1.调用mParent.requestChildFocus()通知父控件,即将获取焦点。
2.通知其他部件,焦点即将发生变化。
3.通知回调。
4.强制布局更新绘制。

  1. void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
  2. if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
  3. mPrivateFlags |= PFLAG_FOCUSED;
  4. View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
  5. if (mParent != null) {
  6. //此时调用view的parent的requestChildFocus的回调,
  7. //可重写RecyclerView的requestChildFocus做一些处理。
  8. mParent.requestChildFocus(this, this);
  9. updateFocusedInCluster(oldFocus, direction);
  10. }
  11. if (mAttachInfo != null) {
  12. //调用globalFocus回调
  13. mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
  14. }
  15. onFocusChanged(true, direction, previouslyFocusedRect);
  16. refreshDrawableState();
  17. }
  18. }

1.3展开分析当mView没有找到focused时,ViewRootImp调用自身requestFocus

1.检查线程
2.判断mView是否为ViewGroup ——>mView是什么?????
3.使用FocusFinder寻找焦点 ——->后文会分析如何寻找焦点

  1. public View focusSearch(View focused, int direction) {
  2. checkThread();
  3. if (!(mView instanceof ViewGroup)) {
  4. return null;
  5. }
  6. return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
  7. }

2.ViewGroup内的dispatchKeyEvent方法

规则:
1.如果这个viewGroup持有焦点, 那么就会直接调用super.dispatchKeyEvent()
2.如果是它的子控件持有焦点, 那么就会调用子控件的view.dispatchKeyEvetn()
关于其分发策略的标记:

  1. FOCUS_BLOCK_DESCENDANTS: 拦截焦点, 直接自己尝试获取焦点

  2. FOCUS_BEFORE_DESCENDANTS: 首先自己尝试获取焦点, 如果自己不能获取焦点, 则尝试让子控件获取焦点

  3. FOCUS_AFTER_DESCENDANTS: 首先尝试把焦点给子控件, 如果所有子控件都不要, 则自己尝试获取焦点

  1. @Override
  2. public boolean dispatchKeyEvent(KeyEvent event) {
  3. //mInputEventConsistencyVerifier是调试用的,暂时不理会。
  4. if (mInputEventConsistencyVerifier != null) {
  5. mInputEventConsistencyVerifier.onKeyEvent(event, 1);
  6. }
  7. if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
  8. == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
  9. //如果viewgroup持有焦点,先调用其自身的dispacthKeyevent()
  10. if (super.dispatchKeyEvent(event)) {
  11. return true;
  12. }
  13. } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
  14. == PFLAG_HAS_BOUNDS) {
  15. //如果子view持有焦点,先将事件传给子view。
  16. if (mFocused.dispatchKeyEvent(event)) {
  17. return true;
  18. }
  19. }
  20. if (mInputEventConsistencyVerifier != null) {
  21. mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
  22. }
  23. return false;
  24. }

3.View内的dispatchKeyEvent方法

1.先处理当前view的onKey监听
2.再处理其他监听的回调

  1. public boolean dispatchKeyEvent(KeyEvent event) {
  2. if (mInputEventConsistencyVerifier != null) {
  3. mInputEventConsistencyVerifier.onKeyEvent(event, 0);
  4. }
  5. ListenerInfo li = mListenerInfo;
  6. //判断view是否注册了onKeyListener监听,先判断其返回值,若为true,则事件处理到此为止。
  7. if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
  8. && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
  9. return true;
  10. }
  11. if (event.dispatch(this, mAttachInfo != null
  12. ? mAttachInfo.mKeyDispatchState : null, this)) {
  13. return true;
  14. }
  15. if (mInputEventConsistencyVerifier != null) {
  16. mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
  17. }
  18. return false;
  19. }

处理其他的回调事件,判断是否被拦截处理。

  1. public final boolean dispatch(Callback receiver, DispatcherState state,
  2. Object target) {
  3. switch (mAction) {
  4. case ACTION_DOWN: {
  5. mFlags &= ~FLAG_START_TRACKING;
  6. if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
  7. + ": " + this);
  8. boolean res = receiver.onKeyDown(mKeyCode, this);
  9. if (state != null) {
  10. if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
  11. if (DEBUG) Log.v(TAG, " Start tracking!");
  12. state.startTracking(this, target);
  13. } else if (isLongPress() && state.isTracking(this)) {
  14. try {
  15. if (receiver.onKeyLongPress(mKeyCode, this)) {
  16. if (DEBUG) Log.v(TAG, " Clear from long press!");
  17. state.performedLongPress(this);
  18. res = true;
  19. }
  20. } catch (AbstractMethodError e) {
  21. }
  22. }
  23. }
  24. return res;
  25. }
  26. case ACTION_UP:
  27. if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
  28. + ": " + this);
  29. if (state != null) {
  30. state.handleUpEvent(this);
  31. }
  32. return receiver.onKeyUp(mKeyCode, this);
  33. case ACTION_MULTIPLE:
  34. final int count = mRepeatCount;
  35. final int code = mKeyCode;
  36. if (receiver.onKeyMultiple(code, count, this)) {
  37. return true;
  38. }
  39. if (code != KeyEvent.KEYCODE_UNKNOWN) {
  40. mAction = ACTION_DOWN;
  41. mRepeatCount = 0;
  42. boolean handled = receiver.onKeyDown(code, this);
  43. if (handled) {
  44. mAction = ACTION_UP;
  45. receiver.onKeyUp(code, this);
  46. }
  47. mAction = ACTION_MULTIPLE;
  48. mRepeatCount = count;
  49. return handled;
  50. }
  51. return false;
  52. }
  53. return false;
  54. }

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

  1. private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
  2. View next = null;
  3. //若当前focus的不为空
  4. if (focused != null) {
  5. //优先一层一层寻找该用户已经指定的可获取焦点的view
  6. //执行当前focus的view的findUserSetNextFocus方法
  7. //如果该方法返回的View不为空,且isFocusable = true && isInTouchMode() = true的话。
  8. //FocusFinder找到的焦点就是findNextUserSpecifiedFocus()返回的View。
  9. next = findNextUserSpecifiedFocus(root, focused, direction);
  10. }
  11. if (next != null) {
  12. //若能找到,则直接返回。
  13. return next;
  14. }
  15. ArrayList<View> focusables = mTempList;
  16. try {
  17. //赋值后,先清空该对象的历史值
  18. focusables.clear();
  19. //添加任何可聚焦的view,这些view是root的子view(可能)
  20. //包括这个视图,如果它本身可以聚焦到视图。如果我们处于触摸模式,添加在触摸模式中也是可聚焦的视图。
  21. root.addFocusables(focusables, direction);
  22. if (!focusables.isEmpty()) {
  23. //根据root,当前focus的view,其坐标矩形,按键方向,所有可获取焦点的view,寻找下一个符合条件的view
  24. next = findNextFocus(root, focused, focusedRect, direction, focusables);
  25. }
  26. } finally {
  27. focusables.clear();
  28. }
  29. return next;
  30. }

4.1优先寻找用户在direction上已经指定获取focus的view

  1. private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {
  2. View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
  3. if (userSetNextFocus != null && userSetNextFocus.isFocusable()
  4. && (!userSetNextFocus.isInTouchMode()
  5. || userSetNextFocus.isFocusableInTouchMode())) {
  6. return userSetNextFocus;
  7. }
  8. return null;
  9. }

调用view中的findUserSetNextFocus()

  1. View findUserSetNextFocus(View root, @FocusDirection int direction) {
  2. switch (direction) {
  3. case FOCUS_LEFT:
  4. if (mNextFocusLeftId == View.NO_ID) return null;
  5. return findViewInsideOutShouldExist(root, mNextFocusLeftId);
  6. case FOCUS_RIGHT:
  7. if (mNextFocusRightId == View.NO_ID) return null;
  8. return findViewInsideOutShouldExist(root, mNextFocusRightId);
  9. case FOCUS_UP:
  10. if (mNextFocusUpId == View.NO_ID) return null;
  11. return findViewInsideOutShouldExist(root, mNextFocusUpId);
  12. case FOCUS_DOWN:
  13. if (mNextFocusDownId == View.NO_ID) return null;
  14. return findViewInsideOutShouldExist(root, mNextFocusDownId);
  15. case FOCUS_FORWARD:
  16. if (mNextFocusForwardId == View.NO_ID) return null;
  17. return findViewInsideOutShouldExist(root, mNextFocusForwardId);
  18. case FOCUS_BACKWARD: {
  19. if (mID == View.NO_ID) return null;
  20. final int id = mID;
  21. return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
  22. @Override
  23. public boolean apply(View t) {
  24. return t.mNextFocusForwardId == id;
  25. }
  26. });
  27. }
  28. }
  29. return null;
  30. }

4.2寻找所有可focus的child中合适的view规则

具体没有获取指定view后,寻找该方向上,可获取view的findNextFocus方法

  1. private View findNextFocus(ViewGroup root, View focused, Rect focusedRect,
  2. int direction, ArrayList<View> focusables) {
  3. if (focused != null) {
  4. if (focusedRect == null) {
  5. focusedRect = mFocusedRect;
  6. }
  7. // fill in interesting rect from focused
  8. focused.getFocusedRect(focusedRect);
  9. root.offsetDescendantRectToMyCoords(focused, focusedRect);
  10. } else {
  11. if (focusedRect == null) {
  12. focusedRect = mFocusedRect;
  13. // make up a rect at top left or bottom right of root
  14. switch (direction) {
  15. case View.FOCUS_RIGHT:
  16. case View.FOCUS_DOWN:
  17. setFocusTopLeft(root, focusedRect);
  18. break;
  19. case View.FOCUS_FORWARD:
  20. if (root.isLayoutRtl()) {
  21. setFocusBottomRight(root, focusedRect);
  22. } else {
  23. setFocusTopLeft(root, focusedRect);
  24. }
  25. break;
  26. case View.FOCUS_LEFT:
  27. case View.FOCUS_UP:
  28. setFocusBottomRight(root, focusedRect);
  29. break;
  30. case View.FOCUS_BACKWARD:
  31. if (root.isLayoutRtl()) {
  32. setFocusTopLeft(root, focusedRect);
  33. } else {
  34. setFocusBottomRight(root, focusedRect);
  35. break;
  36. }
  37. }
  38. }
  39. }
  40. switch (direction) {
  41. case View.FOCUS_FORWARD:
  42. case View.FOCUS_BACKWARD:
  43. return findNextFocusInRelativeDirection(focusables, root, focused, focusedRect,
  44. direction);
  45. case View.FOCUS_UP:
  46. case View.FOCUS_DOWN:
  47. case View.FOCUS_LEFT:
  48. case View.FOCUS_RIGHT:
  49. return findNextFocusInAbsoluteDirection(focusables, root, focused,
  50. focusedRect, direction);
  51. default:
  52. throw new IllegalArgumentException("Unknown direction: " + direction);
  53. }
  54. }

5.小结

  • ViewRootImpl接收按键事件,并对其进行分发处理。

  • DecorView会调用dispatchKey逐层进行焦点的分发,若某个view的dispatchKeyEvent方法返回true,

  1. 则按键不再传递,焦点都不再继续处理。(可对其设置OnKeyListener监听,返true即可)
  • 如果焦点没有被拦截的话,那么焦点就会交给系统来处理,还是会继续分发,直到找到那个获取焦点的View。

  • focusSearch内部其实是通过FocusFinder来查找焦点的。FocusFinder会优先通过View在XML布局设置的下一个焦点的ID来查找焦点。

  • 若没有找到,又会根据按键的方向,执行focusSearch方法来寻找下一个将要获取焦点的View

  • 最终如果找到将要获取焦点的View,就让其requestFocus。如果请求无效,将其放在onWindowFocusChanged()这个方法中去请求。这是在Activity寻找到焦点的时候。