焦點處理相關記錄以下所涉及的焦點部分,只是按鍵移動部分,不明確包含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.java private 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.java private final class DecorView extends FrameLayout implements RootVie.... { ... }<br> //FrameLayout.java public class FrameLayout extends ViewGroup { ... }<br> //ViewGroup.java //mFocused記錄的是當前被焦點選中的view @Override public 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.java public 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 focus View 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>() { @Override public 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 focused focused.getFocusedRect(focusedRect); root.offsetDescendantRectToMyCoords(focused, focusedRect); } else { if (focusedRect == null) { focusedRect = mFocusedRect; // make up a rect at top left or bottom right of root switch (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