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 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 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 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. 后台动态添加布局文件、控件与动态设置属性2
  2. 一个关于android中ListView的子控件中按钮事件的简单方法
  3. android的触摸事件
  4. 使用Android常用控件与布局实现美观的登录页面
  5. [置顶] Android WebKit消息处理(二)Touch事件的分发处理
  6. Android发光特效焦点框-遥控器版本
  7. android 事件分发之dispatchTouchEvent()用法
  8. [Android]RecyclerView基本使用+adapter回调接口实现点击事件
  9. EditText点击事件——弹出单选框

随机推荐

  1. Android(安卓)自定义dialog,实现右上角显
  2. Android中Button设置background过程的研
  3. [Android(安卓)步步为营]第2营 Hello Wor
  4. Android(安卓)开发 框架系列 百度语音合
  5. Android(安卓)VideoView播放视频(1)
  6. android让后将程序图标显示在状态栏
  7. Android获取屏幕宽高新方法
  8. Android(安卓)Studio 使用Log
  9. android 使用butterknife简化加载布局控
  10. Android(安卓)数据库框架ormlite(一)