View中 /** * Dispatch a key event to the next view on the focus path. This path runs * from the top of the view tree down to the currently focused view. If this * view has focus, it will dispatch to itself. Otherwise it will dispatch * the next node down the focus path. This method also fires any key * listeners. * * @param event The key event to be dispatched. * @return True if the event was handled, false otherwise. */ publicbooleandispatchKeyEvent(KeyEvent event)
ViewRootImpl中 privatebooleanperformFocusNavigation(KeyEvent event){ 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; case KeyEvent.KEYCODE_DPAD_UP: if (event.hasNoModifiers()) { direction = View.FOCUS_UP; } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (event.hasNoModifiers()) { direction = View.FOCUS_DOWN; } break; case KeyEvent.KEYCODE_TAB: if (event.hasNoModifiers()) { direction = View.FOCUS_FORWARD; } elseif (event.hasModifiers(KeyEvent.META_SHIFT_ON)) { direction = View.FOCUS_BACKWARD; } break; } if (direction != 0) { View focused = mView.findFocus();//找出此时这个有焦点的View if (focused != null) { //调用它的focusSearch方法,顾名思义寻找这个方向上的下一个获得焦点的View View v = focused.focusSearch(direction); if (v != null && v != focused) { // do the math the get the interesting rect // of previous focused into the coord system of // newly focused view focused.getFocusedRect(mTempRect); if (mView instanceof ViewGroup) { ((ViewGroup) mView).offsetDescendantRectToMyCoords( focused, mTempRect); ((ViewGroup) mView).offsetRectIntoDescendantCoords( v, mTempRect); } //调用它的requestFocus,让它获得焦点 if (v.requestFocus(direction, mTempRect)) { playSoundEffect(SoundEffectConstants .getContantForFocusDirection(direction)); returntrue; } }
// Give the focused view a last chance to handle the dpad key. if (mView.dispatchUnhandledMove(focused, direction)) { returntrue; } } else { if (mView.restoreDefaultFocus()) { returntrue; } } } returnfalse; }
View中 public View focusSearch(@FocusRealDirectionint direction){ if (mParent != null) { return mParent.focusSearch(this, direction); } else { returnnull; } }
直接调用了它的父View的方法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
ViewGroup中 @Override public View focusSearch(View focused, int direction){ if (isRootNamespace()) { // root namespace means we should consider ourselves the top of the // tree for focus searching; otherwise we could be focus searching // into other tabs. see LocalActivityManager and TabHost for more info. return FocusFinder.getInstance().findNextFocus(this, focused, direction); } elseif (mParent != null) { return mParent.focusSearch(focused, direction); } returnnull; }
if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {//拦截了焦点,只判断、添加自己 if (focusSelf) { super.addFocusables(views, direction, focusableMode); } return; }
if (blockFocusForTouchscreen) { focusableMode |= FOCUSABLES_TOUCH_MODE; } //在所有子View之前添加自己到views if ((descendantFocusability == FOCUS_BEFORE_DESCENDANTS) && focusSelf) { super.addFocusables(views, direction, focusableMode); }
int count = 0; final View[] children = new View[mChildrenCount]; //挑出可见的View for (int i = 0; i < mChildrenCount; ++i) { View child = mChildren[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { children[count++] = child; } } //对所有子View排序 FocusFinder.sort(children, 0, count, this, isLayoutRtl()); for (int i = 0; i < count; ++i) {//把所有子View按顺序添加到views children[i].addFocusables(views, direction, focusableMode); }
// When set to FOCUS_AFTER_DESCENDANTS, we only add ourselves if // there aren't any focusable descendants. this is // to avoid the focus search finding layouts when a more precise search // among the focusable children would be more interesting. if ((descendantFocusability == FOCUS_AFTER_DESCENDANTS) && focusSelf && focusableCount == views.size()) { super.addFocusables(views, direction, focusableMode); } }
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 focused focused.getFocusedRect(focusedRect); //将focused的坐标变成rootView下的坐标 root.offsetDescendantRectToMyCoords(focused, focusedRect); } else { ...... }
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: thrownew IllegalArgumentException("Unknown direction: " + direction); } } ``` 到这里,寻找的才与方向有关,下面的分析以按右键为例,对应的是View.FOCUS_RIGHT,调用了findNextFocusInAbsoluteDirection方法: ```java View findNextFocusInAbsoluteDirection(ArrayList<View> focusables, ViewGroup root, View focused, Rect focusedRect, int direction){ // initialize the best candidate to something impossible // (so the first plausible view will become the best choice) mBestCandidateRect.set(focusedRect); switch(direction) { case View.FOCUS_LEFT: mBestCandidateRect.offset(focusedRect.width() + 1, 0); break; case View.FOCUS_RIGHT://向右寻找时,将初始的位置设为当前View的最左边 mBestCandidateRect.offset(-(focusedRect.width() + 1), 0); break; case View.FOCUS_UP: mBestCandidateRect.offset(0, focusedRect.height() + 1); break; case View.FOCUS_DOWN: mBestCandidateRect.offset(0, -(focusedRect.height() + 1)); }
View closest = null;
int numFocusables = focusables.size(); //遍历所有的可获得焦点的View for (int i = 0; i < numFocusables; i++) { View focusable = focusables.get(i); //排除自己 // only interested in other non-root views if (focusable == focused || focusable == root) continue; //获得这个View的rect,并把它调整到和focusedRect一致 // get focus bounds of other view in same coordinate system focusable.getFocusedRect(mOtherRect); root.offsetDescendantRectToMyCoords(focusable, mOtherRect); //判断这个View是不是比mBestCandidateRect更优 if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) { //更优,那么将它设置成mBestCandidateRect,并将closest赋值 mBestCandidateRect.set(mOtherRect); closest = focusable; } } return closest; }