Android TV FocusFinder尋焦流程分析(一)

  1. FocusFinder類的實(shí)例是依附于線程的,每個線程一個實(shí)例。

    private static final ThreadLocal<FocusFinder> tlFocusFinder =
        new ThreadLocal<FocusFinder>() {
            @Override
            protected FocusFinder initialValue() {
                return new FocusFinder();
            }
        };
    
  2. public final View findNextFocus(ViewGroup root, View focused, int direction)
    public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction)

  3. 先從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;
    }
    
最后編輯于
?著作權(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ù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容