Android Paint 详细讲解

文章目录

1、简介

在Android 中,2D 的图形绘制一般使用的是 Graphics2D 这个类。
Graphics 类,提供了对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制。它是用于在 Java™ 平台上呈现二维形状、文本和图像的基础类。

自定义View 一般我们需要自己绘制一些图形,这就像画家一样,我们需要画笔和画布。

在Android 中整好有两个这样的类,来方便我们自己创作View.
Paint 类 我们称之为 画笔,为画图的过程中,定义各种参数,比如:颜色、线条样式、图案样式等。
Canvas 类我们定义为画布,主要提供若干方法用于绘制各种颜色图案:点、线、路径等。

我们绘制自定义View的时候,一般先定义Paint ,指定绘图参数,再通过Canvas 进行绘制图形。

自定义 MyView 继承 View

package myapplication.lum.com.myapplication;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyView extends View {
    String TAG = "lum_ : ";
    Paint paint;
    public MyView(Context context) {
        super(context);
        Log.i(TAG,"1");
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");

    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.i(TAG,"3");

    }

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        Log.i(TAG,"4");
    }

    protected void onDraw(Canvas canvas) {
        //图形绘制
    }

}


布局文件 引用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

<myapplication.lum.com.myapplication.MyView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

</LinearLayout>

2、Paint 类常用方法介绍

Paint 类 主要可以实现 画笔的 颜色、透明度、文本 样式等

1)setColor() 设置颜色

我们可以很简单的知道这是设置 画笔的颜色

paint.setColor(Color.RED);  //设置画笔为红色
2) setStrokeWidth() 设置宽度

设置画笔的宽度

        paint.setStrokeWidth(10);//设置画笔宽度 ,单位px

在画笔宽度为 0 的情况下,使用 drawLine 或者使用描边模式(STROKE)也可以绘制出内容。只是绘制出的内容始终是 1 像素,不受画布缩放的影响。该模式被称为hairline mode (发际线模式)。

如果你设置了画笔宽度为 1 像素,那么如果画布放大到 2 倍,1 像素会变成 2 像素。但如果是 0 像素,那么不论画布如何缩放,绘制出来的宽度依旧为 1 像素。

        paint.setStrokeWidth(0);//设置画笔宽度0  ,单位px  默认一个像素
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        canvas.scale(10,10); //放大 画布 10倍
        canvas.drawCircle(50,50,20,paint);

并没有改变画笔像素大小
在这里插入图片描述
更改宽度参数:

        paint.setStrokeWidth(1);//设置画笔宽度1  ,单位px
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        canvas.scale(10,10); //放大 画布 10倍
        canvas.drawCircle(50,50,20,paint);

画笔像素被跟随放大:
在这里插入图片描述

3)setAntiAlias(true) 抗齿距

设置是否开启抗齿距: true 开启;false ,不开启

        paint.setAntiAlias(true);//抗锯齿功能

抗锯齿是指在图像中,物体边缘总会或多或少的呈现三角形的锯齿,而抗锯齿就是指对图像边缘进行柔化处理,使图像边缘看起来更平滑,更接近实物的物体。说白了,就是画面你看上去,有一小格一小格的东西。抗拒齿就是画面更加精细。

我们画一条直线 对双数三个方法做一次对比:
自定义MyView 继承View 主要代码列出:

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");
        //就是通过修改画笔的一些参数设置
        paint = new Paint(); //新建一个画笔对象
        paint.setAntiAlias(true);//抗锯齿功能 打开
        paint.setColor(Color.RED);  //设置画笔颜色 红色
        paint.setStrokeWidth(10);//设置画笔宽度 10,单位px
    }

    protected void onDraw(Canvas canvas) {
       //画一条直线
        canvas.drawLine(0,0,200,200,paint);
    }

在这里插入图片描述

修改 paint 参数:
在这里插入图片描述

   public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");
        //就是通过修改画笔的一些参数设置
        paint = new Paint(); //新建一个画笔对象
        paint.setAntiAlias(false);//抗锯齿功能 关闭
        paint.setColor(Color.BLUE);  //设置画笔颜色  蓝色
        paint.setStrokeWidth(40);//设置画笔宽度 40  ,单位px
    }
    
    protected void onDraw(Canvas canvas) {
       //画一条直线
        canvas.drawLine(0,0,200,200,paint);
    }

截个图给你看看:
在这里插入图片描述

4) setAlpha() 设置画笔透明度
public void setAlpha(int a) {      
    nSetAlpha(mNativePaint, a);    
}                                  

取值范围 0~ 255
0是完全透明,255是完全不透明,数值越大 越不透明

  public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");
        //就是通过修改画笔的一些参数设置
        paint = new Paint(); //新建一个画笔对象
        paint.setAntiAlias(true);//抗锯齿功能
        paint.setStrokeWidth(100);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK); //设置 黑色
        paint.setAlpha(255); **//设置255  完全不透明**

    }
    protected void onDraw(Canvas canvas) {
       //画一个圆
        canvas.drawCircle(500, 500,400, paint);//画一个点
    }

在这里插入图片描述

修改参数:

  paint.setAlpha(10); //设置255  完全不透明

在这里插入图片描述

