-
FocusFinder類的實(shí)例是依附于線程的,每個線程一個實(shí)例。
private static final ThreadLocal<FocusFinder> tlFocusFinder = new ThreadLocal<FocusFinder>() { @Override protected FocusFinder initialValue() { return new FocusFinder(); } }; public final View findNextFocus(ViewGroup root, View focused, int direction)
public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction)-
先從
findNextFocus()入手private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) { //流程一: View next = null; //1.如果當(dāng)前的焦點(diǎn)view不為空,則尋找用戶指定的下一個焦點(diǎn) if (focused != null) { next = findNextUserSpecifiedFocus(root, focused, direction); } //如果找到,直接返回用戶指定的焦點(diǎn) if (next != null) { return next; } //流程二: ArrayList<View> focusables = mTempList; try { focusables.clear(); //2.把當(dāng)前root下的所有的可以獲得焦點(diǎn)的view加入列表 root.addFocusables(focusables, direction); if (!focusables.isEmpty()) { //3.繼續(xù)尋找當(dāng)前root下的焦點(diǎn) next = findNextFocus(root, focused, focusedRect, direction, focusables); } } finally { focusables.clear(); } return next; }
流程一:
findNextUserSpecifiedFocus()//分析流程一: //----------------------FocusFinder------------------------- //1.FocusFinder findUserSetNextFocus()找到用戶指定的下一個焦點(diǎn) private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) { // check for user specified next View userSetNextFocus = focused.findUserSetNextFocus(root, direction); if (userSetNextFocus != null && userSetNextFocus.isFocusable() && (!userSetNextFocus.isInTouchMode() || userSetNextFocus.isFocusableInTouchMode())) { return userSetNextFocus; } return null; } //----------------------focused--view------------------------- //2.進(jìn)入focused View里面,findViewInsideOutShouldExist() 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: ... ... } return null; } //3. private View findViewInsideOutShouldExist(View root, int id) { if (mMatchIdPredicate == null) { //Predicate<View> mMatchIdPredicate = new MatchIdPredicate(); } //id == 要尋找的下一個焦點(diǎn)的view的id mMatchIdPredicate.mId = id; //Note:this == focusedView View result = root.findViewByPredicateInsideOut(this, mMatchIdPredicate); if (result == null) { Log.w(VIEW_LOG_TAG, "couldn't find view with id " + id); } return result; } //----------------------root--view------------------------ //4.start == focusedView是獲得焦點(diǎn)的view public final View findViewByPredicateInsideOut(View start, Predicate<View> predicate) { View childToSkip = null; for (;;) { //比對一下start是不是要尋找的上面的id的view,如果是則返回start View view = start.findViewByPredicateTraversal(predicate, childToSkip); //如果找到了view,或者start已經(jīng)是root view了,則返回 if (view != null || start == this) { return view; } //如果沒有找到,則一層層找到start的父view繼續(xù)比較 ViewParent parent = start.getParent(); if (parent == null || !(parent instanceof View)) { return null; } childToSkip = start; start = (View) parent; } } //5.就是比對start是不是上面要尋找的id的view protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { if (predicate.apply(this)) { return this; } return null; } //總結(jié): 1.如果mNextFocusLeftId == View.NO_ID 即用戶沒有指定下一個焦點(diǎn)直接返回null 2.如果用戶指定了mNextFocusLeftId,則從比對focused view的id是不是等于mNextFocusLeftId。 如果不是,則向上找到focused view的parent繼續(xù)比對id是否等于mNextFocusLeftId,直到找到root view 如果還沒有找到,則返回root view
流程二:
addFocusables()
findNextFocus()//----------------------root--view------------------------ //ViewGroup繼承自View的方法 public void addFocusables(ArrayList<View> views, @FocusDirection int direction) { addFocusables(views, direction, isInTouchMode() ? FOCUSABLES_TOUCH_MODE : FOCUSABLES_ALL); } //ViewGroup復(fù)寫自View的方法 @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { final int focusableCount = views.size(); //root view是否可以獲得焦點(diǎn) final int descendantFocusability = getDescendantFocusability(); if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) { if (shouldBlockFocusForTouchscreen()) { focusableMode |= FOCUSABLES_TOUCH_MODE; } final int count = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < count; i++) { final View child = children[i]; //如果child view 可見,繼續(xù)循環(huán)調(diào)用chid的addFocusables()方法 //如果chid view是ViewGroup,則繼續(xù)調(diào)用ViewGroup類中的addFocusables()方法 //如果chid view是View,則調(diào)用View類中的addFocusables()方法 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { child.addFocusables(views, direction, focusableMode); } } } //滿足以下條件,則把當(dāng)前的ViewGroup當(dāng)做View嘗試加入可獲得焦點(diǎn)的列表 //1.觸摸模式下可以獲得焦點(diǎn) || 觸摸模式下不會攔截焦點(diǎn) //2.focusableCount == views.size() || descendantFocusability != FOCUS_AFTER_DESCENDANTS // we add ourselves (if focusable) in all cases except for when we are // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is // to avoid the focus search finding layouts when a more precise search // among the focusable children would be more interesting. //FOCUS_AFTER_DESCENDANTS:This view will get focus only if none of its descendants want it. if ((descendantFocusability != FOCUS_AFTER_DESCENDANTS // No focusable descendants || (focusableCount == views.size())) && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())) { //調(diào)用View類中的addFocusables()把ViewGroup計(jì)入可獲得焦點(diǎn)的列表 super.addFocusables(views, direction, focusableMode); } } //View類中的方法 public void addFocusables(ArrayList<View> views, @FocusDirection int direction, @FocusableMode int focusableMode) { if (views == null) { return; } //view不可以獲取焦點(diǎn)則返回 if (!isFocusable()) { return; } //如果是FOCUSABLES_TOUCH_MODE模式,并且在這種模式下不可以獲得焦點(diǎn),則返回 if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE && !isFocusableInTouchMode()) { return; } //滿足條件,加入集合 views.add(this); } //總結(jié): //把root view中的所有可以獲得焦點(diǎn)的view,加入列表 //-----------------FocusFinder---------------------------- //先考慮focused != null的情況 private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction, ArrayList<View> focusables) { //1.焦點(diǎn)不為空的情況 if (focused != null) { if (focusedRect == null) { focusedRect = mFocusedRect; } // fill in interesting rect from focused focused.getFocusedRect(focusedRect); root.offsetDescendantRectToMyCoords(focused, focusedRect); } //2.焦點(diǎn)為空的情況 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_LEFT: case View.FOCUS_UP: setFocusBottomRight(root, focusedRect); break; ... } } } switch (direction) { case View.FOCUS_FORWARD: ... case View.FOCUS_UP: case View.FOCUS_DOWN: case View.FOCUS_LEFT: case View.FOCUS_RIGHT: //Note: return findNextFocusInAbsoluteDirection(focusables, root, focused, focusedRect, direction); ... } } 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: // //Note:mBestCandidateRect在focusedReact的右邊 //并且距離focusedReact的右邊一個像素 mBestCandidateRect.offset(focusedRect.width() + 1, 0); break; case View.FOCUS_RIGHT: ... } View closest = null; //遍歷可獲得焦點(diǎn)的列表 int numFocusables = focusables.size(); for (int i = 0; i < numFocusables; i++) { View focusable = focusables.get(i); // only interested in other non-root views //忽略當(dāng)前的focused view 和root view if (focusable == focused || focusable == root) continue; // get focus bounds of other view in same coordinate system focusable.getFocusedRect(mOtherRect); root.offsetDescendantRectToMyCoords(focusable, mOtherRect); //找到最佳的候選的view,則返回 if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) { mBestCandidateRect.set(mOtherRect); closest = focusable; } } return closest; }
Android TV FocusFinder尋焦流程分析(一)
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
相關(guān)閱讀更多精彩內(nèi)容
- 焦點(diǎn): 焦點(diǎn)(Focus)可以理解為選中態(tài),在Android TV上起很重要的作用。一個視圖控件只有在獲得焦點(diǎn)的狀...
- ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
- 本文重點(diǎn)針對android TV開發(fā)的同學(xué),分析遙控或鍵盤按鍵事件后焦點(diǎn)的分發(fā)機(jī)制。尤其是剛從手機(jī)開發(fā)轉(zhuǎn)向TV開發(fā)...