【自定义View】invalidate、postInvalidate以及requestLayout笔记

基础知识

1、标志位:

invalidate方法里面用了大量的标志位去标识各种操作,如果不搞明白这些操作的含义,很容易误解的

		mPrivateFlags & PFLAG_DIRTY != 0	 //mPrivateFlags包含PFLAG_DIRTY这个标志位
		mPrivateFlags & PFLAG_DIRTY == PFLAG_DIRTY 	//同上
 	    mPrivateFlags |= PFLAG_DIRTY   		//添加PFLAG_DIRTY标志位
  		mPrivateFlags &= ~PFLAG_DIRTY		//移除PFLAG_DIRTY标志位
  • PFLAG_DRAWING_CACHE_VALID

   /** {@hide} */
   static final int PFLAG_DRAWING_CACHE_VALID         = 0x00008000;

代表drawingCache缓存是否有效。


关于drawingCache: 注意:在Android Q版本已经被标记为@Deprecated,谷歌更推荐自己去创建Bitmap
View组件显示的内容可以通过cache机制保存为bitmap,只需要通过以下API

 void  setDrawingCacheEnabled(boolean flag),//开启缓存
 Bitmap  getDrawingCache(boolean autoScale),//获取缓存图片
 void  buildDrawingCache(boolean autoScale),//重建缓存
 void  destroyDrawingCache()//销毁缓存

  • PFLAG_INVALIDATED

    /**
     * Indicates that this view was specifically invalidated, not just dirtied because some
     * child view was invalidated. The flag is used to determine when we need to recreate
     * a view's display list (as opposed to just returning a reference to its existing
     * display list).
     *
     * @hide
     */
    static final int PFLAG_INVALIDATED                 = 0x80000000;

mPrivateFlags添加此标志位则表明视图已明确无效,也就是该被重新绘制了,这个标志位用来控制变量mRecreateDisplayList


  • PFLAG_DRAWN

    /** {@hide} */
    static final int PFLAG_DRAWN                       = 0x00000020;

draw的过去分词,代表这个view是否已经被绘制完成,在draw方法被添加


  • PFLAG_HAS_BOUNDS

    /** {@hide} */
    static final int PFLAG_HAS_BOUNDS                  = 0x00000010;

这个标志位在layout过程中被添加,它代表了这个View是否layout过


  • PFLAG_DIRTY

    /**
     * View flag indicating whether this view was invalidated (fully or partially.)
     *
     * @hide
     */
    static final int PFLAG_DIRTY                       = 0x00200000;

代表这部分视图已经无效,子ViewView.invalidateInternal方法里面被添加,父View硬件绘制onDescendantInvalidated中添加


  • PFLAG_DIRTY_MASK

    /**
     * Mask for {@link #PFLAG_DIRTY}.
     *
     * @hide
     */
    static final int PFLAG_DIRTY_MASK                  = 0x00200000;

这是PFLAG_DIRTY的掩码,我们可以直接理解为它就是PFLAG_DIRTY,因为它们的值是相同的

关于掩码:
简单来说掩码可以辅助我们判断mPrivateFlag是否同时满足多个条件,例如我们定义一个掩码:PFLAG_TEST,用来代表是否同时满足AB两个条件,这样我们下次需要判断是否同时满足AB时,就可以直接判断该掩码,而不用去判断A再判断B
具体可以看这篇文章: 位运算的那些事(三)位掩码

然而这里并没有用作多个操作的掩码,或许只是为了拓展,又或者是为了兼容之前的标志位PFLAG_DIRTY_OPAQUE


  • PFLAG_FORCE_LAYOUT

    /** {@hide} */
    static final int PFLAG_FORCE_LAYOUT                = 0x00001000;

这个标志位在方法requestLayout中会被添加,代表强行回调onMeasure,而不是使用缓存mMeasureCache


  • PFLAG_HAS_BOUNDS

    /** {@hide} */
    static final int PFLAG_HAS_BOUNDS                  = 0x00000010;

这个标志位在layout过程中被添加,它代表了这个View是否layout过


2、变量

  • mRecreateDisplayList

    /**
     * Flag to indicate that this view was marked INVALIDATED, or had its display list
     * invalidated, prior to the current drawing iteration. If true, the view must re-draw
     * its display list. This flag, used only when hw accelerated, allows us to clear the
     * flag while retaining this information until it's needed (at getDisplayList() time and
     * in drawChild(), when we decide to draw a view's children's display lists into our own).
     *
     * {@hide}
     */
    @UnsupportedAppUsage
    boolean mRecreateDisplayList = false;

根据是否开启硬件加速,draw分为:

  • mAttachInfo.mThreadedRenderer.draw
  • drawSoftware

这个变量在硬件加速绘制过程中使用,当为true就代表这个View需要重新绘制



源码

1、invalidate

1.1、View # invalidate

	/**
     *
     * @param invalidateCache Whether the drawing cache for this view should be
     *            invalidated as well. This is usually true for a full
     *            invalidate, but may be set to false if the View's contents or
     *            dimensions have not changed.
     * @hide
     */
    @UnsupportedAppUsage
    //invalidateCache参数代表了drawing chche是否也要跟着刷新,通常情况下都是true
    //但是View的内容和尺寸都没有改变的话,传false性能会更好
    public void invalidate(boolean invalidateCache) {
   
        invalidateInternal(0, 0, mRight - mLeft, mBottom - 
                     mTop, invalidateCache, true);
    }

2、View # invalidateInternal

        void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
   
		
		//同时满足以下三个条件就会跳过invalidate
		//1、View不可见	
		//2、而且View没有设置动画
		//3、parent不是ViewGroup或者parent不处于过渡态(从VISIBLE、INVISBLE、GONE几个状态里面转换)
        if (skipInvalidate()) {
   
            return;
        }

		//这里判断是否需要将PFLAG_DRAWN去掉,PFLAG_DRAWN代表绘制完成		
		//这里也就是判断是否需要重新绘制
		//只要满足以下条件之一:
		//1、View已经初始化绘制完成(调用过draw)并且已经Layout过了
		//2、invalidateCache为true 同时 drawingCache有效(绘制缓存有效)
		//3、mPrivateFlag没有添加PFLAG_INVALIDATED,即没有重绘过
		//4、fullInvalidate为true同时不透明度变化了
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
                || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
                || (fullInvalidate && isOpaque() != mLastIsOpaque)) {
   
            if (fullInvalidate) {
   
            	//保存不透明度
                mLastIsOpaque = isOpaque();
                //去掉PFLAG_DRAWN标志位
                mPrivateFlags &= ~PFLAG_DRAWN;
            }
			
			//添加PFLAG_DIRTY标志位
            mPrivateFlags |= PFLAG_DIRTY;
			
			//【分析点1】
			//如果invalidateCache为true,通常情况下我们调用invalidate()时		
			//invalidateCache都会为true
			//1、添加PFLAG_INVALIDATED标志位
			//2、去除PFLAG_DRAWING_CACHE_VALID标志位
            if (invalidateCache) {
   
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }

            
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            //【分析点2】
            //1、从AttachInfo里面得到一块dirty区域,这块区域就是需要刷新的
            //2、如果ViewParent !=null,调用ViewParent的invalidateChild,这也就是为什么在主线程
            //的onCreate刷新UI并不会因为ViewRootImpl不存在而报错,因为p==null
            if (p != null && ai != null && l < r && t < b) {
   
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                p.invalidateChild(this, damage);
            }

           	...
        }
    }

3、ViewGroup # invalidateChild

	//注意注解@Deprecated
    @Deprecated
    @Override
    public final void invalidateChild(View child, final Rect dirty) {
   
        final AttachInfo attachInfo = mAttachInfo;
        
        //==============分支1====================//
        
        //【分析点3】
        //如果开了硬件加速的话,是直接调用onDescendantInvalidated
        //和软件绘制一样,也是不断向上调用,直到调用ViewRootImpl
        if (attachInfo != null && attachInfo.mHardwareAccelerated) {
   
            // HW accelerated fast path
            onDescendantInvalidated(child, child);
            return;
        }


		
		//==============分支2====================//		

		//【分析点4】
		//没有开启硬件加速的逻辑
        ViewParent parent = this;
        if (attachInfo != null) {
   
            
            //【分析点5】
            //要注意,开启了硬件加速是不会走这里的!!
            //设置了layerType并不代表开启了硬件加速,它们直接没有直接关系的(但是有联系)
            //可以理解为:view可以被绘制到off-screen buffers(离屏缓冲区),layer就是一个buffer
            //layerType分成三种:
            //1、LAYER_TYPE_HARDWARE 
            //2、LAYER_TYPE_SOFTWARE
            //3、LAYER_TYPE_NONE
            //默认的layerType为LAYER_TYPE_NONE
            //所以这里是设置了layerType的情况下,才会做以下两件事:
            //1、为当前ViewGroup添加PFLAG_INVALIDATED标志位
            //2、清除PFLAG_DRAWING_CACHE_VALID标志位
            if (child.mLayerType != LAYER_TYPE_NONE) {
   
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }

            //省略设置dirty区域大小的代码
            ...

            do {
   
                View view = null;
                if (parent instanceof View) {
   
                    view = (View) parent;
                }

				...

				//【分析点6】
				//通过do-while不断调用invalidateChildInParent
				//直到ViewRootImpl
                parent = parent.invalidateChildInParent(location, dirty);
                
				...
			
            } while (parent != null);
        }
    }

分支1:开启硬件加速

1、ViewGroup # onDescendantInvalidated

    @Override
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值