1.view中包含的绘制元素.
view背景,任何view都有背景,可以是图片.色值或者drawable资源
视图本身内容. 在ondraw中绘制
渐变边框,本质是一个shader对象
滚动条,紧紧显示滚动的状态,任何的view都有滚动条.
2.设置思路
2.png
和之前的layout,measure过程都是类似的.draw方法不建议子视图重写. 首先是mView的draw函数被调用,一般来说mView就是窗户的根视图,对于Activity就是PhoneWindow.DecorView对象. 会调用View的draw方法,然后在onDraw中完成界面内容的绘制,如果还有子视图.就调用dispatchDraw函数,dispatchDraw由viewgroup实现了.也不建议我们来重写他.该方法内部循环调用drawchild()方法,其实就是调用子视图的draw方法完成绘制. 其实draw的过程就是把canvas从根部局不断剪裁,然后传给所有子视图利用canvas进行绘制的过程.
3.ViewRootImpl 中draw 的主要过程就是得到绘图用的Canvas.设置屏幕密度.调用mView.draw()函数.
4.View中draw()函数内部流程.
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* 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) 绘值装饰(例如滚动条)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {//1.dirtyOpaque表示dirty区是否透明,只有透明才需要绘制背景.
drawBackground(canvas);//绘制背景时会对canvas的左边进行调整,需要调整到该视图内部的原点.
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {//2表示没有渐变.跳过第二步
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);//3视图在这里实现自己内容的绘制
// Step 4, draw the children
dispatchDraw(canvas); //4viewgroup实现这个方法来绘制子视图
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas);//5绘制滚动条等装饰内容
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// we're done...
return;
}
// Step 2,这里是对渐变框计算的数值的处理,代码不看了
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas); //绘制视图
// Step 4, draw the children
dispatchDraw(canvas);//绘制内容
// Step 5, //利用step2步骤的值绘制渐变框,不看了
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas); //绘制滚动条
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
}
5.ViewGroup 绘制子视图 dispatchDraw 内部流程.
protected void dispatchDraw(Canvas canvas) {
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {//1.判断是否有布局动画标识,有的话就处理.该参数设置在viewgroup中.
layoutAnimation属性,是ViewGroup为所有子视图设置的动画
final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
for (int i = 0; i < childrenCount; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, childrenCount);
bindLayoutAnimation(child);
}
}
final LayoutAnimationController controller = mLayoutAnimationController;
controller.start();
}
int clipSaveCount = 0;//2.处理padding属性,剪切该视图的canvas.
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
clipSaveCount = canvas.save();
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
for (int i = 0; i < childrenCount; i++) {//3.遍历子视图,调用它的draw方法,此时的canvas的范围经过上一部的剪裁后的
//就是该视图内可供子视图绘制的区域.
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);//此时还会调用到view的draw方法,但是是三个参数的.和之前的不同.
}
}
//4.绘制被该视图remove但是扔在执行移除动画的子视图.
if (mDisappearingChildren != null) {
final ArrayList disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
//5.通知重绘界面
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
invalidate(true);
}
//6.通知布局动画绘制完成
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
mLayoutAnimationController.isDone() && !more) {
final Runnable end = new Runnable() {
public void run() {
notifyAnimationListener();
}
};
post(end);
}
}
6. View.draw(Canvas canvas, ViewGroup parent, long drawingTime)
这个方法是通过ViewGroup.drawChild()调用的,不建议被重写.核心过程就是为该视图分配合理的canvas剪裁区
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
final Animation a = getAnimation();//1.有动画就执行动画.没动画清除掉view变化的值.
if (a != null) {
more = drawAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
transformToApply = parent.getChildTransformation();
}
// 2.判断子视图的canvas剪裁区是否超出父视图的剪切区,如果超过.就不绘制.
if (!concatMatrix &&
(flags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
(mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
return more;
}
if (!hasDisplayList) {
computeScroll(); //3.计算视图的当前滚动值
sx = mScrollX;
sy = mScrollY;
}
if (offsetForScroll) {//3.通过计算的滚动值,将canvas的坐标原点由父视图的坐标原点改为子视图的左边原点
//注意.一个视图的坐标远点不总是他的左上角.因为如果视图的内容可以滚动,导致部分内容不可见.这种视图的坐标远点其实是他内容的左上角,
canvas.translate(mLeft - sx, mTop - sy);
} else {
if (!usingRenderNodeProperties) {
canvas.translate(mLeft, mTop);
}
if (scalingRequired) {
if (usingRenderNodeProperties) {
restoreTo = canvas.save();
}
// mAttachInfo cannot be null, otherwise scalingRequired == false
final float scale = 1.0f / mAttachInfo.mApplicationScale;
canvas.scale(scale, scale);
}
}
//4.剪裁canvas.使他匹配当前视图的.
if (!usingRenderNodeProperties) {
if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN
&& cache == null) {
if (offsetForScroll) {
canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
} else {
if (!scalingRequired || cache == null) {
canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
} else {
canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
}
}
}
}
//5.绘制视图.这里分有缓存和没缓存的处理, -代码不看了.
if (!layerRendered) {
if (!hasDisplayList) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
drawAccessibilityFocus(canvas);
} else {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
((HardwareCanvas) canvas).drawRenderNode(renderNode, null, flags);
}
}
}
return more;
}
3.png