Paint 和 Canvas 类常用方法说明

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zgcqflqinhao/article/details/75458339

自定义控件是 Android 开发很吸引人的一部分,各种私人定制的效果和各种炫酷的效果都需要通过自定义控件来实现,而在自定义控件中,最常用的两个类也就是 Paint (画笔)类和 Canvas (画布)类了,所以在这里记录一下它们的常用方法,对实现自定义控件也会有一定帮助。


1 Paint 类的常用方法

1.1 与 Text 相关的常用方法

public void setTextSize(float textSize):设置绘制文字的大小,必须大于 0。
public void setTextAlign(Align align):设置绘制文字的对齐方向,取值有 Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT。
public void setTextLocale(@NonNull Locale locale):API 17 出现的,设置地理位置,一般用 Locale.getDefault() 即可,我在学习的时候分别设置了 Locale.CHINESE 、 Locale.ENGLISH 、 Locale.JAPANESE,区别就是字体不太一样,并不是我想象中的自动翻译。
public void setTextLocales(@NonNull @Size(min=1) LocaleList locales):API 24 出现的,设置地理位置组。
public void setTextScaleX(float scaleX):设置绘制文字 x 轴的缩放比例,即文字的拉伸效果。默认值为 1,大于 1 则拉伸,大于 0 且小于 1 则收缩,小于 0 的时候会有意想不到的效果。
public void setTextSkewX(float skewX):设置绘制文字倾斜度,默认值为 0,取值范围貌似是 -48.0f ~ 48.0f。
public void setLinearText(boolean linearText):设置绘制文字是否需要缓存,true 为不需要。
public void setSubpixelText(boolean subpixelText):当设置为 true 时,它可以保证在绘制斜线时平滑该斜线的外观,相当于绘制文字的抗锯齿效果。
public void setUnderlineText(boolean underlineText):设置绘制文字是否带有下划线。
public void setElegantTextHeight(boolean elegant):API 21 出现的,设置绘制文字是否具有优雅的文字高度,但是具体有什么效果还有试出来。
public void setFakeBoldText(boolean fakeBoldText):设置绘制文字是否为粗体字,当字体大小比较小时效果会非常差。
public void setStrikeThruText(boolean strikeThruText):设置绘制文字是否带有删除线。
public void setTypeface(Typeface typeface):设置字体,常用字体:DEFAULT 、 DEFAULT_BOLD 、 SANS_SERIF 、 SERIF 、 MONOSPACE。
设置字体代码如下,各字体的效果大家可以自己试下:
        Typeface typeface = Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL);
        mPaint.setTypeface(typeface);

1.2 与图形相关的常用方法

public void setARGB(int a, int r, int g, int b):设置画笔透明度及颜色,各参数取值范围都是0~255。
public void setAlpha(int a):设置画笔透明度,取值范围为0~255,255为完全不透明。
public void setColor(Color color):设置画笔颜色。
public void setAntiAlias(boolean aa):设置抗锯齿,设置为 false 会出现锯齿状的边界,设置为 true 会多消耗性能但边界就会变得模式,避免锯齿状的情况。
public void setDither(boolean dither):防抖动,这个属性的需求场景主要出现在绘制渐变色彩或含渐变的图片时,Android 对不含 alpha 通道的图片会进行一个转化,成为 RGB565 格式的,这种格式占用内存小,但因为如此,就会出现讨厌的“色带”情景,让人感觉过渡的不是那么柔和,针对这个问题,Android 提出了防抖动,设置为 true ,它会将原始颜色的过渡处根据两边的色值进行一些改变,从而让颜色过渡更加的柔和,让人觉得是平滑的过渡。
public void setStyle(Style style):设置画笔样式,可选参数有 Paint.Style.FILL(填充)、 Paint.Style.STROKE(描边)、 Paint.Style.FILL_AND_STROKE(填充及描边)。
public void setStrokeCap(Cap cap):设置画笔帽,可选参数有 Paint.Cap.BUTT(无笔帽)、 Paint.Cap.ROUND(圆形笔帽)、 Paint.Cap.SQUARE(方形笔帽)。笔帽的意思就是前后两端多出来一截。

public void setStrokeJoin(Join join):这个方法用于设置接合处的形态,就像你用代码画了一条线,但是这条线其实是由无数条小线拼接成的,拼接处的形状就由该方法指定。可选参数是:Paint.Join.BEVEL(直线)、 Paint.Join.MITER(锐角)、Paint.Join.ROUND(圆弧)。但其实 Paint.Join.Round 和 Paint.Join.BEVEL 并没有明显的区别。
public void setStrokeWidth(float width):当画笔样式为 STROKE 或 FILL_AND_STROKE 时,设置画笔的宽度。
public void setFilterBitmap(boolean filter):如果该项设置为 true ,则图像在动画进行中会滤掉对 Bitmap 图像的优化操作,加快显示速度,本设置项依赖于 dither 和 xfermode 的设置。
public void setShadowLayer(float radius, float dx, float dy, int shadowColor):在图形下面设置阴影层,产生阴影效果,radius 为阴影的角度,dx 和 dy 为阴影在 x 轴和 y 轴上的距离,color 为阴影的颜色。
public void setShader(Shader shader):设置图像效果,使用 Shader 可以绘制出各种渐变效果。
public Xfermode setXfermode(Xfermode xfermode):设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果。
public void setMaskFilter(MaskFilter maskfilter):设置 MaskFilter ,可以用不同的 MaskFilter 实现滤镜的效果,如滤化,立体等。
public void setColorFilter(ColorFilter colorfilter):设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果。
public void setPathEffect(PathEffect effect):设置绘制路径的效果,如点画线等。
后面的五个方法这是个很强大的方法,我还没研究透,具体用法在这里推荐一个大神的系列博客:

其实在自定义控件中要想实现很多类似 PhotoShop 处理图形的效果,大多数是设置 Paint ,而 Canvas 更多是用来显示图形的。如果想研究得很深,上面链接推荐的大神的博客真的写得很好,我也会向他看齐的。

2 Canvas 类常用方法

Canvas 可以理解为画布,我们可以在这个画布上画点、线、圆、矩形和位图等,我们还可以对这个画布进行旋转、平移、缩放等。而 Canvas 大致上可以分为两类,一种是 View,用来画普通的图像,这类图像一般计算量比较小,就算含有动画也是帧率比较低的动画;另一种是 SurfaceView,用来显示高品质动画,一般用在游戏上,SurfaceView 中专门有一个线程来完成画图工作,所以程序不需要等待 View 完成画图,提高性能。
除了知道 Canvas 的分类之外,还需要了解的一点是 Canvas 牵扯到两种坐标系,一种是 Canvas 坐标系,它指的是 Canvas 本身的坐标系,它是唯一且不变的,坐标原点在 View 的左上角,从坐标原点向右为 x 轴正半轴,向下为 y 轴正半轴;另一种是绘图坐标系,一般来说这是对我们画图真正有用的坐标系,因为 Canvas 中的 drawXXXX 方法传入的各种坐标都是绘图坐标系中的坐标,默认情况下它是和 Canvas 坐标系完全重合,也就是说初始情况下它的坐标原点也在 View 的左上角,从坐标原点向右为 x 轴正半轴,向下为 y 轴正半轴,但是我们可以通过平移、旋转、缩放等方法来改变绘图坐标系,而且这些操作都是基于当前绘图坐标系的,绘图也是基于当前坐标系的,这么说可能比较抽象,一会儿会在 2.1 用代码说明绘图坐标系的变化。

2.1 对 Canvas 的操作方法

先举个栗子,我们随便写一个自定义控件,在 onDraw() 方法中对 Canvas 进行一些操作:
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //先画一条线,以这条线为参照物
        mPaint.setColor(Color.RED);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //Canvas 的 x 轴平移 100dx,y 轴平移 100dx.
        canvas.translate(100, 100);
        mPaint.setColor(Color.YELLOW);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //Canvas 顺时针旋转 30 度.
        canvas.rotate(30);
        mPaint.setColor(Color.BLUE);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //Canvas 缩放 2 倍
        canvas.scale(2,2);
        mPaint.setColor(Color.GREEN);
        canvas.drawLine(0, 100, 800, 100, mPaint);
    }

运行程序后,结果如下:


我们每次在 drawLine() 方法中传入的 startX、startY、stopX、StopY 都是一样的,但是画出来的线却在不同的位置,就是因为上面提到坐标系分为 Canvas 坐标系和绘图坐标系,对 Canvas 进行平移、旋转、缩放等操作是可以改变绘图坐标系的,而绘图又是基于绘图坐标系的,所以虽然我们传入的 startX、startY、stopX、StopY 都是一样的,但是由于参考的绘图坐标系改变了,所以画出来的线也不一样了。

