开发文章

Android焦点事件分发与传递机制

如果您对TouchEvent事件分发机制不太了解的,可以参考我的这篇文章——安卓TounchEvent事件分发机制。

问题:TV端焦点满天飞,如何解决和处理?

记得初入TV开发,以为很简单。TV的这些界面与布局太简单了,分分钟就可以把页面搭建出来,处理好,然后就没有然后了。。。。

下面我们就从源码来带大家进行安卓TV焦点事件的传递

这里先给出Android系统View的绘制流程:

依次执行View类里面的如下三个方法:

  • measure(int ,int) :测量View的大小
  • layout(int ,int ,int ,int) :设置子View的位置
  • draw(Canvas) :绘制View内容到Canvas画布上

View类.png

ViewRootImpl的主要作用如下(此处不多讲,如有意图,看源码):

  • A:链接WindowManager和DecorView的纽带,更广一点可以说是Window和View之间的纽带。

  • B:完成View的绘制过程,包括measure、layout、draw过程。

  • C:向DecorView分发收到的用户发起的event事件,如按键触屏等事件。

ViewRootImpl不再多余叙述,进入正题:

Android焦点分发的主要方法以及拦截方法的讲解。

在RootViewImpl中的函数通道是各种策略(InputStage)的组合,各策略负责的任务不同,如SyntheticInputStage、ViewPostImeInputStage、NativePostImeInputStage等等,这些策略以链表结构结构起来,当一个策略者没有消费事件时,就传递个下一个策略者。其中触摸和按键事件由ViewPostImeInputStage处理。

