Draw对应的方法就叫draw()。是在view中提供。谁先加入到布局中谁先画。画的层级绝对不能变。因为draw直接影响着用户的视觉效果。先画的会被后画的覆盖掉。画的时候如果是一个ViewGroup,会先画自己在画子view.所以draw有绘制流程,就在draw()方法中写死了。所以我们不能更改draw()方法。和layout()方法一样,draw()方法也允许我们重写。但是不建议。透过draw()源码,我们可以看到源码中的注释已经告诉我们draw()的流程。
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
第一步先绘制背景(调用drawBackground(canvas);)。
drawBackground就是把一个背景图片,如果设置的是颜色,系统也会把color转化成bitmap。然后大小就和之前测量出来的大小一致。绘制的时候会更改背景图片的bounds,无论背景图片的bounds有多大,它都会按测量出来的大小进行绘制,背景图片就有可能压缩或者拉伸。而背景图片一定会被全部显示出来的。
第二步和第五步是做一些准备,做一些我们现在已经不用的事情。这个可以不用掌握,这种效果我们已经不再用了。是在android 2.x时代有的效果。边缘模糊效果。我们重点来看第3步和第4步。
第三步是绘制view自身。
制自身时的实现都在onDraw()方法中,View的源码中是个空实现,需要子view去继承实现。
第四步是绘制子view。
绘制子view的代码实现方法是在dispatchDraw(canvas);中,View的源码中是个空实现,需要ViewGroup去继承实现。
最后还有第六步,是最后绘制的,滚动条。
在 onDrawForeground(canvas);中,View的源码中有具体逻辑,所有的view都有scrollbar,默认不显示。
这些都不是我们要重点掌握的,我们重点掌握的是dispatchDraw(canvas);方法。这个方法负责绘制子view,同时负责子view的绘制流程、动画效果、滚动视图。里面的逻辑非常复杂。这个方法是在ViewGroup中实现的。也不建议大家去复写。
方法的一开始就判断它本身有没有动画:
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
而这个动画指的是补间动画。属性动画不在这里处理。
之后是padding检查:
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
接下来是绘制子view:
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
transientIndex++;
if (transientIndex >= transientCount) {
transientIndex = -1;
}
}
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
到这里就调用了view的 draw(Canvas canvas, ViewGroup parent, long drawingTime) 方法。其中就是先判断view有没有动画:
final Animation a = getAnimation();
如果有动画就设置偏移量、缩放值等等。
处理完动画,就处理滑动。
if (!drawingWithRenderNode) {
computeScroll();
sx = mScrollX;
sy = mScrollY;
}
view对draw部分进行自定义,复写onDraw()方法。
viewGroup是复写dispatchDraw()方法绘制,因为onDraw()方法要求必须有内容。