public void translate(float dx, float dy):平移画布,dx 为在 x 轴上平移的距离,dy 为在 y 轴上平移的距离。
public final void scale(float sx, float sy):缩放画布,sx 为 x 轴缩放的倍数,sy 为 y 轴缩放的倍数。
public final void scale(float sx, float sy, float px, float py):缩放画布,sx、sy 同上,px 为 x 轴的基准点,py 为 y 轴的基准点,默认情况是以原点为基准点,这个方法可以以某个中心点来进行缩放。
public void rotate(float degrees):旋转画布,degrees 为旋转的角度,正值为顺时针旋转,负值为逆时针旋转。
public final void rotate(float degrees, float px, float py):类似 scale() 方法,可以指定基准点。
public void skew(float sx, float sy):倾斜画布,sx 为将画布在 x 轴上倾斜相应的角度的 tan 值,sy 为将画布在 y 轴上倾斜相应的角度的 tan 值。

关于 scale() 和 rotate() 提到的基准点的问题,用一段代码来说明一下:
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //先画一条线,以这条线为参照物
        mPaint.setColor(Color.RED);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //默认原点为基准点缩放 0.5 倍
        mPaint.setColor(Color.YELLOW);
        canvas.scale(0.5f, 0.5f);
        canvas.drawLine(0, 100, 800, 100, mPaint);

        //指定基准点缩放 0.5 倍
        canvas.restore();
        mPaint.setColor(Color.BLUE);
        canvas.scale(0.5f, 0.5f, 400, 50);
        canvas.drawLine(0, 100, 800, 100, mPaint);
    }

运行程序后,结果如下:

可以看到我们知道的基准点不同,所以缩放后的中心点也不同。rotate() 方法同理。
在上面的代码中 Canvas 调用了一个 restore() 方法,事实上 Canvas 是可以保存当前坐标系的状态的,也可以进行还原。主要涉及下面几个方法:
public int save():保存画布当前状态。
public int save(@Saveflags int saveFlags):保存画布当前状态到指定位置。
public void restore():恢复画布状态至上一次保存的状态。
public int getSaveCount():返回画布保存的状态个数。
public void restoreToCount(int saveCount):恢复画布状态至指定位置的状态。

2.2 利用 Canvas 绘制的方法

刚才说了可以在 Canvas 上绘制点、线、圆、矩形等等图形,下面就详细解释一下各个图形怎么画。

2.2.1 填充画布

public void drawRGB(int r, int g, int b):使用 RGB 填充画布。
public void drawARGB(int a, int r, int g, int b):使用 ARGB 填充画布。
public void drawColor(@ColorInt int color):使用 Color 填充画布。
public void drawPaint(@NonNull Paint paint):使用指定 paint 填充画布。

2.2.2 画点

