android 焦点获取流程

来源:互联网 发布:如何安装管家婆软件 编辑:程序博客网 时间:2024/06/05 11:25

android一般都是手机或者平板,一般都是点击的时候获取焦点,当我们添加遥控或手柄支持焦点移动时,这个时候焦点的查找就比较明显了,那么Android的焦点是怎么查找的呢。

我们从handleImeFinishedEvent(ViewRootImpl.java)开始了解焦点的查找流程,handleImeFinishedEven是由dispatchImeFinishedEvent触发,dispatchImeFinishedEvent又是由InputMethodManager触发来的,

handleImeFinishedEvent中跟焦点相关的代码:

if (direction != 0) {                View focused = mView.findFocus();//当前拥有焦点的控件                if (focused != null) {                    View v = focused.focusSearch(direction);//根据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);                        }                        if (v.requestFocus(direction, mTempRect)) {//请求焦点网                            playSoundEffect(SoundEffectConstants                                    .getContantForFocusDirection(direction));                            finishInputEvent(q, true);                            return;                        }                    }                    // Give the focused view a last chance to handle the dpad key.                    if (mView.dispatchUnhandledMove(focused, direction)) {//以前的控件 焦点改变事件                        finishInputEvent(q, true);                        return;                    }                }

这里先获取当前焦点控件,然后根据direction获取下一个最佳的控件,获取控件后调用他的requestFocus,并给前面的焦点控件一个机会处理失去焦点事件,看一下focusSearch

    public View focusSearch(int direction) {        if (mParent != null) {//父控件不为空,调用它的focusSearch            return mParent.focusSearch(this, direction);        } else {            return null;        }    }

一直调用parent的focusSearch,最终到

    public View focusSearch(View focused, int direction) {        if (isRootNamespace()) {//已经是Root层 (installDecor  mDecor.setIsRootNamespace(true);)            // 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);//查找下一个可获得焦点的控件        } else if (mParent != null) {//继续调用父控件的focusSearch            return mParent.focusSearch(focused, direction);        }        return null;    }

如果已经是根控件,调用FocusFinder的findNextFocus,最终调用它的findNextFocus

    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {        View next = null;        if (focused != null) {            next = findNextUserSpecifiedFocus(root, focused, direction);//是xml里通过android:nextFocusUp="..."等或者代码特别指定的焦点顺序        }        if (next != null) {//已经找到            return next;        }        ArrayList<View> focusables = mTempList;//mTempList        try {            focusables.clear();            root.addFocusables(focusables, direction);//获取所有可以获取焦点的控件            if (!focusables.isEmpty()) {                next = findNextFocus(root, focused, focusedRect, direction, focusables);//查找下一个焦点控件            }        } finally {            focusables.clear();        }        return next;    }


先看一下该控件是否已经设置过它的焦点移动事件,indNextUserSpecifiedFocus就是干这个事的,此方法先去判断特定Id值是否存在,若存在则查询出Id对应的view.其实这些Id就是xml里通过android:nextFocusUp="..."等或者代码特别指定的焦点顺序.所以在此过程先判断,若存在,说明下个焦点已经找到,直接返回.,未找到,则调用findNextFocus继续查找

    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);            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);        }    }

调用findNextFocusInAbsoluteDirection查找下一个焦点控件

    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);//设置mBestCandidateRect        switch(direction) {            case View.FOCUS_LEFT:                mBestCandidateRect.offset(focusedRect.width() + 1, 0);                break;            case View.FOCUS_RIGHT:                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();        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;            // get focus bounds of other view in same coordinate system            focusable.getFocusedRect(mOtherRect);//获取当其拥有焦点时的区域大小            root.offsetDescendantRectToMyCoords(focusable, mOtherRect);            if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {//比较和Best哪个更好                mBestCandidateRect.set(mOtherRect);                closest = focusable;//更合适            }        }        return closest;//返回    }

根据焦点控件的区域去查找一个合适的,具体查找,比较那个合适比较复杂,暂时还没看懂。

关于移动的时候有时候没有焦点,我们可以查看在findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction)这个函数中,root.addFocusables(focusables, direction);/可能获取到了很多有焦点的控件,最后找到的合适的控件可能不是我们想要的,这时我们可以把某些控件设置为不能获取焦点,这样我们移动焦点时,可能是我们想要的焦点(偷懒的做法)

        mDaySpinner.setFocusable(false);        mMonthSpinner.setFocusable(false);        mYearSpinner.setFocusable(false);        mYearSpinnerInput.setSelection(0, 4); //获取焦点时选中的text




1 0
原创粉丝点击