5)setARGB() 设置透明度 和 颜色
    public void setARGB(int a, int r, int g, int b) {
        setColor((a << 24) | (r << 16) | (g << 8) | b);
    }

setARGB 设置有四个参数,取值范围都是 0~255,
argb形式alpha,red,green,blue,
第一个参数 是 透明度的 设置
第二个、第三个、第四个 参数分别的 红绿黄 三个基本色素值,来控制画笔颜色

使用方法基本同上。

paint.setARGB(100,0,0,0);

就是意思 设置 透明度为 100, 000 Android 色度代码 表示黑色 的画笔。

6) setStyle() 设置画笔样式

设置绘制的图形是空心样式还是实心样式,默认为实心样式。

    public void setStyle(Style style) {
        nSetStyle(mNativePaint, style.nativeInt);
    }

画笔样式有三种,
Paint.Style.FILL:填充内部
Paint.Style.STROKE :描边
Paint.Style.FILL_AND_STROKE :填充内部和描边

我们画一个圆来给大家展示一下 这几个的区别

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");
        //就是通过修改画笔的一些参数设置
        paint = new Paint(); 
        paint.setAntiAlias(true);//抗锯齿功能
        paint.setStrokeWidth(100);//设置画笔宽度 ,单位px

    }
 protected void onDraw(Canvas canvas) {
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.FILL); //设置填充模式
        canvas.drawCircle(500, 500,400, paint);//画一个圆
        }
    

在这里插入图片描述

修改一下样式的参数:

        paint.setStyle(Paint.Style.STROKE); //描边模式 

在这里插入图片描述
修改一下样式:

paint.setStyle(Paint.Style.FILL_AND_STROKE);  //填充 与 描边模式

在这里插入图片描述

很多人看来
paint.setStyle(Paint.Style.FILL); //设置填充模式
paint.setStyle(Paint.Style.FILL_AND_STROKE); //填充 与 描边模式

看起来没有什么差别。
下面我们把这两种模式放到一起比较:
FILL_AND_STROKE 模式画一个 红色的圆
FILL 模式画一个 蓝色的 透明圆

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");
        //就是通过修改画笔的一些参数设置
        paint = new Paint(); //新建一个画笔对象
        paint.setAntiAlias(true);//抗锯齿功能
        paint.setStrokeWidth(100);//设置画笔宽度 ,单位px
    }
    
protected void onDraw(Canvas canvas) {
        paint.setColor(Color.RED); //设置红色
        paint.setStyle(Paint.Style.FILL_AND_STROKE);  //设置 填充 与 描边 样式
        canvas.drawCircle(500, 500,400, paint);//画圆
        paint.reset(); //重置画笔

        paint.setColor(Color.BLUE);  // 蓝色
        paint.setStyle(Paint.Style.FILL); //填充模式
        paint.setAlpha(100); 
        canvas.drawCircle(500, 500,400, paint); //画圆
        }

在这里插入图片描述

我们看到 虽然 他们的 半径,坐标 都是一样的 ,但是最终显示的大小却不是一样的。

我们 再把 填充模式 和 扫描 模式 放到一起比较就清楚了:
FILL 模式画一个 红色的圆
STROKE 模式画一个 蓝色的 透明圆

  public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG,"2");
        //就是通过修改画笔的一些参数设置
        paint = new Paint(); //新建一个画笔对象
        paint.setAntiAlias(true);//抗锯齿功能
        paint.setStrokeWidth(100);//设置画笔宽度 ,单位px

    }
    protected void onDraw(Canvas canvas) {
        paint.setColor(Color.RED); //设置红色
        paint.setStyle(Paint.Style.FILL);  //设置 填充
        canvas.drawCircle(500, 500,400, paint);//画圆

        paint.setColor(Color.BLUE);  // 蓝色
        paint.setAlpha(100);
        paint.setStyle(Paint.Style.STROKE); //描边 模式
        canvas.drawCircle(500, 500,400, paint); //画圆

    }

在这里插入图片描述

因此我们可以看到 ,描边模式 显示出来的 半径 是比填充模式大的,
描边模式/填充与描边模式 实际显示半径 r = 圆半径 + (画笔的宽度 / 2)

3、线条样式相关的类

1) setStrokeJoin() 线条连接处样式

样条连接处的样式有三种:

public static enum Join {
BEVEL,
MITER,
ROUND
}

**当绘图样式为 STROKRE / FILL_AND_STROKE 的时候,**这个方法用于指定线条连接拐角样式:

在这里插入图片描述

我们代码示例:
Paint 图形样式 设置为 STROKRE,连接处样式 设置为 BEVEL:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLUE);
        
        paint.setStyle(Paint.Style.STROKE); //设置绘图模式 扫描
        paint.setStrokeJoin(Paint.Join.BEVEL); //设置线条闭合模式 BEVEL
        canvas.drawRect(100,100,500,500,paint); //画矩形

        Path path = new Path();  //绘制路径 画一个闭合三角形
        path.moveTo(100,700);
        path.lineTo(300,900);
        path.lineTo(100,1200);
        path.close();
        canvas.drawPath(path,paint);
    }