public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint):画多个点,传入坐标数组,数组中每两个值为一组,为一个点的坐标,如果最后不足两个则忽略。
public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count, @NonNull Paint paint):画多个点,传入坐标数组,数组中每两个值为一组,为一个点的坐标,如果最后不足两个,则忽略,offset 为截取的取值范围起始位置(包含),count 为截取的取值范围的结束位置(包含)。
public void drawPoint(float x, float y, @NonNull Paint paint):画一个点,x 为 x 轴坐标,y 为 y 轴坐标。
示例代码:
        mPaint.setColor(Color.RED);
        canvas.drawPoint(800, 800, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawPoints(new float[]{100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600}, mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawPoints(new float[]{100, 100, 200, 200, 300, 300, 400, 400, 500, 500, 600, 600}, 1, 5, mPaint);


2.2.3 画线

public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint):画一条线,startX 为 x 轴起点坐标,startY 为 y 轴起点坐标,stopX 为 x 轴终点坐标,stopY 为 y 轴终点坐标。
public void drawLines(@Size(multiple=4) @NonNull float[] pts, @NonNull Paint paint):画多条线,同 public void drawPoints(@Size(multiple=2) @NonNull float[] pts, @NonNull Paint paint),不过数组中是每四个值为一组,如果最后不足四个则忽略。
public void drawLines(@Size(multiple=4) @NonNull float[] pts, int offset, int count, @NonNull Paint paint):画多条线,同 public void drawPoints(@Size(multiple=2) float[] pts, int offset, int count, @NonNull Paint paint),不过数组中是每四个值为一组,如果最后不足四个则忽略。
示例代码:
        mPaint.setColor(Color.RED);
        canvas.drawLine(100, 500, 500, 500, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawLines(new float[]{100, 600, 500, 600, 100, 700, 500, 700, 100, 800, 500, 800}, mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawLines(new float[]{100, 900, 500, 900, 100, 1000, 500, 1000, 100, 1100, 500, 1100}, 1, 9, mPaint);

2.2.4 画矩形

public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint):画矩形,参数分别为矩形的左边、顶边、右边和底边。
public void drawRect(@NonNull Rect r, @NonNull Paint paint):画矩形,r 为 int 类型参数创建的矩形。
public void drawRect(@NonNull RectF rect, @NonNull Paint paint):画矩形,rect 为 float 类型参数创建的矩形。
示例代码:
        mPaint.setColor(Color.RED);
        canvas.drawRect(30, 30, 800, 500, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(new Rect(30, 600, 800, 1100), mPaint);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(new RectF(30, 1200, 800, 1700), mPaint);

2.2.5 画椭圆

public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint):API 21 出现的,画椭圆,参数分别为矩形的左边、顶边、右边和底边。
public void drawOval(@NonNull RectF oval, @NonNull Paint paint):画椭圆,oval 为 float 类型参数创建的矩形。
示例代码:
        mPaint.setColor(Color.RED);
        canvas.drawOval(30, 30, 800, 600, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawOval(new RectF(30, 700, 800, 1300), mPaint);


2.2.6 画圆

public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint):画圆,cx 为圆心在 x 轴的坐标,cy 为圆心在 y 轴的坐标,radius 为圆的半径。
示例代码:
        mPaint.setColor(Color.RED);
	canvas.drawCircle(500, 500, 300, mPaint);


2.2.7 画扇形或弧线

public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint):API 21 出现的,画扇形或者弧线,参数分别为矩形的左边、顶边、右边、底边,开始角度,扇形的角度,是否需要和圆心连线。
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint):oval 为 float 类型参数创建的矩形,其余参数同上。
示例代码:
        mPaint.setColor(Color.RED);
        canvas.drawArc(30, 30, 500, 600, 0, 135, true, mPaint);
        mPaint.setColor(Color.YELLOW);
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawArc(new RectF(30, 700, 500, 1300), 0, 135, false, mPaint);


2.2.8 画圆角矩形

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint):API 21 出现的,参数分别为矩形的左边、顶边、右边、底边,椭圆的位于 x 轴的圆角的半径,椭圆的位于 y 轴的圆角的半径。
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint):rect 为 float 类型参数创建的矩形。
示例代码:
        mPaint.setColor(Color.RED);
        canvas.drawRoundRect(30, 30, 800, 600, 45, 45, mPaint);
        mPaint.setColor(Color.YELLOW);
        canvas.drawRoundRect(new RectF(30, 700, 800, 1300), 30, 50, mPaint);


2.2.9 画路线

public void drawPath(@NonNull Path path, @NonNull Paint paint):画路线,也可以利用该方法来画任意多边形,参数 path 为路线对象。
示例代码:
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(50, 100);
        path.lineTo(50, 300);
        path.lineTo(100, 500);
        path.lineTo(400, 500);
        path.lineTo(300, 300);
        path.lineTo(450, 50);
        path.lineTo(200, 200);
        path.lineTo(50,100);
        canvas.drawPath(path, mPaint);

2.3 小结

Canvas 还提供了画图片和画文字的方法,画文字如果结合 1.1 中的 Paint 的不同设置,可以产生更多的效果,上面的画各种图形的方法也是一样,如果画笔设置不同,画出来的图形也有不同的效果。至于画图片,这其中的细节很多,不在这里多说,今后会慢慢研究怎么实现那些滤镜效果。

3 总结

自定义控件是 Android 最吸引人的地方之一,可以说自定义控件真的是可以实现任何效果,而如果想学好自定义控件,Paint 和 Canvas 只是其中一部分,这一部分的东西掌握个基本还是不难,如果是专门做图片处理的话可能就需要好好研究一下了。这篇博客写了很久,因为虽然是看到别的大神的博客才想去学习这两个类,但是这跟视觉效果有关的东西,想看看每一个方法设置不同的参数都有什么效果所以很多方法都自己试了试,学习完之后也有一种感觉就是之前看到的很多 github 上开源的自定义控件如果要自己实现的话应该会有一些头绪了。路很长,水很深,为了更美好的明天需要多努力。

猜你喜欢

转载自blog.csdn.net/zgcqflqinhao/article/details/75458339