Android View系统源码分析(十三)—— View.requestFocus() & ViewRoot.requestLayout()

来源:互联网 发布:linux xen 编辑:程序博客网 时间:2024/04/29 07:56

View.requestFocus()

先更要让某个视图获得焦点,一种犯法是用户使用物理方向键将焦点移动到该视图,另一种方法是程序员直接调用View.requestFocus()。
requestFocus()也是不能独自完成的,当一个视图想要获取焦点时,必须请求它的父视图来完成该操作。因为父视图知道当前哪个视图正在拥有焦点,如果要进行焦点的切换,则必须要先告诉原先的视图放弃焦点,而这些操作所需要的信息正是父视图中保存的,所以requestFocus()也必须由父视图来完成。

  • requestFocus():空参,它被转换成requestFocus(View.FOCUS_DOWN)。
  • requestFocus(int direction):它被转换成requestFocus(direction,null)。
  • requestFocus(int direction , Rect preFocusRec):
    • direction:往哪个方向上寻找下一个视图
    • preFocusRec:当前拥有焦点的视图所占的矩形区域,这个区域是相对该视图的直接父视图来定义的。

因为该函数内部世界级上在DOWN方向上找下一个可以获得焦点的视图,至于是哪一个视图就不一定了,这取决于父视图的执行逻辑,这就是为什么该函数的返回值是一个boolean的原因,其意义是该视图到底能不能获得焦点。

VIEW-REQUESTFOCUS

下载VSDX

  /**
     * Call this to try to give focus to a specific view or to one of its descendants
     * and give it hints about the direction and a specific rectangle that the focus
     * is coming from.  The rectangle can help give larger views a finer grained hint
     * about where focus is coming from, and therefore, where to show selection, or
     * forward focus change internally.
     *
     * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns false),
     * or if it is focusable and it is not focusable in touch mode ({@link #isFocusableInTouchMode})
     * while the device is in touch mode.
     *
     * A View will not take focus if it is not visible.
     *
     * A View will not take focus if one of its parents has {@link android.view.ViewGroup#getDescendantFocusability()}
     * equal to {@link ViewGroup#FOCUS_BLOCK_DESCENDANTS}.
     *
     * See also {@link #focusSearch}, which is what you call to say that you
     * have focus, and you want your parent to look for the next one.
     *
     * You may wish to override this method if your custom {@link View} has an internal
     * {@link View} that it wishes to forward the request to.
     *
     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
     *        to give a finer grained hint about where focus is coming from.  May be null
     *        if there is no hint.
     * @return Whether this view or one of its descendants actually took focus.
     */
 
    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
 
        //需要有一个获取焦点的能力
        // need to be focusable
 
        //如果当前视图的逻辑标志位的FOCUSABLE位不为 FOCUSABLE(没有获取焦点的能力),或者没有显示。直接return false。
        if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
                (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
            return false;
        }
 
 
        // 如果是在touch 模式下,就要有在touch 模式下获取焦点的能力。
        // need to be focusable in touch mode if in touch mode
 
        //如果当前为touchMode,并且当前视图的逻辑标志位不具备 FOCUSABLE_IN_TOUCH_MODE的属性,那么也直接return false。
        if (isInTouchMode() &&
                (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
            return false;
        }
 
 
        //需要判断是否父视图阻止当前视图获取焦点,如果阻止,则直接返回false。应用程序可以调用其父视图的 ViewGroup.setDecendantFocusability(int focusability)方法设置该VIewGroup是否要阻止其子视图获取焦点,默认情况下都不阻止。
        // need to not have any parents blocking us
        if (hasAncestorThatBlocksDescendantFocus()) {
            return false;
        }
 
 
        //执行完该函数后,则该视图肯定会获取焦点,所以直接返回true。
        handleFocusGainInternal(direction, previouslyFocusedRect);
        return true;
    }
 
 
 
 
View.handleFocusGainInternal(int direction , Rect previouslyFocusedRect)
/**
     * Give this view focus. This will cause {@link #onFocusChanged} to be called.
     *
     * Note: this does not check whether this {@link View} should get focus, it just
     * gives it focus no matter what.  It should only be called internally by framework
     * code that knows what it is doing, namely {@link #requestFocus(int, Rect)}.
     *
     * @param direction values are View.FOCUS_UP, View.FOCUS_DOWN,
     *        View.FOCUS_LEFT or View.FOCUS_RIGHT. This is the direction which
     *        focus moved when requestFocus() is called. It may not always
     *        apply, in which case use the default View.FOCUS_DOWN.
     * @param previouslyFocusedRect The rectangle of the view that had focus
     *        prior in this View's coordinate system.
     */
    void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
        if (DBG) {
            System.out.println(this + " requestFocus()");
        }
 
        //如果当前视图没有获得焦点。这就意味着,应用程序连续调用两次requestFocus(),第二次调用的时候就直接返回了。
        if ((mPrivateFlags & FOCUSED) == 0) {
 
            //更新当前视图的逻辑标志位。
            mPrivateFlags |= FOCUSED;
 
 
            //如果该视图有父视图
            if (mParent != null) {
 
                //调用父视图来使当前视图获取焦点。第一个参数为当前视图,第二个参数为真正获取申请焦点的视图,该视图可以为当前视图的子视图。
                mParent.requestChildFocus(this, this);
            }
 
            //执行回调方法。应用程序可以重载该函数以便进行其他操作。这时该视图已经获取到焦点了。
            onFocusChanged(true, direction, previouslyFocusedRect);
 
            //重绘当前视图状态
            refreshDrawableState();
        }
    }
 
 
 
 
ViewGroup.requestChildFocus(View child, View focused)
public void requestChildFocus(View child, View focused) {
        if (DBG) {
            System.out.println(this + " requestChildFocus()");
        }
 
        //判断当前ViewGroup的逻辑标志位是否存在 FOCUS_BLOCK_DESCENDANTS 属性,也就是是否阻止其子视图的获取焦点。
        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
            return;
        }
 
        // Unfocus us, if necessary
        //释放当前视图的焦点,如果有必要的话。
        super.unFocus();
 
        // We had a previous notion of who had focus. Clear it.
        // mFocused 记录的是之前拥有焦点的子视图,如果之前拥有焦点的视图不是现在要请求获取焦点的视图
        if (mFocused != child) {
            if (mFocused != null) {
 
                //放弃之前视图的焦点
                mFocused.unFocus();
            }
 
            //更新mFocused变量
            mFocused = child;
        }
 
        //如果当前ViewGroup有父视图的话,就继续递归
        if (mParent != null) {
                //注意,这里直接传递的是真正要拥有焦点子视图
            mParent.requestChildFocus(this, focused);
        }
    }
 
 
ViewRoot.requestFocus()
public void requestChildFocus(View child, View focused) {
 
        //判断当前操作线程
        checkThread();
 
        //如果ViewRoot中记录的之前拥有焦点的视图不等于当前要申请焦点的视图
        if (mFocusedView != focused) {
 
            //View树 全局焦点改变 监听回调。
            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
 
            //遍历树发起
            scheduleTraversals();
        }
 
        //更新变量
        mFocusedView = mRealFocusedView = focused;
 
        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
                + mFocusedView);
    }
 
 
 
View.requestLayout()


该函数的执行过程比较简单,因为当View树进行重新布局时,总是重新给所有的视图进行布局。因为最简单的想法就是只要设置了一个标志位属性就好了。


首先给mPrivateFlags添加 FORCE_LAYOUT标识,然后调用mParent的 ViewGroup.requestLayout()。对于一个具体的VIew对象而言,其父视图要么是一个ViewGroup,要么是一个ViewRoot。而ViewGroup没有对该方法进行重载。也就是说,ViewGroup会按照View.requestLayout()进行处理。


如果有多层视图嵌套,这就会才产生一个递归调用,并最终调用到ViewRoot类的requestLayout()。
ViewRoot.requestLayout


 public void requestLayout() {
 
        //判断线程
        checkThread();
 
        //给ViewRoot中的变量mLayoutRequested赋值为 true,之后真正进行布局的代码会检查该变量,并决定是否需要重新布局。
        mLayoutRequested = true;
 
        //发起一个View树的遍历消息
        scheduleTraversals();
    }
 

0 0