渲染机制,绘制--android

我们要知道Android系统每隔16ms就重新绘制一次Activity,也就是说,我们的应用必须在16ms内完成屏幕刷新的全部逻辑操作,即每一帧只能停留16ms,不然用户感觉卡顿。

CPU与GPU。CPU负责包括Measure,Layout,Record,Execute的计算操作,GPU 负责Rasterization(栅格化)操作。栅格化就是解决那些复杂的XML布局文件和标记语言,使之转化成用户能看懂的图像,但是这不是直接转换的,XML布局文件需要在CPU中首先转换为多边形或者纹理,然后再传递给GPU进行格栅化,对于栅格化,跟OpenGL有关,格栅化是一个特别费时的操作。

过度绘制及解决-一般超过三层就是过度绘制
方案一:布局层级优化

层级的优化我们应该注意多方面:

Hierarchy Viewer工具使用,他是查看耗时情况,和布局树的深度的工具。

在开发中,ViewStub,Merge标签的使用,RelativeLayout的使用等等。其实这一部分就是考验你布局的根底了。如果可以的话,约束布局ConstraintLayout也是一个不错的选择。

方案二:

去掉window的默认背景

当我们使用了Android自带的一些主题时,window会被默认添加一个纯色的背景,这个背景是被DecorView持有的。当我们的自定义布局时又添加了一张背景图或者设置背景色,那么DecorView的background此时对我们来说是无用的,但是它会产生一次Overdraw,带来绘制性能损耗。去掉window的背景可以在onCreate()中setContentView()之后调用getWindow().setBackgroundDrawable(null);或者在theme中添加android:windowbackground=”null”;

方案三:去掉其他不必要的背景

有时候为了方便会先给Layout设置一个整体的背景,再给子View设置背景,这里也会造成重叠,如果子View宽度mach_parent,可以看到完全覆盖了Layout的一部分,这里就可以通过分别设置背景来减少重绘。再比如如果采用的是selector的背景,将normal状态的color设置为“@android:color/transparent”,也同样可以解决问题。这里只简单举两个例子,我们在开发过程中的一些习惯性思维定式会带来不经意的Overdraw,所以开发过程中我们为某个View或者ViewGroup设置背景的时候,先思考下是否真的有必要,或者思考下这个背景能不能分段设置在子View上,而不是图方便直接设置在根View上。

方案四:当背景无法避免,尽量用Color.TRANSPARENT

浅谈硬件加速


硬件加速,直观上说就是依赖GPU实现图形绘制加速,软硬件加速的区别主要是图形的绘制究竟是GPU来处理还是CPU,如果是GPU,就认为是硬件加速绘制,反之,软件绘制。大概从Android 4.+开始,默认情况下都是支持跟开启了硬件加速的,也存在手机支持硬件加速,但是部分API不支持硬件加速的情况,如果使用了这些API,就需要主关闭硬件加速,或者在View层,或者在Activity层,比如Canvas的clipPath等。但是,View的绘制是软件加速实现的还是硬件加速实现的,一般在开发的时候并不可见,那图形绘制的时候,软硬件的分歧点究竟在哪呢?

2.View的绘制流程

一个view要显示在界面上,需要经历一个view树的遍历过程,这个过程又可以分为三个过程,分别是:

  • 测量measure 确定一个View的大小
  • 布局layout 确定view在父节点上的位置
  • 绘制draw 绘制view 的内容

1、第一步:背景绘制
看注释即可,不是重点

private void drawBackground(Canvas canvas) {
     Drawable final Drawable background = mBackground;
      ......
     //mRight - mLeft, mBottom - mTop layout确定的四个点来设置背景的绘制区域
     if (mBackgroundSizeChanged) {
        background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);  
        mBackgroundSizeChanged = false; rebuildOutline();
     }
     ......
     //调用Drawable的draw() 把背景图片画到画布上
     background.draw(canvas);
     ......
}

第三步,对View的内容进行绘制。
onDraw(canvas) 方法是view用来draw 自己的,具体如何绘制,颜色线条什么样式就需要子View自己去实现,View.java 的onDraw(canvas) 是空实现,ViewGroup 也没有实现,每个View的内容是各不相同的,所以需要由子类去实现具体逻辑。
第四步 对当前View的所有子View进行绘制
dispatchDraw(canvas)是用来绘制子View的,View.java 的dispatchDraw()方法是一个空方法,因为View没有子View,不需要实现dispatchDraw ()方法,ViewGroup就不一样了,它实现了dispatchDraw方法:

@Override
 protected void dispatchDraw(Canvas canvas) {
       ...
        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        } else {
            for (int i = 0; i < count; i++) {
                final View child = children[getChildDrawingOrder(count, i)];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        }
      ......
    }

代码一眼看出,就是遍历子View然后drawChild(),drawChild()方法实际调用的是子View.draw()方法,ViewGroup类已经为我们实现绘制子View的默认过程,这个实现基本能满足大部分需求,所以ViewGroup类的子类(LinearLayout,FrameLayout)也基本没有去重写dispatchDraw方法,我们在实现自定义控件,除非比较特别,不然一般也不需要去重写它, drawChild()的核心过程就是为子视图分配合适的cavas剪切区,剪切区的大小正是由layout过程决定的,而剪切区的位置取决于滚动值以及子视图当前的动画。设置完剪切区后就会调用子视图的draw()函数进行具体的绘制了。
第六步 对View的滚动条进行绘制
不是重点,知道有这东西就行,onDrawScrollBars 的一句注释 :Request the drawing of the horizontal and the vertical scrollbar. The scrollbars are painted only if they have been awakened first.

一张图看下整个draw的递归流程

发布了77 篇原创文章 · 获赞 3 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/haiyang497661292/article/details/85705698