复制内容到剪贴板
  1. @Override  
  2.        protected int onProcess(QueuedInputEvent q) {  
  3.            if (q.mEvent instanceof KeyEvent) {  
  4.                return processKeyEvent(q);//如果是按键事件走此处,处理按键和焦点问题了  
  5.            } else {  
  6.                final int source = q.mEvent.getSource();  
  7.                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {  
  8.                    return processPointerEvent(q);//如果是触摸事件走此处  
  9.                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {  
  10.                    return processTrackballEvent(q);  
  11.                } else {  
  12.                    return processGenericMotionEvent(q);  
  13.                }  
  14.            }  
  15.        }  

processKeyEvent(QueuedInputEvent q)源码如下:

复制内容到剪贴板
  1. @Override  
  2.       protected void onDeliverToNext(QueuedInputEvent q) {  
  3.           if (mUnbufferedInputDispatch  
  4.                   && q.mEvent instanceof MotionEvent  
  5.                   && ((MotionEvent)q.mEvent).isTouchEvent()  
  6.                   && isTerminalInputEvent(q.mEvent)) {  
  7.               mUnbufferedInputDispatch = false;  
  8.               scheduleConsumeBatchedInput();  
  9.           }  
  10.           super.onDeliverToNext(q);  
  11.       }  
  12.   
  13.       private int processKeyEvent(QueuedInputEvent q) {  
  14.           final KeyEvent event = (KeyEvent)q.mEvent;  
  15.   
  16.           // Deliver the key to the view hierarchy.  
  17.           if (mView.dispatchKeyEvent(event)) {  
  18.               return FINISH_HANDLED;  
  19.           }  
  20.   
  21.           if (shouldDropInputEvent(q)) {  
  22.               return FINISH_NOT_HANDLED;  
  23.           }  
  24.   
  25.           // If the Control modifier is held, try to interpret the key as a shortcut.  
  26.           if (event.getAction() == KeyEvent.ACTION_DOWN  
  27.                   && event.isCtrlPressed()  
  28.                   && event.getRepeatCount() == 0  
  29.                   && !KeyEvent.isModifierKey(event.getKeyCode())) {  
  30.               if (mView.dispatchKeyShortcutEvent(event)) {  
  31.                   return FINISH_HANDLED;  
  32.               }  
  33.               if (shouldDropInputEvent(q)) {  
  34.                   return FINISH_NOT_HANDLED;  
  35.               }  
  36.           }  
  37.   
  38.           // Apply the fallback event policy.  
  39.           if (mFallbackEventHandler.dispatchKeyEvent(event)) {  
  40.               return FINISH_HANDLED;  
  41.           }  
  42.           if (shouldDropInputEvent(q)) {  
  43.               return FINISH_NOT_HANDLED;  
  44.           }  
  45.   
  46.           // Handle automatic focus changes.  
  47.           if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  48.               int direction = 0;  
  49.               switch (event.getKeyCode()) {  
  50.                   case KeyEvent.KEYCODE_DPAD_LEFT:  
  51.                       if (event.hasNoModifiers()) {  
  52.                           direction = View.FOCUS_LEFT;  
  53.                       }  
  54.                       break;  
  55.                   case KeyEvent.KEYCODE_DPAD_RIGHT:  
  56.                       if (event.hasNoModifiers()) {  
  57.                           direction = View.FOCUS_RIGHT;  
  58.                       }  
  59.                       break;  
  60.                   case KeyEvent.KEYCODE_DPAD_UP:  
  61.                       if (event.hasNoModifiers()) {  
  62.                           direction = View.FOCUS_UP;  
  63.                       }  
  64.                       break;  
  65.                   case KeyEvent.KEYCODE_DPAD_DOWN:  
  66.                       if (event.hasNoModifiers()) {  
  67.                           direction = View.FOCUS_DOWN;  
  68.                       }  
  69.                       break;  
  70.                   case KeyEvent.KEYCODE_TAB:  
  71.                       if (event.hasNoModifiers()) {  
  72.                           direction = View.FOCUS_FORWARD;  
  73.                       } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {  
  74.                           direction = View.FOCUS_BACKWARD;  
  75.                       }  
  76.                       break;  
  77.               }  
  78.               if (direction != 0) {  
  79.                   View focused = mView.findFocus();  
  80.                   if (focused != null) {  
  81.                       View v = focused.focusSearch(direction);  
  82.                       if (v != null && v != focused) {  
  83.                           // do the math the get the interesting rect  
  84.                           // of previous focused into the coord system of  
  85.                           // newly focused view  
  86.                           focused.getFocusedRect(mTempRect);  
  87.                           if (mView instanceof ViewGroup) {  
  88.                               ((ViewGroup) mView).offsetDescendantRectToMyCoords(  
  89.                                       focused, mTempRect);  
  90.                               ((ViewGroup) mView).offsetRectIntoDescendantCoords(  
  91.                                       v, mTempRect);  
  92.                           }  
  93.                           if (v.requestFocus(direction, mTempRect)) {  
  94.                               playSoundEffect(SoundEffectConstants  
  95.                                       .getContantForFocusDirection(direction));  
  96.                               return FINISH_HANDLED;  
  97.                           }  
  98.                       }  
  99.   
  100.                       // Give the focused view a last chance to handle the dpad key.  
  101.                       if (mView.dispatchUnhandledMove(focused, direction)) {  
  102.                           return FINISH_HANDLED;  
  103.                       }  
  104.                   } else {  
  105.                       // find the best view to give focus to in this non-touch-mode with no-focus  
  106.                       View v = focusSearch(null, direction);  
  107.                       if (v != null && v.requestFocus(direction)) {  
  108.                           return FINISH_HANDLED;  
  109.                       }  
  110.                   }  
  111.               }  
  112.           }  
  113.           return FORWARD;  
  114.       }  

 

进入源码讲解:

(1) 首先由dispatchKeyEvent进行焦点的分发

如果dispatchKeyEvent方法返回true,那么下面的焦点查找步骤就不会继续了。

dispatchKeyEvent方法返回true代表事件(包括焦点和按键)被消费了。

dispatchKeyEvent(event)如果不了解,看我上一篇文章安卓TounchEvent事件分发机制。

mView的dispatchKeyEvent方法,
mView是是Activity的顶层容器DecorView,它是一FrameLayout

所以这里的dispatchKeyEvent方法应该执行的是ViewGroup的dispatchKeyEvent()方法,而不是View的dispatchKeyEvent方法。

