你需要了解下Android View的更新requestLayout与重绘invalidate

在大家都了解过Android View的测量布局绘制机制后,我们来细化地分析一下关于View的重绘invalidate与更新requestLayout

现象

public class CustomEmptyView extends View {
    public CustomEmptyView(Context context) {
        super(context);
    }

    public CustomEmptyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomEmptyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.i("CustomEmptyView", "onMeasure");
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.i("CustomEmptyView", "onLayout");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i("CustomEmptyView", "onDraw");
    }
}

从View的绘制机制可知,View从测量、布局、绘制的步骤中会对应执行该View#onMeasure()、View#onLayout()、View#onDraw()。那么我们今天讨论的View#invalidate()和View#requestLayout()呢?我们打印一下数据。

View#invalidate()的执行步骤是:

2019-03-26 17:32:34.739 8075-8075/com.example.myapplication I/CustomEmptyView: onDraw

View#requestLayout()的执行步骤是:

2019-03-26 17:33:13.497 8075-8075/com.example.myapplication I/CustomEmptyView: onMeasure
2019-03-26 17:33:13.501 8075-8075/com.example.myapplication I/CustomEmptyView: onLayout
2019-03-26 17:33:13.503 8075-8075/com.example.myapplication I/CustomEmptyView: onDraw

从打印数据来推测就是View#invalidate()只会执行View#onDraw();而View#requestLayout()则会重新走View的绘制流程。接下来我们从源码的角度来分析一下。
下面的源码分析基于android-28

View#requestLayout()

我们分析一下View#requestLayout()。我们定位到对应的源码

    /**
     * Call this when something has changed which has invalidated the
     * layout of this view. This will schedule a layout pass of the view
     * tree. This should not be called while the view hierarchy is currently in a layout
     * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the
     * end of the current layout pass (and then layout will run again) or after the current
     * frame is drawn and the next layout occurs.
     *
     * 当某内容发生更改,导致该视图的布局重绘时调用此函数。这将安排视图树的布局传递。
     * 当视图层次结构当前处于布局Layout事件时,不会执行该函数.
     * 如果正在进行布局,可以在当前布局传递结束时(然后布局将再次运行)或在绘制当前帧并执行下一个布局之后执行请求。
     * <p>Subclasses which override this method should call the superclass method to
     * handle possible request-during-layout errors correctly.</p>
     */
    @CallSuper
    public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();

        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            // 该方法不在布局事件期间中执行
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }
        // PFLAG_FORCE_LAYOUT会在执行View的measure()和layout()方法时判断,这个是以前的文章看到的。
        // 但是在当前源码的View.class和ViewRootImpl.class,全局搜索PFLAG_FORCE_LAYOUT,并没有直接的判断,导致View#requestLayout()不执行测量和布局方法
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
        // isLayoutRequested()对应是mLayoutRequested字段,该字段在默认为false
        if (mParent != null && !mParent.isLayoutRequested()) {
            // 执行父容器的requestLayout()方法
            mParent.r
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值