Android显示框架:自定义View实践之绘制篇

文章目录

  • 一 View
  • 二 Paint
    • 2.1 颜色处理
    • 2.2 文字处理
    • 2.3 特殊处理
  • 三 Canvas
    • 3.1 界面绘制
    • 3.2 范围裁切
    • 3.3 集合变换
  • 四 Path
    • 4.1 添加图形
    • 4.3 画线(直线或曲线)
    • 4.3 辅助设置和计算

文章源码

本文还提供了三个综合性的完整实例来辅助理解。

  • View绘制 - 图片标签效果实现
  • Canvas绘图 - 水面涟漪效果实现
  • 二阶贝塞尔曲线的应用 - 杯中倒水效果实现

第一次阅览本系列文章,请参见导读,更多文章请参见文章目录

本篇文章我们来分析View绘制方面的实践。

一个简单的自定义View

public class DrawView extends View {

    Paint paint = new Paint();

    public DrawView(Context context) {
super(context);
} public DrawView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
} public DrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
canvas.drawCircle(150, 150, 150, paint);
}
}

它在屏幕上绘制了一个圆形,如图:

Android显示框架:自定义View实践之绘制篇
image.png

在处理绘制的时候有以下几个关键点:

  • 处理绘制需要重写绘制方法,常用的是View的onDraw(),当然我们也可以使用其他的绘制方法来处理遮盖关系。
  • 完成绘制的是Canvas类,该类提供了绘制系列方法drawXXX()。裁剪系列方法clipXXX()以及几何变换方法translate()方法,还有辅助绘制的Path与Matrix。
  • 定制绘制的是Paint类,该类是绘制所用的画笔,可以实现特殊的绘制效果。

我们分别来看看这个关键的角色。

一 View

我们讨论的第一个问题就是View/ViewGroup的绘制顺序问题,绘制在View.draw()方法里调用的,具体的执行顺序是:

  1. drawBackground():绘制背景,不能重写。
  2. onDraw():绘制主体。
  3. dispatchDraw():绘制子View
  4. onDrawForeground():绘制滑动边缘渐变、滚动条和前景。
Android显示框架:自定义View实践之绘制篇

我们先从个小例子开始。

我们如果继承View来实现自定义View。View类的onDraw()是空实现,所以我们的绘制代码写在super.onDraw(canvas)的前面或者后面都没有关系,如下所示:

public class DrawView extends View {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制代码,写在super.onDraw(canvas)前后均可
}
}

但是如果我们继承特定的控件,例如TextView。我们就需要去考虑TextView的绘制逻辑。

public class DrawView extends TextView {
@Override
protected void onDraw(Canvas canvas) { //写在前面,DrawView的绘制会先于TextView的绘制,TextView绘制的内容可以会覆盖DrawView
super.onDraw(canvas);
//写在后面,DrawView的绘制会晚于TextView的绘制,DrawView绘制的内容可以会覆盖TextView
}
}
  • 写在前面,DrawView的绘制会先于TextView的绘制,TextView绘制的内容可以会覆盖DrawView
  • 写在后面,DrawView的绘制会晚于TextView的绘制,DrawView绘制的内容可以会覆盖TextView

具体怎么做取决于你实际的需求,例如你如果想给TextView加个背景,就写在super.onDraw(canvas)前面,想给TextView前面加些点缀,就
写在super.onDraw(canvas)后面。

我们来写个例子理解下。

举例

Android显示框架:自定义View实践之绘制篇
image.png
public class LabelImageView extends AppCompatImageView {