复制内容到剪贴板
  1. @Override  
  2.     public boolean dispatchKeyEvent(KeyEvent event) {  
  3.         if (mInputEventConsistencyVerifier != null) {  
  4.             mInputEventConsistencyVerifier.onKeyEvent(event, 1);  
  5.         }  
  6.   
  7.         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))  
  8.                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {  
  9.             if (super.dispatchKeyEvent(event)) {  
  10.                 return true;  
  11.             }  
  12.         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)  
  13.                 == PFLAG_HAS_BOUNDS) {  
  14.             if (mFocused.dispatchKeyEvent(event)) {  
  15.                 return true;  
  16.             }  
  17.         }  
  18.   
  19.         if (mInputEventConsistencyVerifier != null) {  
  20.             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);  
  21.         }  
  22.         return false;  
  23.     }  

ViewGroup的dispatchKeyEvent简略执行流程

首先ViewGroup会执行父类的dispatchKeyEvent方法,如果返回true那么父类的dispatchKeyEvent方法就会返回true,也就代表父类消费了该焦点事件,那么焦点事件自然就不会往下进行分发

然后ViewGroup会判断mFocused这个view是否为空如果为空就会****return false,焦点继续往下传递;如果不为空,那就会return mFocused的dispatchKeyEvent方法返回的结果。这个mFocused是ViewGroup中当前获取焦点的子View,这个可以从requestChildFocus方法中得到答案。

requestChildFocus()的源码如下:

复制内容到剪贴板
  1. @Override  
  2.   public void requestChildFocus(View child, View focused) {  
  3.       if (DBG) {  
  4.           System.out.println(this + " requestChildFocus()");  
  5.       }  
  6.       if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {  
  7.           return;  
  8.       }  
  9.   
  10.       // Unfocus us, if necessary  
  11.       super.unFocus(focused);  
  12.   
  13.       // We had a previous notion of who had focus. Clear it.  
  14.       if (mFocused != child) {  
  15.           if (mFocused != null) {  
  16.               mFocused.unFocus(focused);  
  17.           }  
  18.   
  19.           mFocused = child;  
  20.       }  
  21.       if (mParent != null) {  
  22.           mParent.requestChildFocus(this, focused);  
  23.       }  
  24.   }  

居然有这个彩蛋?

居然有这个彩蛋.png

View的dispatchKeyEvent简略执行流程

复制内容到剪贴板
  1. public boolean dispatchKeyEvent(KeyEvent event) {  
  2.         if (mInputEventConsistencyVerifier != null) {  
  3.             mInputEventConsistencyVerifier.onKeyEvent(event, 0);  
  4.         }  
  5.   
  6.         // Give any attached key listener a first crack at the event.  
  7.         //noinspection SimplifiableIfStatement  
  8.         ListenerInfo li = mListenerInfo;  
  9.         if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED  
  10.                 && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {  
  11.             return true;  
  12.         }  
  13.   
  14.         if (event.dispatch(this, mAttachInfo != null  
  15.                 ? mAttachInfo.mKeyDispatchState : nullthis)) {  
  16.             return true;  
  17.         }  
  18.   
  19.         if (mInputEventConsistencyVerifier != null) {  
  20.             mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);  
  21.         }  
  22.         return false;  
  23.     }  

要修改ViewGroup焦点事件的分发:

  • 重写view的dispatchKeyEvent方法
  • 给某个子view设置onKeyListener监听

焦点没有被dispatchKeyEvent拦截的情况下的继续代码中的处理过程,还是进入ViewRootImpl源码

