目录

  • 1. addOnPreDrawListener
  • 2. postFrameCallback
  • 3. setOnHierarchyChangeListener
  • 4. setOnApplyWindowInsetsListener
  • 5. SynchronizedPool
  • 6. setWillNotDraw

不定期更新一些看源码时候常用的但平时基本用不上的东西

1. addOnPreDrawListener

addOnPreDrawListener方法是在View绘制前回调的方法,在CoordinatorLayoutbehavioronDependentViewChanged的调用场景之一会在这里

 boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;        if (!cancelDraw) {           ...            performDraw();        } else {            if (isViewVisible) {                scheduleTraversals();            }            ...        } public final boolean dispatchOnPreDraw() {        boolean cancelDraw = false;        final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;        if (listeners != null && listeners.size() > 0) {            CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();            try {                int count = access.size();                for (int i = 0; i < count; i++) {                    cancelDraw |= !(access.get(i).onPreDraw());                }            } finally {                listeners.end();            }        }        return cancelDraw;    }

使用方式是

View view;view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {            @Override            public boolean onPreDraw() {                return false;            }        });

注意因为是在performDraw前触发的,前面的performMeasureperformLayout已经完成,这里也是可以获取视图尺寸大小的

2. postFrameCallback

使用方式

 Choreographer choreographer = Choreographer.getInstance();        choreographer.postFrameCallback(new Choreographer.FrameCallback() {            @Override            public void doFrame(long frameTimeNanos) {            }        });

这个是渲染帧的回调,下一次渲染帧到达时会回调这个方法,只会触发一次。
这里所谓的渲染帧指的是屏幕刷新的时间点,因为有同步机制,屏幕会在每16ms左右进行一次刷新,涉及到CPU和GPU绘制,以及SurfeFlinger的双缓冲技术,有兴趣可以查一下。这里的话就是一下次渲染到达时的回调处理。

3. setOnHierarchyChangeListener

ViewGroup的子View添加和删除的监听方法,比如会在addViewremoveView方法中回调这个监听

 ViewGroup view;view.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {           @Override           public void onChildViewAdded(View parent, View child) {           }           @Override           public void onChildViewRemoved(View parent, View child) {           }       })

4. setOnApplyWindowInsetsListener

ImageView imageView;imageView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {            @Override            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {                return insets.consumeSystemWindowInsets();            }        });

setOnApplyWindowInsetsListener 方法回传的是window的内容区域限制,只有当设置了fitsSystemWindows才会生效。

定义了fitSystemWindows后,会从ViewRootImp出发,调用dispatchApplyInsets方法依次遍历DecorViewView,给设置过这个属性的子View设置padding,比如这里测试机返回的系统限制getSystemWindowInsets()的四个区域是(0,50,0,0),而这里的50也正是状态栏高度的大小,如果有导航栏则bottom的数值也会有变动。

一般多个View都设置的话会以第一个为准生效,这里的insets.consumeSystemWindowInsets()表示消耗掉这个WindowInsets,这个也是默认方法,可以选择不消耗,那么这个WindowInsets就会交付给下一个查找到的有fitSystemWindows的属性的View处理

注意这个返回的getSystemWindowInsets的各个属性都是final类型的,不能修改

可以使用ViewCompat.requestApplyInsets(View)触发一次检测,这个其实最终就会调用到ViewRootImpscheduleTraversals方法,重新触发测量布局绘制三大流程,只是这里把mApplyInsetsRequested设置成了true,可以触发dispatchApplyInsets方法

5. SynchronizedPool

享元对象池,对于需要频繁创建对象的可以用一下,acquire去获取池中的对象,release去存一个新的对象。

  Pools.Pool<TestJava> sPool = new Pools.SynchronizedPool<>(3);        String TAG = "1234";        TestJava t1 = new TestJava("1");        TestJava t2 = new TestJava("2");        TestJava t3 = new TestJava("3");        sPool.release(t1);        sPool.release(t2);        sPool.release(t3);        TestJava tr1 = sPool.acquire();        Log.e(TAG, "tr1 ->> " + (tr1 != null ? tr1.id : -1));        TestJava tr2 = sPool.acquire();        Log.e(TAG, "tr2 ->> " + (tr2 != null ? tr2.id : -1));        TestJava tr3 = sPool.acquire();        Log.e(TAG, "tr3 ->> " + (tr3 != null ? tr3.id : -1));        TestJava tr4 = sPool.acquire();        Log.e(TAG, "tr4 ->> " + (tr4 != null ? tr4.id : -1));

这里的acquire方法会把对象从池中移除,当用完需要把这个对象release再放进去
打印结果是

E/1234: tr1 ->> 3E/1234: tr2 ->> 2E/1234: tr3 ->> 1E/1234: tr4 ->> -1

SimplePool和这个是一样的,只是SynchronizedPool的方法使用了同步加锁

6. setWillNotDraw

当我们定义ViewGroup的子类时,默认是不会走onDraw绘制方法的,因为在ViewGroup中有这个标记

 private void initViewGroup() {        if (!debugDraw()) {            setFlags(WILL_NOT_DRAW, DRAW_MASK);        }        ......}        

而且在View

void setFlags(int flags, int mask) {......  if ((changed & DRAW_MASK) != 0) {            if ((mViewFlags & WILL_NOT_DRAW) != 0) {                if (mBackground != null                        || mDefaultFocusHighlight != null                        || (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {                    mPrivateFlags &= ~PFLAG_SKIP_DRAW;                } else {                    mPrivateFlags |= PFLAG_SKIP_DRAW;                }            } else {                mPrivateFlags &= ~PFLAG_SKIP_DRAW;            } }                   //这个代码块有好几处调用if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {        dispatchDraw(canvas);        ......        } else {         draw(canvas);    } public void setWillNotDraw(boolean willNotDraw) {        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);    }

draw方法中会触发onDraw,也会触发dispatchDraw方法;而dispatchDraw是完全给子类重写的,这应该是性能上的考虑,避免不必要的绘制

这么一来,因为ViewGroup默认设置了WILL_NOT_DRAW标记,导致大部分情况下是不会走onDraw方法的

setWillNotDraw(false)可以清除这个标记,回调onDraw方法

更多相关文章

  1. 深入理解Android(安卓)Notifiction机制
  2. Android(安卓)RecyclerView 间距全适配
  3. Android(安卓)1.5 自带的图标一览表
  4. Android监听HOME键的最简单的方法
  5. android 实现圆形进度条
  6. Android(安卓)DrawBitmap绘制图像
  7. Android(安卓)MediaPlayer 常用方法介绍
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. 视频专辑: 善知堂android 4.0.3 就业视频
  2. 给Activity切换加入动画
  3. 知识储备:Android系统架构
  4. android 1.5 NDK发布
  5. qpython3:安卓上运行Python
  6. Android(安卓)View Attributes
  7. Android启动模式之singleTask解析
  8. TextView使用完全讲解
  9. Android 异步Http框架简介和实现原理
  10. Android中设置控件可见与不可见