关于View的焦点

##1.什么是View/ViewGroup的焦点,焦点的作用是什么?
从广义上来说,焦点就是用户当前正在或者下一步可能操作的目标,在按键模式下(现在大部分手机都是触摸模式)一般具有焦点的View都会高亮展示,以提示用户当前可以操作的目标。
从狭义上来说,焦点就是View中的mPrivateFlags成员字段被添加上了PFLAG_FOCUSED标示。

##2.View和ViewGroup中hasFocus方法的区别?

    //View的hasFocus方法
    public boolean hasFocus() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0;
    }
   //ViewGroup的hasFocus方法
    public boolean hasFocus() {
        return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
    }

上述是View的hasFocus方法就是去检查当前view的mPrivateFlags字段中是否具有PFLAG_FOCUSED,而ViewGroup的hasFocus方法出了检查PFLAG_FOCUSED,还可以检查mFocused变量,也就是说ViewGroup的hasFocus方法返回两种情况

  1. 当前ViewGroup自身就是焦点所有者
  2. 当前ViewGroup自身没有焦点,但是具有焦点的控件在其子View中,这个时候mFocused就是焦点链路上的一个节点
    焦点链路
    上述的绿色背景的节点就构成了一个焦点链路,最顶层的ViewGroup的mFocused执行第二层的ViewGroup,第二层的中的mFocused指向View,该View就是真正获取到焦点的控件

##3.View.requestFocus方法是怎么获取和释放焦点的?

    public final boolean requestFocus() {
        return requestFocus(View.FOCUS_DOWN);
    }
    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
        return requestFocusNoSearch(direction, previouslyFocusedRect);
    }

    private boolean requestFocusNoSearch(int direction, Rect previouslyFocusedRect) {
        // need to be focusable
        // 检查当前View是否能具有焦点
        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;
        }

        // need to not have any parents blocking us
        // 检查其所属的ViewGroup是否管控了焦点分发策略,后面会说道
        if (hasAncestorThatBlocksDescendantFocus()) {
            return false;
        }
        //开始焦点更换流程
        handleFocusGainInternal(direction, previouslyFocusedRect);
        return true;
    }

view的requestFocus方法先检查当前View是否具有获取焦点的资格,之后再检查是否在触摸模式下,如果在该模式下,则还会检查是否具有触摸模式的焦点资格(在控件系统中,存在两种模式,分别是按键模式和触摸模式)。之后会检查其ViewGroup是否允许子View获取焦点,当ViewGroup中具有FOCUS_BLOCK_DESCENDANTS标示时,就会阻止子View获取焦点,如果允许子View获取焦点,则开始焦点请求

      //如果当前View不是焦点控件,才进行后续操作
      if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
            //给当前View加上焦点标示
            mPrivateFlags |= PFLAG_FOCUSED;
            //先找到最顶层的ViewGroup,之后根据焦点链路找到前一个真正的焦点控件
            View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;

            if (mParent != null) {
                // 通过mParent向上一级View回溯,通知ViewGroup焦点控件更换了,让ViewGroup更新焦点链路
                mParent.requestChildFocus(this, this);
                updateFocusedInCluster(oldFocus, direction);
            }

            if (mAttachInfo != null) {
                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
            }
            //通知焦点观察者,焦点改变了
            onFocusChanged(true, direction, previouslyFocusedRect);
            // 刷新视图展示,比如在按键模式下,具有焦点的控件会高亮显示
            refreshDrawableState();
        }

接下来看下ViewGroup的requestChildFocus方法:

    @Override
    public void requestChildFocus(View child, View focused) {
        // 再次检查当前ViewGroup焦点拦截策略
        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
            return;
        }
        // Unfocus us, if necessary
        super.unFocus(focused);
        // 如果当前ViewGroup是之前的焦点链路上的一个节点,则mFocused节点去释放焦点(清空焦点标示)
        if (mFocused != child) {
            if (mFocused != null) {
                mFocused.unFocus(focused);
            }
            mFocused = child;
        }
        if (mParent != null) {
           //不断向上一集View回溯,直到更新了整个View树的焦点链路,注意这里的第一个参数不是之前的焦点控件了,而是当前ViewGroup了
            mParent.requestChildFocus(this, focused);
        }
    }

看下View的unFocus方法:

   void unFocus(View focused) {
        clearFocusInternal(focused, false, false);
    }
   void clearFocusInternal(View focused, boolean propagate, boolean refocus) {
        if ((mPrivateFlags & PFLAG_FOCUSED) != 0) {
            //清空焦点标示
            mPrivateFlags &= ~PFLAG_FOCUSED;
            if (propagate && mParent != null) {
                //向上级View回溯,清空焦点
                mParent.clearChildFocus(this);
            }

            onFocusChanged(false, 0, null);
            refreshDrawableState();

            if (propagate && (!refocus || !rootViewRequestFocus())) {
                notifyGlobalFocusCleared(this);
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值