当前位置: 首页 > news >正文

东莞做网站定制/预防电信网络诈骗

东莞做网站定制,预防电信网络诈骗,网站开发 发票,网站cms管理后台电话号码作者简介罗宁,六年安卓踩坑经验,致力于底层平台、上层应用等多领域开发。文能静坐弹吉他,武能通宵写代码。各位好,从今天开始,将带来几期关于 Android 源码解析的文章,感谢你的阅读,也欢迎一起讨…
fb4e0ea0c55ecfaeb931d336d084401c.png作者简介
罗宁,六年安卓踩坑经验,致力于底层平台、上层应用等多领域开发。文能静坐弹吉他,武能通宵写代码。
各位好,从今天开始,将带来几期关于 Android 源码解析的文章,感谢你的阅读,也欢迎一起讨论。这节内容我们先从 requestFocus 入手:在开发过程中,我们需要某个控件 View 进行聚焦,一般会主动调用该控件的 requestFocus 方法。(本文基于 API 27 源码进行分析)
public final boolean requestFocus() {    // 默认使用 FOCUS_DOWN 进行聚焦    return requestFocus(View.FOCUS_DOWN);}
我们从源码中可以看到,requestFocus 接着会走到 requestFocusNoSearch 方法中,看方法名就能理解,因为我们是直接 requestFocus,意图就是指定某个 View 获得焦点,所以不需要走寻焦机制:
private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {    // need to be focusable    // 如果该 view 设置的 focusable = false,直接返回    if ((mViewFlags & FOCUSABLE) != FOCUSABLE            || (mViewFlags & VISIBILITY_MASK) != VISIBLE) {        return false;    }    // need to be focusable in touch mode if in touch mode    // 触摸模式下    if (isInTouchMode() &&        (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {           return false;// 如果该 view 设置的 focusableInTouchMode = false,直接返回    }    // need to not have any parents blocking us    if (hasAncestorThatBlocksDescendantFocus()) {        return false;// 如果 parent 中设置了 FOCUS_BLOCK_DESCENDANTS,直接返回    }    handleFocusGainInternal(direction, previouslyFocusedRect);    return true;// 聚焦成功}

1 requestFocusNoSearch

方法 requestFocusNoSearch 的大致功能如下:
  • 首先第一步会判断当前 View 的 focusable 状态,如果是 false,说明该 View并不能获取焦点,也就没有必要再往下走了。
  • 接着会判断是否触摸模式,在触摸模式下,如果 focusableInTouchMode 是 false 的话,也说明该 View 通过触摸并不能获取焦点,也没必要往下走了。
  • 继续看下面一个判断 hasAncestorThatBlocksDescendantFocus() 方法:
private boolean hasAncestorThatBlocksDescendantFocus() {final boolean focusableInTouchMode = isFocusableInTouchMode();    ViewParent ancestor = mParent;while (ancestor instanceof ViewGroup) {final ViewGroup vgAncestor = (ViewGroup) ancestor;if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS                || (!focusableInTouchMode && vgAncestor.shouldBlockFocusForTouchscreen())) {return true;        } else {            ancestor = vgAncestor.getParent();        }    }return false;}

2 hasAncestorThatBlocksDescendantFocus

再来看下 hasAncestorThatBlocksDescendantFocus,这个方法也就是遍历 parent 父 View 查找是否有设置 FOCUS_BLOCK_DESCENDANTS,如果设置了,说明父 View 把焦点传递给拦截了,并不希望自己获得焦点,因此该方法会返回 true。回到 requestFocusNoSearch 中,也就直接 return 不往下走了。经过一系列的条件判断,如果可聚焦,并且父 View 未拦截焦点,最终会走到核心方法 handleFocusGainInternal 中:
void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {    if (DBG) {        System.out.println(this + " requestFocus()");    }    if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {        mPrivateFlags |= PFLAG_FOCUSED;// 更新标记位(isFocused判断依据)        // 当前状态下的焦点        View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;        if (mParent != null) {            mParent.requestChildFocus(this, this);// 清除当前焦点,将 mFocus 变量更新值为当前期望聚焦的 view            updateFocusedInCluster(oldFocus, direction);// android 高版本新增的方法,此方法和键盘相关,在此不作重点关注        }        if (mAttachInfo != null) {            // 通知 ViewTreeObserver 焦点变化            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);        }        onFocusChanged(true, direction, previouslyFocusedRect);// 通知焦点变化回调        refreshDrawableState();// 当前 view 聚焦,刷新 drawable 状态    }}

3 handleFocusGainInternal

而对于 handleFocusGainInternal 这个方法,首先会更新当前 View 的标记位 mPrivateFlags 记录自己的 isFocused 状态,接着通过 rootView 查找到当前的焦点赋值给 oldFocus,然后调用 parent 的 requestChildFocus 方法告知 parent 自己当前聚焦啦。
@Overridepublic void requestChildFocus(View child, View focused) {    if (DBG) {        System.out.println(this + " requestChildFocus()");    }    // 再次判断是否设置了 FOCUS_BLOCK_DESCENDANTS    if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {        return;    }    // Unfocus us, if necessary    super.unFocus(focused);    // We had a previous notion of who had focus. Clear it.    if (mFocused != child) {        if (mFocused != null) {            mFocused.unFocus(focused);        }        mFocused = child;    }    if (mParent != null) {        mParent.requestChildFocus(this, focused);    }}

4 requestChildFocus

在 requestChildFocus 这个方法里,focused 这个参数其实没用到(unFocus 作为形参传入,其实里面也根本没用到该参数),在上面第一次调用时传入的都是 this,这个 focused 实际上就是直接的焦点,child 在第一次调用时也是直接焦点,child == focused,但是通过 mParent.requestChildFocus(this, focused);后,child 这个参数就变成了直接焦点的父 View,一层一层往上进行调用以此类推,这里要重点区分下两个参数含义。下面是官方对该参数的注释:
    /**     * Called when a child of this parent wants focus     *     * @param child The child of this ViewParent that wants focus. This view     *        will contain the focused view. It is not necessarily the view that     *        actually has focus.     * @param focused The view that is a descendant of child that actually has     *        focus     */    public void requestChildFocus(View child, View focused);
‍在每个 ViewGroup 中都有一个 mFocus 变量,该变量的作用就是保存着当前 ViewGroup 下的焦点,并非直接焦点。(官方对这个变量含义的注释:The view contained within this ViewGroup that has or contains focus.)接着回到这个 requestChildFocus(View child, View focused)方法接着往下看,具体逻辑是:
  • 再次判断是否设置了 FOCUS_BLOCK_DESCENDANTS,如果拦截则不继续往下走。
  • 一般情况下,当前焦点 mFocused 都和我们期望聚焦的 view 并非同一个,则进入分支调用 mFocused.unFocus(focused)
void unFocus(View focused) {    if (DBG) {        System.out.println(this + " unFocus()");    }    clearFocusInternal(focused, false, false);}// 最终调用这个方法void clearFocusInternal(View focused, boolean propagate, boolean refocus) {    // 其实没用到focused这个参数    if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {        mPrivateFlags &= ~PFLAG_FOCUSED;        if (propagate && mParent != null) {            mParent.clearChildFocus(this);// 通知 parent 清除自己(当前的焦点)的 mFocus 值,因为焦点已经不在该 View 树节点下        }        onFocusChanged(false, 0, null);// 回调焦点状态变更的通知        refreshDrawableState();// 刷新失去焦点后的 drawable 状态        if (propagate && (!refocus || !rootViewRequestFocus())) {            notifyGlobalFocusCleared(this);        }    }}

5 clearFocusInternal

最后再来聊聊 clearFocusInternal,这个方法是 mFocus 进行调用的,也就是对当前的焦点所在的 View 进行清除焦点状态处理,主要做了下面几件事:
  • 通知 parent 调用 clearChildFocus 将 mFocus 变量置 null,因为焦点已经不在该 View 树节点下。
  • 回调自身的焦点状态变更的通知,我们通常所设置的 setOnFocusChangeListener 的监听就是在这里面进行触发回调的。
  • 由于第 1 步中清除了自己的焦点状态,失焦之后自然需要刷新视图状态,这里会调用 refreshDrawableState 进行 drawableState 的刷新,也就是我们通常在 xml 中设置的 selector 状态属性。
注意一点:这里面的 focused 参数其实根本没用到,但是这个 focused 才是真正最直接的焦点。
清除了当前焦点之后,回到 parent 的 requestChildFocus 中,将我们期望聚焦的 child 赋值给 mFocused,前面说过这个 mFocus 变量就是保存着当前的焦点,走到这步,我们调用 View.requestFocus 已经成功将焦点从 oldFocus 转移到 newFocus 上了。
ViewGroup.requestChildFocus...// We had a previous notion of who had focus. Clear it.if (mFocused != child) {    if (mFocused != null) {        mFocused.unFocus(focused);    }    mFocused = child;}if (mParent != null) {    mParent.requestChildFocus(this, focused);}...
接下去还会再次通过 parent 一层一层的告诉父 View,当前焦点在我这。也就是说某一个子 View 如果聚焦了,它会将自己赋值给 parent 的 mFocus 变量,这样下次查找焦点,就可以通过顶层的 parent 一级一级通过 mFocus 变量进行 findFocus 查找到最下层的直接焦点。这里展开一下 findFocus 方法就很明白了:
@Overridepublic View findFocus() {    if (DBG) {        System.out.println("Find focus in " + this + ": flags="                + isFocused() + ", child=" + mFocused);    }    // 如果当前 isFocused 了,说明我自己已经是焦点了,直接返回自己    if (isFocused()) {        return this;    }    // mFocus 不为 null,说明焦点在这个 mFocus 的 View 树下    if (mFocused != null) {        return mFocused.findFocus();    }    return null;}public View findFocus() {    // 当遍历到直接子 View 之后就是根据标志位进行判断    return (mPrivateFlags & PFLAG_FOCUSED) != 0 ? this : null;}
举个例子:A 包含 B,B 包含 C,A 和 B 都是 ViewGroup,C 是直接 View,A 的 mFocus是 B,B 的 mFocus 是 C,注意一点,这里面只是说 mFocus != null,并不是说 A 和 B 的 isFocused 也是 true 的,要区别开 hasFocus 和 isFocused。这里一层层的往上走,最终会走到 ViewRootImpl 的 requestChildFocus 进行 UI 重绘。
public boolean hasFocus() {    return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;}@Overridepublic void requestChildFocus(View child, View focused) {    if (DEBUG_INPUT_RESIZE) {        Log.v(mTag, "Request child focus: focus now " + focused);    }    checkThread();    scheduleTraversals();// UI 重绘}
回到上面,handleFocusGainInternal 中的 mPrivateFlags |= PFLAG_FOCUSED;这里修改了标记位,实际上 isFocused 就是通过这个标记位进行判断的。
@ViewDebug.ExportedProperty(category = "focus")public boolean isFocused() {return (mPrivateFlags & PFLAG_FOCUSED) != 0;}
至此,View.requestFocus 的调用流程结束,焦点已经从之前的 oldFocus 转移到新的 newFocus 上了。接下来,我们继续分析下 ViewGroup.requestFocus 方法:
@Overridepublic boolean requestFocus(int direction, Rect previouslyFocusedRect) {    if (DBG) {        System.out.println(this + " ViewGroup.requestFocus direction="                + direction);    }    int descendantFocusability = getDescendantFocusability();    // 主要还是看 ViewGroup 设置的焦点拦截模式    switch (descendantFocusability) {        case FOCUS_BLOCK_DESCENDANTS:// 拦截掉了焦点,直接调用 super 的逻辑在自己中 requestFocus            return super.requestFocus(direction, previouslyFocusedRect);        case FOCUS_BEFORE_DESCENDANTS: {// 首先调用 super 的逻辑在自己中 requestFocus,如果自己请求焦点失败再遍历子 View 进行 requestFocus            final boolean took = super.requestFocus(direction, previouslyFocusedRect);            return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);        }        case FOCUS_AFTER_DESCENDANTS: {// 与 FOCUS_BEFORE_DESCENDANTS 相反,先遍历子 View 进行 requestFocus,如果子 View 都请求焦点失败后再调用 super 的逻辑在自己中 requestFocus            final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);            return took ? took : super.requestFocus(direction, previouslyFocusedRect);        }        default:            throw new IllegalStateException("descendant focusability must be "                    + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "                    + "but is " + descendantFocusability);    }}
这里面的逻辑还是很清晰的,主要判断依据就是焦点拦截模式 descendantFocusability:
  • FOCUS_BLOCK_DESCENDANTS:自身拦截掉焦点,直接对自己进行 requestFocus 调用去请求焦点;
  • FOCUS_BEFORE_DESCENDANTS:自身优先子 View 获得焦点,先对自己进行 requestFocus 调用去请求焦点,如果失败再遍历子 View 让子 View 进行聚焦;
  • FOCUS_AFTER_DESCENDANTS:先遍历子 View 让子 View 进行聚焦,如果子 View 都没有聚焦,则再对自己进行 requestFocus 调用去请求焦点。
下面我们看下 onRequestFocusInDescendants 里做了些什么:
protected boolean onRequestFocusInDescendants(int direction,        Rect previouslyFocusedRect) {    int index;    int increment;    int end;    int count = mChildrenCount;    if ((direction & FOCUS_FORWARD) != 0) {// 从前往后遍历        index = 0;        increment = 1;        end = count;    } else {// 从后往前遍历        index = count - 1;        increment = -1;        end = -1;    }    final View[] children = mChildren;// mChildren 数组中保存了所有的 childView    for (int i = index; i != end; i += increment) {        View child = children[i];        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {// 遍历子 View,并且 View 可见            if (child.requestFocus(direction, previouslyFocusedRect)) {// 该子 View 请求焦点                return true;// 请求焦点成功,直接返回            }        }    }    return false;}
onRequestFocusInDescendants 主要功能就是遍历该 ViewGroup 下所有子 View,然后对可见的子 View 调用 requestFocus,如果请求焦点成功,则直接返回 true,至此,ViewGroup.requestFocus 也处理完毕了。欢迎加入 Android 交流群,入群请添加下方群秘微信,备注“Android”,等待群秘邀你入群。3bbabd841a93753c221675ac8f691f92.png09a5de1fa96ab06cdd660aff9d01e9ef.png

LIKECOLUMN

悦专栏

在这里,学好编程

做更优秀的 IT人!
http://www.lbrq.cn/news/1237375.html

相关文章:

  • 网站搭建的/站长之家排名查询
  • 曰本真人性做爰相关网站/西安百度竞价外包
  • 西部网站域名出售/360推广怎么收费
  • 阿里云服务器网站备份/免费发广告的网站
  • 大理建设工程信息网站/百度分析
  • 中国开头的网站怎么做/临沂今日头条新闻最新
  • 北京建设银行卡信用卡网站/5151app是交友软件么
  • 网站建设主要内容/微信营销怎么做
  • wordpress分类显示博客/论述搜索引擎优化的具体措施
  • 广东东莞石碣今天新闻/seo网络优化公司
  • 用自己的电脑做网站需要备案吗/线下推广团队
  • 网站建设小组五类成员/东莞seo代理
  • 安顺网站建设兼职/昆山网站建设
  • 留学中介网站建设方案/免费网站制作成品
  • 公司没有网站如何做外贸/爱站网站seo查询工具
  • 济宁建设局网站首页/百度普通收录
  • 跨境出口电商网站/哪里可以建网站
  • 网站做的漂亮的企业/培训机构哪家最好
  • 小公司做网站/百度收录网站提交入口
  • 医院网站建设作用/seo排名优化是什么意思
  • 深圳十大国际外贸公司/seo下载站
  • 万家建设有限公司网站/中央刚刚宣布大消息
  • 长清区网站建设宣传/如何做宣传推广营销
  • 镇江网站营销推广/可以访问境外的浏览器
  • 用html做网站的步骤/it培训学校it培训机构
  • 网站建设空间域名是什么/怎样留别人电话在广告上
  • 2014 网站建设/seo深度解析
  • 深圳房地产网站设计/在百度上打广告找谁
  • wordpress网站公告/百度商家
  • 人力资源和社会保障部网站/搜索大全引擎入口网站
  • 07.config 命令实现动态修改配置和慢查询
  • Python切片命名技术详解:提升代码可读性与维护性的专业实践
  • LangGraph认知篇-Persistence 持久化
  • Navicat连接远程服务器上的mysql
  • 从基础功能到自主决策, Agent 开发进阶路怎么走?
  • vue3渲染html数据并实现文本修改