我们可以看到:
连接处 的样式
在这里插入图片描述
当我们改变参数:

          paint.setStrokeJoin(Paint.Join.MITER); //设置线条闭合模式 MITER

在这里插入图片描述

当我们改变参数:

    paint.setStyle(Paint.Style.FILL_AND_STROKE); //设置绘图模式 扫描 FILL_AND_STROKE
    paint.setStrokeJoin(Paint.Join.ROUND); //设置线条闭合模式 MITER

在这里插入图片描述

2)setStrokeMiter() 斜接模式长度限制

Android 中线段连接方式默认是 MITER,即在拐角处延长外边缘,直到相交位置。
在这里插入图片描述
根据数学原理我们可知,如果夹角足够小,接近于零,那么交点位置就会在延长线上无限远的位置。 为了避免这种情况,如果连接模式为 MITER(尖角),当连接角度小于一定程度时会自动将连接模式转换为 BEVEL(平角)。

那么多大的角度算是比较小呢?根据资料显示,这个角度大约是 28.96°,即 MITER(尖角) 模式下小于该角度的线段连接方式会自动转换为 BEVEL(平角) 模式。

我们代码测试一下:
我们设置 连接模式为 MITER ,但是绘制一个连接角度很小的路径

  protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);

        paint.setStrokeJoin(Paint.Join.MITER); //设置线条闭合模式 MITER

        Path path = new Path();  //绘制路径 画一个闭合三角形
        path.moveTo(100,700);
        path.lineTo(600,700);
        path.lineTo(100,800);
        path.close();
        canvas.drawPath(path,paint);
    }

我们可以看到 小角度 下,确实是 MITER(尖角) 连接模式 自动转换成了 BEVEL (平角)
这个角度 我们计算可得 角度为 11.31 度
在这里插入图片描述
这个方法是对于 setStrokeJoin() 的一个补充,它用于设置 MITER 型拐角的延长线的最大值。所谓「延长线的最大值」,是这么一回事:

当线条拐角为 MITER 时,拐角处的外缘需要使用延长线来补偿
在这里插入图片描述

而这种补偿方案会有一个问题:如果拐角的角度太小,就有可能由于出现连接点过长的情况。比如这样:
所以为了避免意料之外的过长的尖角出现, MITER 型连接点有一个额外的规则:当尖角过长时,自动改用 BEVEL 的方式来渲染连接点。例如上图的这个尖角,在默认情况下是不会出现的,而是会由于延长线过长而被转为 BEVEL 型连接点:

在这里插入图片描述
用几何知识很容易得出这个比值的计算公式:如果拐角的大小为 θ ,那么这个比值就等于 1 / sin ( θ / 2 ) 。

这个 miter limit 的默认值是 4,对应的是一个大约 29° 的锐角:
在这里插入图片描述

默认情况下,大于这个角的尖角会被保留,而小于这个夹角的就会被「削成平头」

所以设置 这个值 越大,则可以保证越小的角度显示出来尖角。
一般设置的参数值 ,和角度值参照表
在这里插入图片描述

3)、setStrokeCap() 设置线头的模式

该方法用于设置落笔时的样式,控制我们的画笔在离开画板时留下的最后一点图形,
可选值如下:

public static enum Cap {
BUTT,
ROUND,
SQUARE
}

我们代码展示一下:

  protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(80);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);


        paint.setStrokeCap(Paint.Cap.BUTT);   //设置平头
        canvas.drawLine(300,300,700,300,paint);

        paint.setStrokeCap(Paint.Cap.ROUND); //设置圆头
        canvas.drawLine(300,500,700,500,paint);

        paint.setStrokeCap(Paint.Cap.SQUARE); //设置方头
        canvas.drawLine(300,700,700,700,paint);
    }

在这里插入图片描述

在这里插入图片描述

4)、setPathEffect()设置所有拐角变成圆角。
PathEffect pathEffect = new CornerPathEffect(20);  
paint.setPathEffect(pathEffect);

...

canvas.drawPath(path, paint);  

在这里插入图片描述

它的构造方法 CornerPathEffect(float radius) 的参数 radius 是圆角的半径。

CornerPathEffect 也可以让手绘效果更加圆润。

5) DashPathEffect()设置线条为虚线

DashPathEffect 用于实现虚线效果(适用于 STROKE 或 FILL_AND_STROKE 样式)。

// intervals:必须为偶数,用于控制显示和隐藏的长度。
// phase:相位。
DashPathEffect(float intervals[], float phase)

在这里插入图片描述

我们代码测试:

        PathEffect pathEffectOne = new DashPathEffect(new float[]{100, 50}, 0);
        paint.setPathEffect(pathEffectOne);
        Path pathOne = new Path();
        pathOne.moveTo(100,50);
        pathOne.lineTo(100,1000);
        canvas.drawPath(pathOne,paint);
      //  canvas.drawLine(100,50,100,1000,paint);

        Path pathTwo = new Path();
        PathEffect pathEffectTwo = new DashPathEffect(new float[]{100, 50},   50);  //偏移量 添加 50 
        paint.setPathEffect(pathEffectTwo);
        pathTwo.moveTo(200,50);
        pathTwo.lineTo(200,1000);
        canvas.drawPath(pathTwo,paint);

在这里插入图片描述

