目录

  • 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中创建包含对象数组对象List 的Parcelable
  2. android EditText 不自动弹出键盘的方法
  3. android解析二维数组对象key:value
  4. Android Studio ADB响应失败解决方法
  5. Unity 调用 Android Native 方法(一) 获得Android系统音量
  6. Mac 下面,添加android adb命令(一般环境变量的添加方法)
  7. Android软键盘挡住输入框的问题及解决方法

随机推荐

  1. java版android Handler机制模型
  2. Android直播系统平台搭建之图片实现阴影
  3. Android实现雅虎新闻摘要加载视差动画效
  4. TextInput 需要注意的点
  5. 如何打造稳定、好用的 Android(安卓)Layo
  6. FFmpeg 调用 Android(安卓)MediaCodec 进
  7. Android(安卓)程序员的进阶之路(转)
  8. 开源分享!2021年最新 Android(安卓)资料汇
  9. Android(安卓)消息机制 你了解多少
  10. 一步教你超简单设置Android(安卓)Studio