    /**
* 梯形距离左上角的长度
*/
private static final int LABEL_LENGTH = 100;
/**
* 梯形斜边的长度
*/
private static final int LABEL_HYPOTENUSE_LENGTH = 100; private Paint textPaint;
private Paint backgroundPaint;
private Path pathText;
private Path pathBackground; public LabelImageView(Context context) {
super(context);
init();
} public LabelImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
} public LabelImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算路径
calculatePath(getMeasuredWidth(), getMeasuredHeight());
canvas.drawPath(pathBackground, backgroundPaint);
canvas.drawTextOnPath("Hot", pathText, 100, -20, textPaint);
} @Override
public void onDrawForeground(Canvas canvas) {
super.onDrawForeground(canvas);
} /**
* 计算路径 x1 x2
* ................................ distance(标签离右上角的垂直距离)
* . . . .
* . . .. y1
* . . .
* . . .
* . . y2 height(标签垂直高度)
* . .
* ................................
*/
private void calculatePath(int measuredWidth, int measuredHeight) { int top = 185;
int right = measuredWidth; float x1 = right - LABEL_LENGTH - LABEL_HYPOTENUSE_LENGTH;
float x2 = right - LABEL_HYPOTENUSE_LENGTH;
float y1 = top + LABEL_LENGTH;
float y2 = top + LABEL_LENGTH + LABEL_HYPOTENUSE_LENGTH; pathText.reset();
pathText.moveTo(x1, top);
pathText.lineTo(right, y2);
pathText.close(); pathBackground.reset();
pathBackground.moveTo(x1, top);
pathBackground.lineTo(x2, top);
pathBackground.lineTo(right, y1);
pathBackground.lineTo(right, y2);
pathBackground.close();
} private void init() {
pathText = new Path();
pathBackground = new Path(); textPaint = new Paint();
textPaint.setTextSize(50);
textPaint.setFakeBoldText(true);
textPaint.setColor(Color.WHITE); backgroundPaint = new Paint();
backgroundPaint.setColor(Color.RED);
backgroundPaint.setStyle(Paint.Style.FILL);
}
}

所以你可以看到,当我们继承了一个View,根据需求的不同可以选择性重写我们需要的方法,在super前插入代码和在super后插入代码,效果是不一样的。

  • draw():super.draw()之前,被背景盖住;super.draw()后,盖住前景;
  • onDraw():super.onDraw()之前,背景与主体内容之前;super.onDraw()之后,主体内容和子View之间;
  • dispatchDraw():super.dispatchDraw()之前,主体内容和子View之间;super.dispatchDraw()之后,子View和前景之间;
  • onDrawForeground():super.onDrawForeground()之前,子View和前景之间;super.onDrawForeground()之后,盖住前景;

二 Paint

Paint:顾名思义,画笔,通过Paint可以对绘制行为进行控制。

Paint有三种构造方法

public class Paint {
//空的构造方法
public Paint() {
this(0);
} //传入flags来构造Paint,flags用来控制Paint的行为,例如:抗锯齿等
public Paint(int flags) {
mNativePaint = nInit();
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint);
setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
// setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
// ? HINTING_OFF : HINTING_ON);
mCompatScaling = mInvCompatScaling = 1;
setTextLocales(LocaleList.getAdjustedDefault());
} //传入另外一个Paint来构造新的Paint
public Paint(Paint paint) {
mNativePaint = nInitWithPaint(paint.getNativeInstance());
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint);
setClassVariablesFrom(paint);
}
}

2.1 颜色处理类

在Paint类中,处理颜色主要有三个方法。

  • setShader(Shader shader):用来处理颜色渐变
  • setColorFilter(ColorFilter filter):用来基于颜色进行过滤处理;
  • setXfermode(Xfermode xfermode) 用来处理源图像和 View 已有内容的关系

setShader(Shader shader)

着色器是图像领域的一个通用概念,它提供的是一套着色规则。

public Shader setShader(Shader shader)

着色器具体由Shader的子类实现:

LinearGradient - 线性渐变

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile)
  • x0 y0 x1 y1:渐变的两个端点的位置
  • color0 color1 是端点的颜色
  • tile:端点范围之外的着色规则,类型是 TileMode。TileMode 一共有 3 个值可选: CLAMP, MIRROR 和 REPEAT。CLAMP

举例

Android显示框架:自定义View实践之绘制篇
image.png
//线性渐变
Shader shader1 = new LinearGradient(0, 100, 200, 100, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
paint1.setShader(shader1); Shader shader2 = new LinearGradient(0, 600, 200, 600, Color.RED, Color.BLUE, Shader.TileMode.MIRROR);
paint2.setShader(shader2); Shader shader3 = new LinearGradient(0, 1100, 200, 1100, Color.RED, Color.BLUE, Shader.TileMode.REPEAT);
paint3.setShader(shader3); canvas.drawRect(0, 100, 1000, 500, paint1);
canvas.drawRect(0, 600, 1000, 1000, paint2);
canvas.drawRect(0, 1100, 1000, 1500, paint3);

SweepGradient - 辐射渐变

public RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, @NonNull TileMode tileMode)
  • centerX centerY:辐射中心的坐标
  • radius:辐射半径
  • centerColor:辐射中心的颜色
  • edgeColor:辐射边缘的颜色
  • tileMode:辐射范围之外的着色模式

举例

Android显示框架:自定义View实践之绘制篇
image.png
//辐射渐变
Shader shader1 = new RadialGradient(0, 100, 200, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
paint1.setShader(shader1); Shader shader2 = new RadialGradient(0, 600, 200, Color.RED, Color.BLUE, Shader.TileMode.MIRROR);
paint2.setShader(shader2); Shader shader3 = new RadialGradient(0, 1100, 200, Color.RED, Color.BLUE, Shader.TileMode.REPEAT);
paint3.setShader(shader3); canvas.drawRect(0, 100, 1000, 500, paint1);
canvas.drawRect(0, 600, 1000, 1000, paint2);

BitmapShader - 位图着色

使用位图的像素来填充图形或者文字。

 public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
  • bitmap:用来做模板的 Bitmap 对象
  • tileX:横向的 TileMode
  • tileY:纵向的 TileMode。

举例

BitmapShader是一个很有用的类,可以利用该类做各种各样的图片裁剪。

Android显示框架:自定义View实践之绘制篇
image.png
//位图着色
Shader shader1 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint1.setShader(shader1); //绘制圆形
canvas.drawCircle(500, 500, 300, paint1);

ComposeShader - 组合Shader

ComposeShader可以将连个Shader组合在一起。

public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
  • shaderA, shaderB:两个相继使用的 Shader
  • mode: 两个 Shader 的叠加模式,即 shaderA 和 shaderB 应该怎样共同绘制。它的类型是PorterDuff.Mode。

PorterDuff.Mode用来指定两个Shader叠加时颜色的绘制策略,它有很多种策略,也就是以一种怎样的模式来与原图像进行合成,具体如下:

蓝色矩形为原图像,红色圆形为目标图像。

Android显示框架:自定义View实践之绘制篇
Android显示框架:自定义View实践之绘制篇

更多细节可以参见PorterDuff.Mode官方文档

setColorFilter(ColorFilter filter)

颜色过滤器可以将颜色按照一定的规则输出,常见于各种滤镜效果。

public ColorFilter setColorFilter(ColorFilter filter)

我们通常使用的是ColorFilter的三个子类:

LightingColorFilter - 模拟光照效果

public LightingColorFilter(int mul, int add)

mul 和 add 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加。

举例

Android显示框架:自定义View实践之绘制篇
image.png
//颜色过滤器
ColorFilter colorFilter1 = new LightingColorFilter(Color.RED, Color.BLUE);
paint2.setColorFilter(colorFilter1); canvas.drawBitmap(bitmapTimo, null, rect1, paint1);
canvas.drawBitmap(bitmapTimo, null, rect2, paint2);

PorterDuffColorFilter - 模拟颜色混合效果

public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)

PorterDuffColorFilter指定一种颜色和PorterDuff.Mode来与源图像就行合成,也就是以一种怎样的模式来与原图像进行合成,我们在上面已经讲过这个内容。

举例

//我们在使用Xfermode的时候也是使用它的子类PorterDuffXfermode
Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
paint.setXfermode(xfermode); // 设置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
paint.setXfermode(null); // 用完及时清除 Xfermode

ColorMatrixColorFilter - 颜色矩阵过滤

ColorMatrixColorFilter使用一个颜色矩阵ColorMatrix来对象图像进行处理。

public ColorMatrixColorFilter(ColorMatrix matrix)

ColorMatrix是一个4x5的矩阵

[ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]

通过计算,ColorMatrix可以对要绘制的像素进行转换,如下:

R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;

利用ColorMatrixColorFilter(可以实现很多炫酷的滤镜效果。

setXfermode(Xfermode xfermode)

Paint.setXfermode(Xfermode xfermode)方法,它也是一种混合图像的方法。

Xfermode 指的是你要绘制的内容和 Canvas 的目标位置的内容应该怎样结合计算出最终的颜色。但通俗地说,其实就是要你以绘制的内容作为源图像,以View中已有的内
容作为目标图像,选取一个PorterDuff.Mode作为绘制内容的颜色处理方案。

小结

关于PorterDuff.Mode,我们已经提到

  • ComposeShader:混合两个Shader
  • PorterDuffColorFilter:增加一个单色的ColorFilter
  • Xfermode:指定原图像与目标图像的混合模式

这三种以不同的方式来使用PorterDuff.Mode,但是原理都是一样的。

2.2 文字处理类

Paint里有大量方法来设置文字的绘制属性,事实上文字在Android底层是被当做图片来处理的。

  • setTextSize(float textSize):设置文字大小
  • setTypeface(Typeface typeface):设置文字字体
  • setFakeBoldText(boolean fakeBoldText):是否使用伪粗体(并不是提到size,而是在运行时描粗的)
  • setStrikeThruText(boolean strikeThruText):是否添加删除线
  • setUnderlineText(boolean underlineText):是否添加下划线
  • setTextSkewX(float skewX):设置文字倾斜度
  • setTextScaleX(float scaleX):设置文字横向缩放
  • setLetterSpacing(float letterSpacing):设置文字间距
  • setFontFeatureSettings(String settings):使用CSS的font-feature-settings的方式来设置文字。
  • setTextAlign(Paint.Align align):设置文字对齐方式
  • setTextLocale(Locale locale):设置文字Local
  • setHinting(int mode):设置字体Hinting(微调),过向字体中加入 hinting 信息,让矢量字体在尺寸过小的时候得到针对性的修正,从而提高显示效果。
  • setSubpixelText(boolean subpixelText):设置次像素级抗锯齿,根据程序所运行的设备的屏幕类型,来进行针对性的次像素级的抗锯齿计算,从而达到更好的抗锯齿效果。

2.3 特殊效果类

setAntiAlias (boolean aa)

设置抗锯齿,默认关闭,用来是图像的绘制更加圆润。我们还可以在初始化的时候设置Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);。

setStyle(Paint.Style style)

设置填充风格,

  • FILL 模式,填充
  • STROKE 模式,画线
  • FILL_AND_STROKE 模式,填充 + 画线

如果是划线模式,我们针对线条还可以有多种设置。

setStrokeWidth(float width) - 设置线条粗细

setStrokeCap(Paint.Cap cap) - 设置线头的形状,默认为 BUTT

  • UTT 平头
  • ROUND 圆头
  • SQUARE 方头

setStrokeJoin(Paint.Join join) - 设置拐角的形状。默认为 MITER

  • MITER 尖角
  • BEVEL 平角
  • ROUND 圆角

setStrokeMiter(float miter)- 设置 MITER 型拐角的延长线的最大值

setDither(boolean dither)

设置图像的抖动。

Android显示框架:自定义View实践之绘制篇
image.png

抖动是指把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。

当然这个效果旨在低位色的时候比较有用,例如,ARGB_4444 或者 RGB_565,不过现在Android默认的色彩深度都是32位的ARGB_8888,这个方法的效果没有那么明显。

setFilterBitmap(boolean filter)

设置是否使用双线性过滤来绘制 Bitmap 。

Android显示框架:自定义View实践之绘制篇
image.png

图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。

etPathEffect(PathEffect effect)

设置图形的轮廓效果。Android有六种PathEffect:

  • CornerPathEffect:将拐角绘制成圆角
  • DiscretePathEffect:将线条进行随机偏离
  • DashPathEffect:绘制虚线
  • PathDashPathEffect:使用指定的Path来绘制虚线
  • SumPathEffect:组合两个PathEffect,叠加应用。
  • ComposePathEffect:组合两个PathEffect,叠加应用。

CornerPathEffect(float radius)

  • float radius圆角半径

DiscretePathEffect(float segmentLength, float deviation)

  • float segmentLength:用来拼接每个线段的长度,
  • float deviation:偏离量

DashPathEffect(float[] intervals, float phase)

  • float[] intervals:指定了虚线的格式,数组中元素必须为偶数(最少是 2 个),按照「画线长度、空白长度、画线长度、空白长度」……的顺序排列
  • float phase:虚线的偏移量

PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style)

  • Path shape:用来绘制的Path
  • float advance:两个相邻Path段起点间的间隔
  • float phase:虚线的偏移量
  • PathDashPathEffect.Style style:指定拐弯改变的时候 shape 的转换方式:TRANSLATE:位移、ROTATE:旋转、MORPH:变体

SumPathEffect(PathEffect first, PathEffect second)

  • PathEffect first:同时应用的PathEffect
  • PathEffect second:同时应用的PathEffect

ComposePathEffect(PathEffect outerpe, PathEffect innerpe)

  • PathEffect outerpe:后应用的PathEffect
  • PathEffect innerpe:先应用的PathEffect

举例

Android显示框架:自定义View实践之绘制篇
image.png
//图形轮廓效果
//绘制圆角
PathEffect cornerPathEffect = new CornerPathEffect(20);
paint1.setStyle(Paint.Style.STROKE);
paint1.setStrokeWidth(5);
paint1.setPathEffect(cornerPathEffect); //绘制尖角
PathEffect discretePathEffect = new DiscretePathEffect(20, 5);
paint2.setStyle(Paint.Style.STROKE);
paint2.setStrokeWidth(5);
paint2.setPathEffect(discretePathEffect); //绘制虚线
PathEffect dashPathEffect = new DashPathEffect(new float[]{20,10, 5, 10}, 0);
paint3.setStyle(Paint.Style.STROKE);
paint3.setStrokeWidth(5);
paint3.setPathEffect(dashPathEffect); //使用path来绘制虚线
Path path = new Path();//画一个三角来填充虚线
path.lineTo(40, 40);
path.lineTo(0, 40);
path.close();
PathEffect pathDashPathEffect = new PathDashPathEffect(path, 40, 0, PathDashPathEffect.Style.TRANSLATE);
paint4.setStyle(Paint.Style.STROKE);
paint4.setStrokeWidth(5);
paint4.setPathEffect(pathDashPathEffect);

setShadowLayer(float radius, float dx, float dy, int shadowColor)

设置阴影图层,处于目标下层图层。

  • float radius:阴影半径
  • float dx:阴影偏移量
  • float dy:阴影偏移量
  • int shadowColor:阴影颜色

举例

Android显示框架:自定义View实践之绘制篇
image.png
paint1.setTextSize(200);
paint1.setShadowLayer(10, 0, 0, Color.RED);
canvas.drawText("Android", 80, 300 ,paint1);

注:在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己
的透明度;而如果 shadowColor 是不透明的,阴影的透明度就使用 paint 的透明度。

setMaskFilter(MaskFilter maskfilter)

设置图层遮罩层,处于目标上层图层。

MaskFilter有两个子类:

  • BlurMaskFilter:模糊效果
  • BlurMaskFilter:浮雕效果

举例

模糊效果

  • BlurMaskFilter.Blur.NORMAL
  • BlurMaskFilter.Blur.SOLD
  • BlurMaskFilter.Blur.INNER
  • BlurMaskFilter.Blur.OUTTER

分别为:

Android显示框架:自定义View实践之绘制篇
image.png
Android显示框架:自定义View实践之绘制篇
image.png
Android显示框架:自定义View实践之绘制篇
image.png
Android显示框架:自定义View实践之绘制篇
image.png
//设置遮罩图层,处于目标上层图层
//关闭硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
MaskFilter blurMaskFilter = new BlurMaskFilter(200, BlurMaskFilter.Blur.NORMAL);
paint2.setMaskFilter(blurMaskFilter); canvas.drawBitmap(bitmapTimo, null, rect1, paint1);
canvas.drawBitmap(bitmapTimo, null, rect2, paint2);

注:在硬件加速开启的情况下, setMaskFilter(MaskFilter maskfilter)只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。关闭硬件加速可以调用
View.setLayerType(View.LAYER_TYPE_SOFTWARE, null)或者在Activity标签里设置android:hardwareAccelerated="false"。

三 Canvas

Canvas实现了Android 2D图形的绘制,底层基于Skia实现。

3.1 界面绘制

Canvas提供了丰富的对象绘制方法,一般都以drawXXX()打头,绘制的对象包括:

  • 弧线(Arcs)
  • 颜色(Argb、Color)
  • 位图(Bitmap)
  • 圆(Circle)
  • 点(Point)
  • 线(Line)
  • 矩形(Rect)
  • 图片(Picture)
  • 圆角矩形(RoundRect)
  • 文本(Text)
  • 顶点(Vertices)
  • 路径(Path)

这里的方法大都很简单,我们来描述下期中比较复杂的方法。

弧线

public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {
native_drawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
useCenter, paint.getNativeInstance());
}
  • float left, float top, float right, float bottom:左、上、右、下的坐标。
  • float startAngle:弧形起始角度,Android坐标系x轴正右的方向是0度的位置,顺时针为正角度,逆时针为负角度。
  • float sweepAngle:弧形划过的角度。
  • boolean useCenter:是否连接到圆心。如果不连接到圆心就是弧形,如果连接到圆心,就是扇形。

例如

Android显示框架:自定义View实践之绘制篇
image.png
paint.setStyle(Paint.Style.FILL);//填充模式
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint);
canvas.drawArc(200, 100, 800, 500, 20, 140, false, paint);
paint.setStyle(Paint.Style.STROKE);//画线模式
paint.setStrokeWidth(5);
canvas.drawArc(200, 100, 800, 500, 180, 60, false, paint);

位图

  • public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) - 绘制位图
  • public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
    @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
    @Nullable Paint paint) - 绘制拉伸位图

第一个方法很简单,就是在指定的坐标处开始绘制位图。我们着重来看看第二个方法,这个方法不是很常用(可能是计算比较复杂的锅

上一篇:C#学习笔记(1)——快捷键


下一篇:英语是学习Java编程的基础吗