复制内容到剪贴板
  1. // Handle automatic focus changes.  
  2.   if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  3.       int direction = 0;  
  4.       switch (event.getKeyCode()) {  
  5.           case KeyEvent.KEYCODE_DPAD_LEFT:  
  6.               if (event.hasNoModifiers()) {  
  7.                   direction = View.FOCUS_LEFT;  
  8.               }  
  9.               break;  
  10.           case KeyEvent.KEYCODE_DPAD_RIGHT:  
  11.               if (event.hasNoModifiers()) {  
  12.                   direction = View.FOCUS_RIGHT;  
  13.               }  
  14.               break;  
  15.           case KeyEvent.KEYCODE_DPAD_UP:  
  16.               if (event.hasNoModifiers()) {  
  17.                   direction = View.FOCUS_UP;  
  18.               }  
  19.               break;  
  20.           case KeyEvent.KEYCODE_DPAD_DOWN:  
  21.               if (event.hasNoModifiers()) {  
  22.                   direction = View.FOCUS_DOWN;  
  23.               }  
  24.               break;  
  25.           case KeyEvent.KEYCODE_TAB:  
  26.               if (event.hasNoModifiers()) {  
  27.                   direction = View.FOCUS_FORWARD;  
  28.               } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {  
  29.                   direction = View.FOCUS_BACKWARD;  
  30.               }  
  31.               break;  
  32.       }  
  33.       if (direction != 0) {  
  34.           View focused = mView.findFocus();  
  35.           if (focused != null) {  
  36.               View v = focused.focusSearch(direction);  
  37.               if (v != null && v != focused) {  
  38.                   // do the math the get the interesting rect  
  39.                   // of previous focused into the coord system of  
  40.                   // newly focused view  
  41.                   focused.getFocusedRect(mTempRect);  
  42.                   if (mView instanceof ViewGroup) {  
  43.                       ((ViewGroup) mView).offsetDescendantRectToMyCoords(  
  44.                               focused, mTempRect);  
  45.                       ((ViewGroup) mView).offsetRectIntoDescendantCoords(  
  46.                               v, mTempRect);  
  47.                   }  
  48.                   if (v.requestFocus(direction, mTempRect)) {  
  49.                       playSoundEffect(SoundEffectConstants  
  50.                               .getContantForFocusDirection(direction));  
  51.                       return FINISH_HANDLED;  
  52.                   }  
  53.               }  
  54.   
  55.               // Give the focused view a last chance to handle the dpad key.  
  56.               if (mView.dispatchUnhandledMove(focused, direction)) {  
  57.                   return FINISH_HANDLED;  
  58.               }  
  59.           } else {  
  60.               // find the best view to give focus to in this non-touch-mode with no-focus  
  61.               View v = focusSearch(null, direction);  
  62.               if (v != null && v.requestFocus(direction)) {  
  63.                   return FINISH_HANDLED;  
  64.               }  
  65.           }  
  66.       }  
  67.   }  

dispatchKeyEvent方法返回false后,先得到按键的方向direction一个int值。direction值是后面来进行焦点查找的。

接着会调用DecorView的findFocus()方法一层一层往下查找已经获取焦点的子View。

DecorView则是PhoneWindow类的一个内部类,继承于FrameLayout,由此可知它是一个ViewGroup。

那么,DecroView到底充当了什么样的角色呢?

其实,DecorView是整个ViewTree的最顶层View,它是一个FrameLayout布局,代表了整个应用的界面。在该布局下面,有标题view内容view这两个子元素。

复制内容到剪贴板
  1. @Override  
  2.    public View findFocus() {  
  3.        if (DBG) {  
  4.            System.out.println("Find focus in " + this + ": flags="  
  5.                    + isFocused() + ", child=" + mFocused);  
  6.        }  
  7.   
  8.        if (isFocused()) {  
  9.            return this;  
  10.        }  
  11.   
  12.        if (mFocused != null) {  
  13.            return mFocused.findFocus();  
  14.        }  
  15.        return null;  
  16.    }  

View的findFocus方法

复制内容到剪贴板
  1. /** 
  2.     * Find the view in the hierarchy rooted at this view that currently has 
  3.     * focus. 
  4.     * 
  5.     * @return The view that currently has focus, or null if no focused view can 
  6.     *         be found. 
  7.     */  
  8.    public View findFocus() {  
  9.        return (mPrivateFlags & PFLAG_FOCUSED) != 0 ? this : null;  
  10.    }  

 

