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

  1. FocusFinder类的实例是依附于线程的,每个线程一个实例。

    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.如果当前的焦点view不为空,则寻找用户指定的下一个焦点
        if (focused != null) {
            next = findNextUserSpecifiedFocus(root, focused, direction);
        }
        //如果找到,直接返回用户指定的焦点
        if (next != null) {
            return next;
        }
        //流程二:
        ArrayList<View> focusables = mTempList;
        try {
            focusables.clear();
            //2.把当前root下的所有的可以获得焦点的view加入列表
            root.addFocusables(focusables, direction);
            if (!focusables.isEmpty()) {
                //3.继续寻找当前root下的焦点
                next = findNextFocus(root, focused, focusedRect, direction, focusables);
            }
        } finally {
            focusables.clear();
        }
        return next;
    }     
    

    流程一:
    findNextUserSpecifiedFocus()

    //分析流程一:
    //----------------------FocusFinder-------------------------
    //1.FocusFinder findUserSetNextFocus()找到用户指定的下一个焦点
    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.进入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 == 要寻找的下一个焦点的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是获得焦点的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已经是root view了,则返回
            if (view != null || start == this) {
                return view;
            }
            //如果没有找到,则一层层找到start的父view继续比较
            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;
    }
     //总结:
     1.如果mNextFocusLeftId == View.NO_ID 即用户没有指定下一个焦点直接返回null
     2.如果用户指定了mNextFocusLeftId,则从比对focused view的id是不是等于mNextFocusLeftId。
       如果不是,则向上找到focused view的parent继续比对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复写自View的方法
    @Override
    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
        final int focusableCount = views.size();
        //root view是否可以获得焦点
        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 可见,继续循环调用chid的addFocusables()方法
                //如果chid view是ViewGroup,则继续调用ViewGroup类中的addFocusables()方法
                //如果chid view是View,则调用View类中的addFocusables()方法
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                    child.addFocusables(views, direction, focusableMode);
                }
            }
        }
    
        //满足以下条件,则把当前的ViewGroup当做View尝试加入可获得焦点的列表
        //1.触摸模式下可以获得焦点 || 触摸模式下不会拦截焦点
        //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())) {
            //调用View类中的addFocusables()把ViewGroup计入可获得焦点的列表
            super.addFocusables(views, direction, focusableMode);
        }
    }
    
    //View类中的方法
    public void addFocusables(ArrayList<View> views, @FocusDirection int direction, @FocusableMode int focusableMode) {
        if (views == null) {
            return;
        }
        //view不可以获取焦点则返回
        if (!isFocusable()) {
            return;
        }
        //如果是FOCUSABLES_TOUCH_MODE模式,并且在这种模式下不可以获得焦点,则返回
        if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE
                && !isFocusableInTouchMode()) {
            return;
        }
        //满足条件,加入集合
        views.add(this);
    }
    //总结:
    //把root view中的所有可以获得焦点的view,加入列表
    
    //-----------------FocusFinder----------------------------
    //先考虑focused != null的情况
    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect,
            int direction, ArrayList<View> focusables) {
        //1.焦点不为空的情况
        if (focused != null) {
            if (focusedRect == null) {
                focusedRect = mFocusedRect;
            }
            // fill in interesting rect from focused
            focused.getFocusedRect(focusedRect);
            root.offsetDescendantRectToMyCoords(focused, focusedRect);
        } 
        //2.焦点为空的情况
        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;
        
        //遍历可获得焦点的列表
        int numFocusables = focusables.size();
        for (int i = 0; i < numFocusables; i++) {
            View focusable = focusables.get(i);
    
            // only interested in other non-root views
            //忽略当前的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;
    }
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容