6)、PathDashPathEffect() 使用path 绘制虚线

它的构造方法 PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style) 中,
shape 参数是用来绘制的 Path ;
advance 是两个相邻的 shape 段之间的间隔,不过注意,这个间隔是两个 shape 段的起点的间隔,而不是前一个的终点和后一个的起点的距离;
phase 和 DashPathEffect 中一样,是虚线的偏移;
最后一个参数 style,是用来指定拐弯改变的时候 shape 的转换方式。
style 的类型为 PathDashPathEffect.Style ,是一个 enum ,具体有三个值:
TRANSLATE:位移
ROTATE:旋转
MORPH:变体

Path dashPath = ...; // 使用一个三角形来做 dash  
PathEffect pathEffect = new PathDashPathEffect(dashPath, 40, 0,  
        PathDashPathEffectStyle.TRANSLATE);
paint.setPathEffect(pathEffect);

...

canvas.drawPath(path, paint);  

在这里插入图片描述

更改参数 显示效果:

PathDashPathEffectStyle.TRANSLATE
在这里插入图片描述

7) DiscretePathEffect() 设置线条随机偏移

把线条进行随机的偏离,让轮廓变得乱七八糟。乱七八糟的方式和程度由参数决定。

// segmentLength: 分段长度
// deviation: 偏移距离
DiscretePathEffect(float segmentLength, float deviation);

代码示例:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);

        canvas.drawLine(100,100,100,1000,paint);

        paint.setPathEffect(new DiscretePathEffect(50,10));

        Path path = new Path();
        path.moveTo(500,100);
        path.lineTo(500,1000);
        canvas.drawPath(path,paint);
        }

在这里插入图片描述

8) SumPathEffect () 两种线条模式都执行

这是一个组合效果类的 PathEffect 。它的行为特别简单,就是分别按照两种 PathEffect 分别对目标进行绘制。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
PathEffect  pathEffect = new SumPathEffect(dashEffect, discreteEffect);
paint.setPathEffect(pathEffect );

...

canvas.drawPath(path, paint);  

同时对直线执行两种模式:
在这里插入图片描述

9) 、ComposePathEffect()线条组合模式

这也是一个组合效果类的 PathEffect 。不过它是先对目标 Path 使用一个 PathEffect,然后再对这个改变后的 Path 使用另一个 PathEffect。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);  
PathEffect discreteEffect = new DiscretePathEffect(20, 5);  
PathEffect  pathEffect = new ComposePathEffect(dashEffect, discreteEffect);
paint.setPathEffect(pathEffect );
...

canvas.drawPath(path, paint);  

效果展示:

在这里插入图片描述
它的构造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 中的两个 PathEffect 参数, innerpe 是先应用的, outerpe 是后应用的。所以上面的代码就是「先偏离,再变虚线」。而如果把两个参数调换,就成了「先变虚线,再偏离」。

注意: PathEffect 在有些情况下不支持硬件加速,需要关闭硬件加速才能正常使用:
Canvas.drawLine() 和 Canvas.drawLines() 方法画直线时,setPathEffect()
是不支持硬件加速的; PathDashPathEffect 对硬件加速的支持也有问题,所以当使用 PathDashPathEffect 的时候,最好也把硬件加速关了。

4、setShader()Gradient 着色渐变 渲染相关的类

Graphics2D 渐变种类有:

线性渐变:LinearGradient
径向渐变:RadialGradient
扫描渐变:SweepGradient
位图渐变:BitmapShader
混合渐变:ComposeShader

线性渐变、径向渐变和扫描渐变属于颜色渐变,指定 2 种或 2 种以上的颜色,根据颜色过渡算法自动计算出中间的过渡颜色,从而形成渐变效果,对于开发人员来说,无需关注中间的渐变颜色。
位图渐变则不再是简单的颜色渐变,而是以图片做为贴片有规律的变化,类似于壁纸平铺。混合渐变则能将多种渐变进行组合,实现更加复杂的渐变效果。

渐变种类 分为三种,我们 以A,B 代编两种颜色:

ABAB 型:A、B 两种颜色重复变化,通过 TileMode 类的 REPEAT 常量来表示; ABBA 型:A、B
两种颜色镜像变化,通过 TileMode 类的 MIRROR 常量来表示; AABB 型:A、B 两种颜色只出现一次,通过 TileMode
类的 CLAMP 常量来表示

如下图展示:
在这里插入图片描述
ABAB 渐变模式

1)LinearGradient 线性渐变

线性渐变是有两个点,不同颜色在渐变的方向与这两个点的连线垂直。
在这里插入图片描述

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile)

参数意思:
x0、y0:用于决定线性方向的第一个点的坐标(x0,y0);
x1、y1:用于决定线性方向的第二个点的坐标(x1,y1);
color0:第一种颜色;
color1:第二种颜色;
tile:渐变模式

代码示例:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);

        LinearGradient linearGradient = new LinearGradient(100,100,200,100,
                Color.RED,Color.BLUE,Shader.TileMode.MIRROR) ;  //设置渐变区域 属性 
        paint.setShader(linearGradient); //设置线性渐变
        canvas.drawLine(100,100,800,100,paint); //划线

        linearGradient = new LinearGradient(100,200,800,800,Color.RED,Color.BLUE,Shader.TileMode.CLAMP);
        paint.setShader(linearGradient); //设置线性渐变
        canvas.drawRect(100,200,800,800,paint); //画矩形

    }

在这里插入图片描述
如果两种颜色无法满足绘图需求,LinearGradient 支持三种或者三种以上颜色的渐变,对应
的构造方法如下:

public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode
tile)

这是一个功能更加强大的构造方法,我们来看看该构造方法参数的作用:
x0、y0:起始点的坐标
x1、y1:终止点的坐标
colors:多种颜色
positions:颜色的位置(比例)
TileMode:渐变模式

我们代码测试一下:

        LinearGradient linearGradient = new LinearGradient(100,100,800,100,
                new int [] {Color.RED,Color.YELLOW,Color.BLUE},new float[]{0,0.5f,1},Shader.TileMode.CLAMP) ;  //设置渐变区域 属性
        paint.setShader(linearGradient); //设置线性渐变
        canvas.drawLine(100,100,800,100,paint); //划线

效果展示:

在这里插入图片描述

参数 colors 和 positions 都是数组,前者用于指定多种颜色,后者用于指定每种颜色的起始比例位置。positions 数组中的元素个数与 colors 要相同

2) 、径向渐变 RadialGradient()

径向渐变是以指定的点为中心,向四周以渐变颜色进行圆周扩散,和线性渐变一样,支持两种或多种颜色。

在这里插入图片描述

public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile)

该构造方法支持两种颜色,下面是参数的作用:
x、y:中心点坐标
radius:渐变半径
color0:起始颜色
color1:结束颜色
TileMode:渐变模式

public RadialGradient(float x, float y, float radius, int colors[], float positions[], TileModetile)

该构造方法支持 3 种或 3 种以上颜色的渐变,各参数的作用如下:
x、y:中心点坐标
radius:渐变半径
colors:多种颜色
positions:颜色的位置(比例)
TileMode:渐变模式

代码示例:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);

        //两个颜色
        RadialGradient radialGradient = new RadialGradient(400,400,300,Color.RED,Color.BLUE,Shader.TileMode.CLAMP) ;//设置渐变区域 属性
        paint.setShader(radialGradient); //设置线性渐变
        canvas.drawCircle(400,400,300,paint); //划线

        //多种颜色
        radialGradient = new RadialGradient(400,1200,300,
               new int[]{Color.RED,Color.YELLOW,Color.GRAY,Color.BLUE},new float[]{0,0.4f,0.8f,1},Shader.TileMode.CLAMP) ;//设置渐变区域 属性
        paint.setShader(radialGradient); //设置线性渐变
        canvas.drawCircle(400,1200,300,paint); //划线
    }

结果显示:
在这里插入图片描述

3) 、扫描渐变 SweepGradient()

扫面渐变 ,类似于军事雷达一样,不断围绕圆心扫描。

public SweepGradient(float cx, float cy, int color0, int color1)

支持两种颜色的扫描渐变,参数的作用如下:
cx、cy:圆点坐标;
color0:起始颜色;
color1:结束颜色。

public SweepGradient(float cx, float cy, int colors[], float positions[])

支持多种颜色的扫描渐变,参数的作用如下:
cx、cy:圆点坐标;
colors:多种颜色;
positions:颜色的位置(比例)。

代码示例:


    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);

        //两个颜色
        SweepGradient sweepGradient = new SweepGradient(400,400,Color.RED,Color.BLUE) ;//设置渐变区域 属性
        paint.setShader(sweepGradient); //设置线性渐变
        canvas.drawCircle(400,400,300,paint); //划线

        //多种颜色
        sweepGradient = new SweepGradient(400,1200,
               new int[]{Color.RED,Color.YELLOW,Color.GRAY,Color.BLUE},null) ;//设置渐变区域 属性 position  设置为 null 则均分
        paint.setShader(sweepGradient); //设置线性渐变
        canvas.drawCircle(400,1200,300,paint); //划线
    }

在这里插入图片描述

4)位图渐变 BitmapShader

位图渐变其实就是绘制的图形中将指定的位图作为背景。如果图形比位图小,则通过渐变模式进行平铺。
BitmapShader 只有一个构造方法。

public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY)

TileMode.CLAMP 模式不平铺,TileMode.REPEAT 模式表示平铺,TileMode.MIRROR 模式也表示平铺,但是交错的位图是彼此的镜像,方向相反。可以同时指定水平和垂直两个方向
的渐变模式。

Shader.TileMode.CLAMP:如果着色器超出原始边界范围,会复制边缘颜色。
Shader.TileMode.MIRROR:横向和纵向的重复着色器的图像,交替镜像图像是相邻的图像总是接合。这个官方的说明可能不太好理解,说白了,就是图像不停翻转来平铺,直到平铺完毕。
Shader.TileMode.REPEAT: 横向和纵向的重复着色器的图像。
一般来说,当Canvas的宽度(高度)小于等于BitmapShader中Bitmap的宽度(高度),我们会使用Shader.TileMode.CLAMP模式,否则我们会使用Shader.TileMode.MIRROR或者Shader.TileMode.REPEAT模式。

代码测试:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象
        BitmapShader bitmapShader = new BitmapShader(bitmap,Shader.TileMode.REPEAT,Shader.TileMode.CLAMP);//设置 x,y  方向格式
        paint.setShader(bitmapShader);
        canvas.drawRect(10,10,800,800,paint);

        bitmapShader = new BitmapShader(bitmap,Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);
        paint.setShader(bitmapShader);
        canvas.drawRect(10,850,800,1600,paint);
    }

}

效果展示:
我们可以看到不同模式显示不同效果
在这里插入图片描述

5)混合 渐变( ComposeShader )

混合渐变是将两种不同的渐变通过位图运算后得到

ComposeShader 有两个构造方法:
 public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
 public ComposeShader(Shader shaderA, Shader shaderB, Mode mode)

其中位图运算有16中之多,我们可以先大概看一下
在这里插入图片描述

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象
        Shader  shader1 = new BitmapShader(bitmap,Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);//设置 x,y  方向格式


        LinearGradient shader2 = new LinearGradient(   //线性渐变
                0,0,500,0,Color.RED,Color.BLUE,Shader.TileMode.MIRROR);

        // ComposeShader:结合两个 Shader
        Shader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.XOR); //XOR 表示 裁剪shader1 shader2 交集
        paint.setShader(shader);

        canvas.drawRect(0,0,getRight(),getBottom(),paint);
    }

如图所示:

在这里插入图片描述

我们可以看一下各个模式的现象:

在这里插入图片描述
在这里插入图片描述

5、颜色效果处理

1) LightingColorFilter 设定 基本色素 黄绿黑

这个 LightingColorFilter 是用来模拟简单的光照效果的。
LightingColorFilter 的构造方法是 LightingColorFilter(int mul, int add),
参数里的 mul 和 add 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加:

R' = R * mul.R / 0xff + add.R  
G' = G * mul.G / 0xff + add.G  
B' = B * mul.B / 0xff + add.B  

对于一个颜色我们让其保持原,不改变颜色显示的 参数传递:
mul 为 0xffffff,add 为 0x000000(也就是0),那么对于一个像素,它的计算过程就是:
六位 分别代表基本色素 红绿黑

R' = R * 0xff / 0xff + 0x0 = R // R' = R  
G' = G * 0xff / 0xff + 0x0 = G // G' = G  
B' = B * 0xff / 0xff + 0x0 = B // B' = B  

基于这个「基本 LightingColorFilter 」,你就可以修改一下做出其他的 filter。比如,如果你想去掉原像素中的红色,可以把它的 mul 改为 0x00ffff (红色部分为 0 ) ,那么它的计算过程就是:

R' = R * 0x0 / 0xff + 0x0 = 0 // 红色被移除  
G' = G * 0xff / 0xff + 0x0 = G  
B' = B * 0xff / 0xff + 0x0 = B  

我们代码展示:

protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象
canvas.drawBitmap(bitmap,200,200,paint);

ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000); //去掉红色
paint.setColorFilter(lightingColorFilter);
canvas.drawBitmap(bitmap,200,600,paint);

}
效果展示:

在这里插入图片描述
当然我们可以随意的修改颜色的参数,比如让绿色更亮:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.FILL);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象
        canvas.drawBitmap(bitmap,200,200,paint);

        ColorFilter lightingColorFilter = new LightingColorFilter(0xffffff, 0x008800); //绿色更亮
        paint.setColorFilter(lightingColorFilter);
        canvas.drawBitmap(bitmap,200,600,paint);

    }

在这里插入图片描述

2)、PorterDuffColorFilter 设置颜色 模式运算

这个 PorterDuffColorFilter 的作用是使用一个指定的颜色 和 一种指定的 PorterDuff.Mode 来与绘制对象进行合成。
它的构造方法是 PorterDuffColorFilter(int color, PorterDuff.Mode mode)
其中的 color 参数是指定的颜色, mode 参数是指定的 Mode

我们代码测试:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象
        canvas.drawBitmap(bitmap,200,200,paint);

        ColorFilter colorFilter = new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.XOR); //去掉 和 绿色结合的部分
        paint.setColorFilter(colorFilter);
        canvas.drawBitmap(bitmap,200,600,paint);

    }

现象展示:
在这里插入图片描述

3)、ColorMatrixColorFilter 色彩锐度等

ColorMatrixColorFilter 使用一个 ColorMatrix 来对颜色进行处理。 ColorMatrix 这个类,内部是一个 4x5 的矩阵:

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

它的构造函数:

通过计算, ColorMatrix 可以把要绘制的像素进行转换。对于颜色 [R, G, B, A] ,转换算法是这样的:

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;  

代码测试:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象
        canvas.drawBitmap(bitmap,200,200,paint);
        ColorMatrix colorFilter = new ColorMatrix(new float[]{
                -1f, 0f, 0f, 0f, 255f,
                0f, -1f, 0f, 0f, 255f,
                0f, 0f, -1f, 0f, 255f,
                0f, 0f, 0f, 1f, 0f }); //去掉 和 绿色结合的部分
        paint.setColorFilter(new ColorMatrixColorFilter(colorFilter));
        canvas.drawBitmap(bitmap,200,600,paint);

    }