View的hasFocus()方法和isFocused()方法对比

Stackoverflow解释来了:

hasFocus() is different from isFocused(). hasFocus() == true means that the View or one of its descendants is focused. If you look closely, there’s a chain of hasFocused Views till you reach the View that isFocused.

复制内容到剪贴板
  1. /** 
  2.     * Returns true if this view has focus itself, or is the ancestor of the 
  3.     * view that has focus. 
  4.     * 
  5.     * @return True if this view has or contains focus, false otherwise. 
  6.     */  
  7.    @ViewDebug.ExportedProperty(category = "focus")  
  8.    public boolean hasFocus() {  
  9.        return (mPrivateFlags & PFLAG_FOCUSED) != 0;  
  10.    }  
  11.   
  12.  /** 
  13.     * Returns true if this view has focus 
  14.     * 
  15.     * @return True if this view has focus, false otherwise. 
  16.     */  
  17.    @ViewDebug.ExportedProperty(category = "focus")  
  18.    public boolean isFocused() {  
  19.        return (mPrivateFlags & PFLAG_FOCUSED) != 0;  
  20.    }  

 

接着,如果mView.findFocus()方法返回的mFocused不为空,说明找到了当前获取焦点的view(mFocused),接着focusSearch会把direction(遥控器按键按下的方向)作为参数,找到特定方向下一个将要获取焦点的view,最后如果该view不为空,那么就让该view获取焦点。

我们来看一下focusSearch方法的源码以及具体实现。

复制内容到剪贴板
  1. @Override  
  2.    public View focusSearch(View focused, int direction) {  
  3.        if (isRootNamespace()) {  
  4.            // root namespace means we should consider ourselves the top of the  
  5.            // tree for focus searching; otherwise we could be focus searching  
  6.            // into other tabs.  see LocalActivityManager and TabHost for more info  
  7.            return FocusFinder.getInstance().findNextFocus(this, focused, direction);  
  8.        } else if (mParent != null) {  
  9.            return mParent.focusSearch(focused, direction);  
  10.        }  
  11.        return null;  
  12.    }  

 

focusSearch其实是一层一层地网上调用父View的focusSearch方法,直到当前view是根布局(isRootNamespace()方法),通过注释可以知道focusSearch最终会调用DecorView的focusSearch方法。而DecorView的focusSearch方法找到的焦点view是通过FocusFinder来找到的。

FocusFinder是什么?

根据给定的按键方向,通过当前的获取焦点的View,查找下一个获取焦点的view这样算法的类。焦点没有被拦截的情况下,Android焦点的查找最终都是通过FocusFinder类来实现的。

FocusFinder类来实现.png

FocusFinder是如何通过findNextFocus方法寻找焦点的?

复制内容到剪贴板
  1. public final View findNextFocus(ViewGroup root, View focused, int direction) {  
  2.         return findNextFocus(root, focused, null, direction);  
  3.     }  
  4.   
  5.      private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {  
  6.         View next = null;  
  7.         if (focused != null) {  
  8.             next = findNextUserSpecifiedFocus(root, focused, direction);  
  9.         }  
  10.         if (next != null) {  
  11.             return next;  
  12.         }  
  13.         ArrayList<View> focusables = mTempList;  
  14.         try {  
  15.             focusables.clear();  
  16.             root.addFocusables(focusables, direction);  
  17.             if (!focusables.isEmpty()) {  
  18.                 next = findNextFocus(root, focused, focusedRect, direction, focusables);  
  19.             }  
  20.         } finally {  
  21.             focusables.clear();  
  22.         }  
  23.         return next;  
  24.     }  
  25.   
  26. private View findNextFocus(ViewGroup root, View focused, Rect focusedRect,  
  27.             int direction, ArrayList<View> focusables) {  
  28.         if (focused != null) {  
  29.             if (focusedRect == null) {  
  30.                 focusedRect = mFocusedRect;  
  31.             }  
  32.             // fill in interesting rect from focused  
  33.             focused.getFocusedRect(focusedRect);  
  34.             root.offsetDescendantRectToMyCoords(focused, focusedRect);  
  35.         } else {  
  36.             if (focusedRect == null) {  
  37.                 focusedRect = mFocusedRect;  
  38.                 // make up a rect at top left or bottom right of root  
  39.                 switch (direction) {  
  40.                     case View.FOCUS_RIGHT:  
  41.                     case View.FOCUS_DOWN:  
  42.                         setFocusTopLeft(root, focusedRect);  
  43.                         break;  
  44.                     case View.FOCUS_FORWARD:  
  45.                         if (root.isLayoutRtl()) {  
  46.                             setFocusBottomRight(root, focusedRect);  
  47.                         } else {  
  48.                             setFocusTopLeft(root, focusedRect);  
  49.                         }  
  50.                         break;  
  51.   
  52.                     case View.FOCUS_LEFT:  
  53.                     case View.FOCUS_UP:  
  54.                         setFocusBottomRight(root, focusedRect);  
  55.                         break;  
  56.                     case View.FOCUS_BACKWARD:  
  57.                         if (root.isLayoutRtl()) {  
  58.                             setFocusTopLeft(root, focusedRect);  
  59.                         } else {  
  60.                             setFocusBottomRight(root, focusedRect);  
  61.                         break;  
  62.                     }  
  63.                 }  
  64.             }  
  65.         }  
  66.   
  67.   
  68.     private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {  
  69.         // check for user specified next focus  
  70.         View userSetNextFocus = focused.findUserSetNextFocus(root, direction);  
  71.         if (userSetNextFocus != null && userSetNextFocus.isFocusable()  
  72.                 && (!userSetNextFocus.isInTouchMode()  
  73.                         || userSetNextFocus.isFocusableInTouchMode())) {  
  74.             return userSetNextFocus;  
  75.         }  
  76.         return null;  
  77.     }  

 

FocusFinder类通过findNextFocus来找焦点的。一层一层往寻找,后面会执行findNextUserSpecifiedFocus()方法,这个方法会执行focused(即当前获取焦点的View)的findUserSetNextFocus方法,如果该方法返回的View不为空,
且isFocusable = true && isInTouchMode() = true的话。

FocusFinder找到的焦点就是findNextUserSpecifiedFocus()返回的View。

findNextFocus会优先根据XML里设置的下一个将获取焦点的View的ID值来寻找将要获取焦点的View。

复制内容到剪贴板
  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(thisnew Predicate<View>() {  
  22.                     @Override  
  23.                     public boolean apply(View t) {  
  24.                         return t.mNextFocusForwardId == id;  
  25.                     }  
  26.                 });  
  27.             }  
  28.         }  
  29.         return null;  
  30.     }  

 

焦点事件分发步骤:

  • DecorView会调用dispatchKey一层一层进行焦点的分发,如果dispatchKeyEvent方法返回true的话,那么焦点或者按键事件就不会往下分发了。

  • 如果你想拦截某个子View,对其设置OnKeyListener进行焦点的拦截。

  • 如果焦点没有被拦截的话,那么焦点就会交给系统来处理,还是会继续分发,直到找到那个获取焦点的View

  • Android底层先会记录按键的方向,后面DecorView会一层一层往下调用findFocus方法找到当前获取焦点的View

  • 后面系统又会根据按键的方向,执行focusSearch方法来寻找下一个将要获取焦点的View

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

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

我的前一篇文章,主要是介绍了TouchEvent事件分发机制,省略了焦点分发传递机制的代码,这篇文章与此相反。如果将两个结合起来,太繁杂,冗长了。分开反而有利于您的理解。至此,事件分发机制,你也了解的差不多了,給个粉吧!

感谢 单灿灿 支持 磐实编程网 原文地址:
blog.csdn.net/zhcswlp0625/article/details/69338597

文章信息

发布时间:2017-04-09

作者:单灿灿

发布者:aquwcw

浏览次数: