Android焦点分发基础

来源:互联网 发布:淘宝第三方促销平台 编辑:程序博客网 时间:2024/06/18 13:51

目录(?)[+]
本文转载自:http://www.cnblogs.com/myzh/p/3664544.html?utm_source=tuicool&utm_medium=referral 请尊重原创

焦点处理相关记录
以下所涉及的焦点部分,只是按键移动部分,不明确包含Touch Focus部分

需解决问题
控件的下一个焦点是哪?

分析思路
当用户通过按键(遥控器等)触发焦点切换时,事件指令会通过底层进行一系列处理。
在ViewRootImpl.java中有一个方法,deliverKeyEventPostIme(…),因为涉及到底层代码,所以没有详细的跟踪分析此方法的调用逻辑,根据网上的资料,按键相关的处理会经过此方法。

private void deliverKeyEventPostIme(QueuedInputEvent q) {    ...    // Handle automatic focus changes.    if (event.getAction() == KeyEvent.ACTION_DOWN) {        int direction = 0;        switch (event.getKeyCode()) {            case KeyEvent.KEYCODE_DPAD_LEFT:                if (event.hasNoModifiers()) {                    direction = View.FOCUS_LEFT;                }                break;            case KeyEvent.KEYCODE_DPAD_RIGHT:                if (event.hasNoModifiers()) {                    direction = View.FOCUS_RIGHT;                }                break;            ...        }        if (direction != 0) {            View focused = mView.findFocus();            if (focused != null) {                View v = focused.focusSearch(direction);                if (v != null && v != focused) {                   .....                    if (v.requestFocus(direction, mTempRect)) {                        ...finishInputEvent(q, true);                        return;                    }                }                ...            }        }

由此方法可以看出,最主要的两个核心过程:

View v = focused.focusSearch(direction);v.requestFocus(direction, mTempRect)

接下来详细的分析下,看看过程中进行了什么操作

具体分析
在具体分析前,首先我们先明确下相关变量的定义

View mView : 主体View[DecorView]

    //一般把主View“DecorView”添加到WindowManagerImpl中(通过addView)    //WindowManagerImpl.java        private void addView(View view...) {            ViewRootImpl root;            root = new ViewRootImpl(view.getContext());            ...            root.setView(view, wparams, panelParentView);            ...        }            //ViewRootImpl.java    public void setView(View view....) {        synchronized (this) {            if (mView == null) {                mView = view;                ...            }        ...        }    }

所以mView是一个DecorView类型的变量.

View focused :

    View focused = mView.findFocus();            //PhoneWindow.java    private final class DecorView extends FrameLayout implements RootVie.... {        ...    }            //FrameLayout.java    public class FrameLayout extends ViewGroup {        ...    }            //ViewGroup.java    //mFocused记录的是当前被焦点选中的view    @Override    public View findFocus() {    if (DBG) {        System.out.println("Find focus in " + this + ": flags="                + isFocused() + ", child=" + mFocused);    }    if (isFocused()) {        return this;    }    if (mFocused != null) {        return mFocused.findFocus();    }    return null;}

所以最终得到的focused为当前页面中得到焦点的view.

在明确的相关变量后,我们开始View v = focused.focusSearch(direction)的具体分析.

//View.java
public View focusSearch(int direction) {
//如果存在父控件,则执行父控件的focusSearch方法
if (mParent != null) {
return mParent.focusSearch(this, direction);
} else {
return null;
}
}
//ViewGroup.java
public View focusSearch(View focused, int direction) {
//判断是否为顶层布局,若是则执行对应方法,若不是则继续向上寻找,说明会从内到外的一层层进行判断,直到最外层的布局为止
if (isRootNamespace()) {
return FocusFinder.getInstance().findNextFocus(this, focused, direction);
} else if (mParent != null) {
return mParent.focusSearch(focused, direction);
}
return null;
}
说明在这个过程中,其实是从里层开始一直遍历到最外层布局,然后在最外层布局将处理交给了FocusFinder中的方法.

FocusFinder.getInstance().findNextFocus(this, focused, direction);

那我们来看看此方法具体做了什么操作

//FocusFinder.javapublic final View findNextFocus(ViewGroup root, View focused, int direction) {    return findNextFocus(root, focused, null, direction);}//FocusFinder.javaprivate View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {    View next = null;    if (focused != null) {        next = findNextUserSpecifiedFocus(root, focused, direction);    }    if (next != null) {        return next;    }    ArrayList<View> focusables = mTempList;    try {        focusables.clear();        root.addFocusables(focusables, direction);        if (!focusables.isEmpty()) {            next = findNextFocus(root, focused, focusedRect, direction, focusables);        }    } finally {        focusables.clear();    }    return next;}

发现在findNextFocus的执行过程的开始,先执行了findNextUserSpecifiedFocus(…)方法,由代码可以看出,此方法先去判断特定Id值是否存在,若存在则查询出Id对应的view.其实这些Id就是xml里通过android:nextFocusUp=”…”等或者代码特别指定的焦点顺序.所以在此过程先判断,若存在,说明下个焦点已经找到,直接返回.

//FocusFinder.javaprivate View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {    // check for user specified next focus    View userSetNextFocus = focused.findUserSetNextFocus(root, direction);    if (userSetNextFocus != null && userSetNextFocus.isFocusable()            && (!userSetNextFocus.isInTouchMode()                    || userSetNextFocus.isFocusableInTouchMode())) {        return userSetNextFocus;    }    return null;}    //View.javaView findUserSetNextFocus(View root, int direction) {    switch (direction) {        case FOCUS_LEFT:            if (mNextFocusLeftId == View.NO_ID) return null;            return findViewInsideOutShouldExist(root, mNextFocusLeftId);        case FOCUS_RIGHT:            if (mNextFocusRightId == View.NO_ID) return null;            return findViewInsideOutShouldExist(root, mNextFocusRightId);        case FOCUS_UP:            if (mNextFocusUpId == View.NO_ID) return null;            return findViewInsideOutShouldExist(root, mNextFocusUpId);        case FOCUS_DOWN:            if (mNextFocusDownId == View.NO_ID) return null;            return findViewInsideOutShouldExist(root, mNextFocusDownId);        case FOCUS_FORWARD:            if (mNextFocusForwardId == View.NO_ID) return null;            return findViewInsideOutShouldExist(root, mNextFocusForwardId);        case FOCUS_BACKWARD: {            if (mID == View.NO_ID) return null;            final int id = mID;            return root.findViewByPredicateInsideOut(this, new Predicate<View>() {                @Override                public boolean apply(View t) {                    return t.mNextFocusForwardId == id;                }            });        }    }    return null;}

如果上面过程没有查询到,则会执行到findNextFocus(…)方法.在这个方法中,先通过offsetDescendantRectToMyCoords(…)方法获得焦点控件的位置矩阵.然后通过比较得到下一个焦点的控件。具体的比较规则可以查看findNextFocusInRelativeDirection(…)方法与findNextFocusInAbsoluteDirection(…)方法.

//FocusFinder.javaprivate 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);    }}

结论
查找焦点的过程,主要是从View的focusSearch(…)方法开始,从当前焦点开始逐层往外,最终在最外层布局执行FocusFinder中的核心方法来获得下个焦点所在的视图view.

如果需要指定跳转,可以在逐层focusSearch(…)的时候,返回特定的view

0 0
原创粉丝点击