现象展示:
在这里插入图片描述

这个主要是对显示的色彩进一步的调节等,
在这里插入图片描述

4)setXfermode 图片转换模式

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

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);


        Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象
        Bitmap bitmapTwo = BitmapFactory.decodeResource(getResources(),R.mipmap.rect_2); //获取图片资源 转换成bitmap 对象

        Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); //取交集,交集样式取决于下层,颜色取决于上层
        int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(bitmapTwo, 0, 0, paint); 
        paint.setXfermode(xfermode); // 设置 Xfermode
        canvas.drawBitmap(bitmapone, 0, 0, paint); 
        paint.setXfermode(null); // 用完及时清除 Xfermode
        canvas.restoreToCount(saved);

    }

效果显示:
在这里插入图片描述

Xfermode 注意事项

Xfermode 使用很简单,不过有两点需要注意
使用离屏缓冲(Off-screen Buffer)
我们尝试使用代码直接绘制:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);


        Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象
        Bitmap bitmapTwo = BitmapFactory.decodeResource(getResources(),R.mipmap.rect_2); //获取图片资源 转换成bitmap 对象

        Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.XOR);
      //  int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(bitmapone, 0, 0, paint); // 画方
        paint.setXfermode(xfermode); // 设置 Xfermode
        canvas.drawBitmap(bitmapTwo, 0, 0, paint); // 画圆
        paint.setXfermode(null); // 用完及时清除 Xfermode
      //  canvas.restoreToCount(saved);

    }

我们看到都是黑色:
在这里插入图片描述
这是为什么呢:
如下
在这里插入图片描述
按照逻辑我们会认为,在第二步画圆的时候,跟它共同计算的是第一步绘制的方形。但实际上,却是整个 View 的显示区域都在画圆的时候参与计算,并且 View 自身的底色并不是默认的透明色,而且是遵循一种迷之逻辑,导致不仅绘制的是整个圆的范围,而且在范围之外都变成了黑色。就像这样。

在这里插入图片描述

这……那可如何是好?

要想使用 setXfermode() 正常绘制,必须使用离屏缓存 (Off-screen Buffer) 把内容绘制在额外的层上,再把绘制好的内容贴回 View 中。也就是这样:
在这里插入图片描述
通过使用离屏缓冲,把要绘制的内容单独绘制在缓冲层, Xfermode 的使用就不会出现奇怪的结果了。使用离屏缓冲有两种方式:

Canvas.saveLayer()

saveLayer() 可以做短时的离屏缓冲。使用方法很简单,在绘制代码的前后各加一行代码,在绘制之前保存,绘制之后恢复:

int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);


canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
paint.setXfermode(xfermode); // 设置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
paint.setXfermode(null); // 用完及时清除 Xfermode


canvas.restoreToCount(saved);

View.setLayerType()

View.setLayerType() 是直接把整个 View 都绘制在离屏缓冲中。 setLayerType(LAYER_TYPE_HARDWARE) 是使用 GPU 来缓冲, setLayerType(LAYER_TYPE_SOFTWARE) 是直接直接用一个 Bitmap 来缓冲。

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);


        Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象
        Bitmap bitmapTwo = BitmapFactory.decodeResource(getResources(),R.mipmap.rect_2); //获取图片资源 转换成bitmap 对象

        Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
     //   int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        this.setLayerType(View.LAYER_TYPE_SOFTWARE, paint); //设置软件缓存区
        canvas.drawBitmap(bitmapTwo, 0, 0, paint); // 蓝色方形 底层
        paint.setXfermode(xfermode); // 设置 Xfermode
        canvas.drawBitmap(bitmapone, 0, 0, paint); // 绿色机器人
        paint.setXfermode(null); // 用完及时清除 Xfermode
       // canvas.restoreToCount(saved);

    }

如果没有特殊需求,可以选用第一种方法 Canvas.saveLayer() 来设置离屏缓冲,以此来获得更高的性能。

6、色彩优化

Paint 的色彩优化有两个方法: setDither(boolean dither) 和 setFilterBitmap(boolean filter) 。它们的作用都是让画面颜色变得更加「顺眼」,但原理和使用场景是不同的。

1)、setDither(boolean dither) 设置图像抖动

在实际的应用场景中,抖动更多的作用是在图像降低色彩深度绘制时,避免出现大片的色带与色块。
在这里插入图片描述
而且在 Android 里使用起来也很简单,一行代码就搞定:

paint.setDither(true);

setDither(dither) 已经没有当年那么实用了,因为现在的 Android 版本的绘制,默认的色彩深度已经是 32 位的 ARGB_8888 ,效果已经足够清晰了。只有当你向自建的 Bitmap 中绘制,并且选择 16 位色的 ARGB_4444 或者 RGB_565 的时候,开启它才会有比较明显的效果。

2)、setFilterBitmap(boolean filter) 线性过滤

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

paint.setFilterBitmap(true);  

加上这一行,在放大绘制 Bitmap 的时候就会使用双线性过滤了。

7、设置阴影或者上层效果

1)、setShadowLayer () 设置阴影

构造函数:

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

