Drawable的draw方法的作用是将Canvas(画布)的内容绘制到Drawable中,然后通过View的setBackground(Drawable background)方法设置View的背景,Drawable的draw方法代码如下:
//frameworks/base/graphics/java/android/graphic/drawable/Drawable.java
public abstract class Drawable {
public abstract void draw(@NonNull Canvas canvas);
}
GradientDrawable draw
Drawable是一个抽象类,draw方法是一个抽象方法,绘制View背景常用的是Drawable包括ColorDrawable、GradientDrawable、BitmapDrawable等,其中GradientDrawable是一种常用的Drawable类型,用于绘制各种形状的背景,下面我们继续分析GradientDrawable的draw方法:
//frameworks/base/graphics/java/android/graphic/drawable/GradientDrawable.java
public class GradientDrawable extends Drawable {
private Paint mLayerPaint;
private Paint mStrokePaint; // optional, set by the caller
private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public void draw(Canvas canvas) {
if (!ensureValidRect()) {
// nothing to draw
return;
}
// remember the alpha values, in case we temporarily overwrite them
// when we modulate them with mAlpha
final int prevFillAlpha = mFillPaint.getAlpha();
final int prevStrokeAlpha = mStrokePaint != null ? mStrokePaint.getAlpha() : 0;
// compute the modulate alpha values
final int currFillAlpha = modulateAlpha(prevFillAlpha);
final int currStrokeAlpha = modulateAlpha(prevStrokeAlpha);
final boolean haveStroke = currStrokeAlpha > 0 && mStrokePaint != null &&
mStrokePaint.getStrokeWidth() > 0;
final boolean haveFill = currFillAlpha > 0;
final GradientState st = mGradientState;
final ColorFilter colorFilter = mColorFilter != null ? mColorFilter : mBlendModeColorFilter;
/* we need a layer iff we're drawing both a fill and stroke, and the
stroke is non-opaque, and our shapetype actually supports
fill+stroke. Otherwise we can just draw the stroke (if any) on top
of the fill (if any) without worrying about blending artifacts.
*/
final boolean useLayer = haveStroke && haveFill && st.mShape != LINE &&
currStrokeAlpha < 255 && (mAlpha < 255 || colorFilter != null);
/* Drawing with a layer is slower than direct drawing, but it
allows us to apply paint effects like alpha and colorfilter to
the result of multiple separate draws. In our case, if the user
asks for a non-opaque alpha value (via setAlpha), and we're
stroking, then we need to apply the alpha AFTER we've drawn
both the fill and the stroke.
*/
if (useLayer) {
if (mLayerPaint == null) {
mLayerPaint = new Paint();
}
mLayerPaint.setDither(st.mDither);
mLayerPaint.setAlpha(mAlpha);
mLayerPaint.setColorFilter(colorFilter);
float rad = mStrokePaint.getStrokeWidth();
canvas.saveLayer(mRect.left - rad, mRect.top - rad,
mRect.right + rad, mRect.bottom + rad,
mLayerPaint);
// don't perform the filter in our individual paints
// since the layer will do it for us
mFillPaint.setColorFilter(null);
mStrokePaint.setColorFilter(null);
} else {
/* if we're not using a layer, apply the dither/filter to our
individual paints
*/
mFillPaint.setAlpha(currFillAlpha);
mFillPaint.setDither(st.mDither);
mFillPaint.setColorFilter(colorFilter);
if (colorFilter != null && st.mSolidColors == null) {
mFillPaint.setColor(mAlpha << 24);
}
if (haveStroke) {
mStrokePaint.setAlpha(currStrokeAlpha);
mStrokePaint.setDither(st.mDither);
mStrokePaint.setColorFilter(colorFilter);
}
}
switch (st.mShape) {
case RECTANGLE:
if (st.mRadiusArray != null) {
buildPathIfDirty();
canvas.drawPath(mPath, mFillPaint);
if (haveStroke) {
canvas.drawPath(mPath, mStrokePaint);
}
} else if (st.mRadius > 0.0f) {
// since the caller is only giving us 1 value, we will force
// it to be square if the rect is too small in one dimension
// to show it. If we did nothing, Skia would clamp the rad
// independently along each axis, giving us a thin ellipse
// if the rect were very wide but not very tall
float rad = Math.min(st.mRadius,
Math.min(mRect.width(), mRect.height()) * 0.5f);
canvas.drawRoundRect(mRect, rad, rad, mFillPaint);
if (haveStroke) {
canvas.drawRoundRect(mRect, rad, rad, mStrokePaint);
}
} else {
if (mFillPaint.getColor() != 0 || colorFilter != null ||
mFillPaint.getShader() != null) {
canvas.drawRect(mRect, mFillPaint);
}
if (haveStroke) {
canvas.drawRect(mRect, mStrokePaint);
}
}
break;
case OVAL:
canvas.drawOval(mRect, mFillPaint);
if (haveStroke) {
canvas.drawOval(mRect, mStrokePaint);
}
break;
case LINE: {
RectF r = mRect;
float y = r.centerY();
if (haveStroke) {
canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
}
break;
}
case RING:
Path path = buildRing(st);
canvas.drawPath(path, mFillPaint);
if (haveStroke) {
canvas.drawPath(path, mStrokePaint);
}
break;
}
if (useLayer) {
canvas.restore();
} else {
mFillPaint.setAlpha(prevFillAlpha);
if (haveStroke) {
mStrokePaint.setAlpha(prevStrokeAlpha);
}
}
}
}
上面方法主要处理如下:
1、调用canvas(Canvas)的saveLayer方法保存Layer。
2、根据不同的参数调用canvas(Canvas)的drawPath方法绘制路径。
3、根据不同的参数调用canvas(Canvas)的drawRoundRect方法绘制绘制圆角矩形。
4、根据不同的参数调用canvas(Canvas)的drawRect方法绘制矩形。
5、根据不同的参数调用canvas(Canvas)的drawOval方法绘制椭圆。
6、根据不同的参数调用canvas(Canvas)的drawLine方法绘制线。
下面分别进行分析:
Canvas saveLayer
调用canvas(Canvas)的saveLayer方法保存Layer:
Android13 Canvas saveLayer流程分析-CSDN博客
Canvas drawPath
根据不同的参数调用canvas(Canvas)的drawPath方法绘制路径:
Android13 Canvas drawPath流程分析-CSDN博客
Canvas drawRoundRect
根据不同的参数调用canvas(Canvas)的drawRoundRect方法绘制绘制圆角矩形:
Android13 Canvas drawRoundRect流程分析-CSDN博客
Canvas drawRect
根据不同的参数调用canvas(Canvas)的drawRect方法绘制矩形:
Android13 Canvas drawRect流程分析-CSDN博客
Canvas drawOval
根据不同的参数调用canvas(Canvas)的drawOval方法绘制椭圆:
Android Canvas drawOval流程分析-CSDN博客
Canvas drawLine
根据不同的参数调用canvas(Canvas)的drawLine方法绘制线: