博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Android学习笔记]理解焦点处理原理的相关记录
阅读量:6941 次
发布时间:2019-06-27

本文共 9387 字,大约阅读时间需要 31 分钟。

焦点处理相关记录

以下所涉及的焦点部分,只是按键移动部分,不明确包含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.java    public final View findNextFocus(ViewGroup root, View focused, int direction) {        return findNextFocus(root, focused, null, direction);    }
//FocusFinder.java    private 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
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.java    private 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.java    View 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
() { @Override public boolean apply(View t) { return t.mNextFocusForwardId == id; } }); } } return null; }

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

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

结论

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

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

转载于:https://www.cnblogs.com/myzh/p/3664544.html

你可能感兴趣的文章
卖掉用户和产品,互联网先驱雅虎从此就是个投资公司
查看>>
《Android应用开发攻略》——3.9 使用本地运行时应用程序日志分析现场错误情况...
查看>>
专访网金社CEO吴志刚,如何看待互金潮落及Fintech潮起?
查看>>
面对众多的前端框架,我们该如何学习?
查看>>
海外科技股玩闪崩 吓走泡沫吓不走真成长
查看>>
赛维“强裁之殇”:破产法该如何修改
查看>>
移动时代,如何随时随地保持舒适通讯?
查看>>
十款逆天级效率工具盘点:环信移动客服上榜
查看>>
不得了 美国ISP承诺不卖用户浏览记录
查看>>
再用Wi-Fi就过时了 Li-Fi的速度是它的100倍
查看>>
雅虎泄露事故黑客遭起诉能否起到有效的震慑?
查看>>
贵州推动大数据创新试验区建设
查看>>
索尼出售广州工厂 员工停产维权求补偿
查看>>
华为员工家属:华为人收入高背后是全家人的付出
查看>>
Yahoo! Screwdriver:可扩展的持续集成工具
查看>>
Skype已死?触宝电话和微信说:“有事烧纸“
查看>>
高燕婕:解读中国“十三五”智慧医疗与健康服务业之发展
查看>>
安防在金融行业应用的未来发展趋势
查看>>
世界那么大,微信国际化
查看>>
开源SDN来势汹汹 ODL中国实战弥补人才短板
查看>>