方法的参数里, radius 是阴影的模糊范围; dx dy 是阴影的偏移量; shadowColor 是阴影的颜色。

代码示例:

    protected void onDraw(Canvas canvas) {
       // paint.setStrokeWidth(120);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);
        paint.setTextSize(100); //设置字体大小
        paint.setShadowLayer(10, 0, 0, Color.BLUE);
        canvas.drawText("这是个测试",100,100,paint);

    }

效果展示:
在这里插入图片描述
清楚阴影:

        paint.clearShadowLayer(); //清楚阴影

在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。

如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor
是不透明的,阴影的透明度就使用 paint 的透明度。

2)setMaskFilter(MaskFilter maskfilter) 绘制层上附件效果

为之后的绘制设置 MaskFilter。上一个方法 setShadowLayer() 是设置的在绘制层下方的附加效果;而这个 MaskFilter 和它相反,设置的是在绘制层上方的附加效果。

到现在已经有两个 setXxxFilter(filter) 了。前面有一个 setColorFilter(filter) ,是对每个像素的颜色进行过滤;而这里的 setMaskFilter(filter) 则是基于整个画面来进行过滤。

MaskFilter 有两种: BlurMaskFilter 和 EmbossMaskFilter。

(1)模糊效果的 MaskFilter。
构造方法:
BlurMaskFilter(float radius, BlurMaskFilter.Blur style)

它有四种选择模式:

NORMAL: 内外都模糊绘制
SOLID: 内部正常绘制,外部模糊
INNER: 内部模糊,外部不绘制
OUTER: 内部不绘制,外部模糊

 protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);
        paint.setTextSize(100);

        this.setLayerType(LAYER_TYPE_SOFTWARE, null);

        Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象

        BlurMaskFilter blurMaskFilterNORMAL = new BlurMaskFilter(50,BlurMaskFilter.Blur.NORMAL);
        paint.setMaskFilter(blurMaskFilterNORMAL);
        canvas.drawBitmap(bitmapone,50,200,paint);

        BlurMaskFilter blurMaskFilterOUTER = new BlurMaskFilter(50,BlurMaskFilter.Blur.OUTER);
        paint.setMaskFilter(blurMaskFilterOUTER);
        canvas.drawBitmap(bitmapone,550,200,paint);

        BlurMaskFilter blurMaskFilterINNER = new BlurMaskFilter(50,BlurMaskFilter.Blur.INNER);
        paint.setMaskFilter(blurMaskFilterINNER);
        canvas.drawBitmap(bitmapone,50,800,paint);

        BlurMaskFilter blurMaskFilterSOLID = new BlurMaskFilter(50,BlurMaskFilter.Blur.SOLID);
        paint.setMaskFilter(blurMaskFilterSOLID);
        canvas.drawBitmap(bitmapone,550,800,paint);
    }

效果展示:
在这里插入图片描述
(2)EmbossMaskFilter

EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的参数里, direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。

我们代码测试一下:

    protected void onDraw(Canvas canvas) {
        paint.setStrokeWidth(10);//设置画笔宽度 ,单位px
        paint.setStyle(Paint.Style.FILL);
        paint.setTextSize(100);

        this.setLayerType(LAYER_TYPE_SOFTWARE, null);

        //direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。
        EmbossMaskFilter embossMaskFilter =  new EmbossMaskFilter(new float[]{10, 10, 10}, 0.1f, 5, 5);
        paint.setMaskFilter(embossMaskFilter);
        canvas.drawText("Test",100,700,paint);

类似浮雕 效果:
在这里插入图片描述

8、获取实际路径

1) getFillPath(Path src, Path dst)

通过 getFillPath(src, dst) 方法就能获取这个实际 Path。方法的参数里,src 是原 Path ,而 dst 就是实际 Path 的保存位置。 getFillPath(src, dst) 会计算出实际 Path,然后把结果保存在 dst 里。
在这里插入图片描述

2) getTextPath 获取文本路径
getTextPath(String text, int start, int end, float x, float y, Path path)

分别是 文字/ 开始获取位置,结束获取的位置,文字所在的X坐标,文字所在的Y坐标,保存的路径

   protected void onDraw(Canvas canvas) {
        String text = "测试文本 Test";
        Paint srcPaint = new Paint();
        srcPaint.setTextSize(100);
        srcPaint.setColor(Color.BLACK);
        srcPaint.setStyle(Paint.Style.STROKE);
        canvas.drawText(text,50,100,srcPaint);
        //获取文本路径
        canvas.translate(0,150);
        Path desPath = new Path();
        Paint desPaint = new Paint();
        desPaint.setTextSize(100);
        desPaint.setColor(Color.BLACK);
        desPaint.setStyle(Paint.Style.STROKE);
        srcPaint.getTextPath(text,0,text.length(),50,100,desPath);
        canvas.drawPath(desPath,desPaint);


    }

待续···

文件参考:
Android Paint的使用详解

安卓自定义View进阶-画笔基础(Paint)

Android关于Paint你所知道的和不知道的一切

HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解

Android 自定义组件 开发详解 (李赞红)

发布了354 篇原创文章 · 获赞 114 · 访问量 44万+

猜你喜欢

转载自blog.csdn.net/qq_27061049/article/details/102574020