Android提供了2D图形绘制的各种工具,如Canvas(画布)、Point(点)、Paint(画笔)、Rectangles(矩形)等,利用这些工具可以直接在界面上进行绘制。
在自定义View中,我们经常用到的Canvas(画布)和Paint(画笔),像我们画画一样,需要画布和画笔,在View中绘制控件,Canvas就代表着画布,Paint就代表着画笔。
这是的Android的的的官网里画的API:https://developer.android.com/reference/android/graphics/Paint
官网中的API有很多,下面是比较常用的一些API:
Paint.setAntiAlias(boolean flag);//设置抗锯齿效果 设置true的时边缘会将锯齿模糊化
Paint.setDither(boolean flag);//设置防抖动,设置true的时图片看上去会更柔和点
Paint.setColor(int color);//设置画笔颜色
Paint.setARGB(int a, int r, int g, int b); //设置画笔的ARGB值
Paint.setAlpha(int alpha);//设置画笔的Alpha值
Paint.setStyle(); //设置画笔的style (三种:FILL填充 FILL_AND_STROKE填充加描边 STROKE描边 )
Paint.setStrokeWidth(float width);//设置描边宽度
Paint.setXfermode(Xfermode xfermode);//设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
Paint.setShader(Shader shader);//设置图像效果,使用Shader可以绘制出各种渐变效果
Paint.setShadowLayer(float radius ,float dx,float dy,int color);//在图形下面设置阴影层,产生阴影效果,radius为阴影的半径,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
//下面写文本的时候经常用到的
Paint.setTextSize(float textSize);//设置画笔文字大小
Paint.measureText(String text);//测试文本的长度
Paint.setTextAlign(Paint.Align align);// CENTER(文本居中) LEFT(文本左对齐) RIGHT(文本右对齐)
下面就演示一下上面这几个API的效果。
Paint.setStyle()
Paint.setStyle() //设置画笔的style,有三种
- Paint.Style.FILL //将填充使用此样式绘制的几何和文本,忽略绘画中与笔划相关的所有设置
- Paint.Style.FILL_AND_STROKE //使用此样式绘制的几何和文本将同时填充和描边,尊重绘画中与笔划相关的字段
- Paint.Style.STROKE //使用此样式绘制的几何和文本将被描边,尊重绘画上与笔划相关的字段
演示一个小demo:
paint = new Paint();
paint.setColor(Color.RED);//画笔颜色为红色
paint.setStrokeWidth(80); //描边宽度为80(为了区分效果,特意设置特别大)
float radius = 100f;
//将填充使用此样式绘制的几何和文本,忽略绘画中与笔划相关的所有设置
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(400, 200, radius, paint);
//使用此样式绘制的几何和文本将同时填充和描边,尊重绘画中与笔划相关的字段
paint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawCircle(400, 500, radius, paint);
//使用此样式绘制的几何和文本将被描边,尊重绘画上与笔划相关的字段
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(400, 900, radius, paint);
结果:
Paint.setShader(Shader shader)
Paint.setShader(Shader shader) //设置图像效果,使用Shader可以绘制出各种渐变效果
Shader:着色器,用来给图像着色,此类是基类, Shader的API 。有5个子类:
- BitmapShader
- ComposeShader
- LinearGradient
- RadialGradient
- SweepGradient
在了解上面5个类之前,先了解一下Shader.TileMode这个枚举,有三个值:
- Shader.TileMode.CLAMP :如果着色器在其原始边界之外绘制,则复制边缘颜色
- Shader.TileMode.MIRROR :水平和垂直重复着色器的图像,交替镜像,使相邻的图像始终接缝
- Shader.TileMode.REPEAT :水平和垂直重复着色器的图像
BitmapShader
其实这个Shader用于绘制bitmap作为纹理,然后通过平铺模式进行填充
/**
* 构造函数
* @bitmap 用来填充图形的Bitmap
* @tileX X轴Bitmap用Shader.TileMode模式填充
* @tileY Y轴Bitmap用Shader.TileMode模式填充
*/
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)
演示一下:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.MIRROR);
paint.setShader(shader);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
结果:
X轴用Shader.TileMode.CLAMP模式,就是用bitmap的右边缘去填充X轴的其余空间
Y轴用Shader.TileMode.MIRROR模式,就是在用相邻两张图像互为镜像的方式填充整个Y轴其余空间
接下来XY轴换一下模式:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.flower);
BitmapShader shader = new BitmapShader(bitmap, BitmapShader.TileMode.MIRROR, BitmapShader.TileMode.REPEAT);
paint.setShader(shader);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
结果:
X轴用Shader.TileMode.MIRROR模式,就是在用相邻两张图像互为镜像的方式填充整个X轴其余空间
Y轴用Shader.TileMode.REPEAT模式,就是用相同的图像重复填充整个Y轴其余空间
LinearGradient
LinearGradient:它是沿一条直线用来创建线性渐变效果,(x0,y0),(x1,y1)分别是起始坐标和终止坐标,color0,color1分别是起始颜色和终止颜色,tile为Shader.TileMode(CLAMP,REPEAT,MIRROR)模式中的一个。
/**
* 构造函数
* @x0 渐变线起始坐标的X坐标
* @y0 渐变线起始坐标的Y坐标
* @x1 渐变线终止坐标的X坐标
* @y1 渐变线终止坐标的Y坐标
* @color0 渐变线起始颜色
* @color1 渐变线终止颜色
* @tile 渐变线用Shader.TileMode模式填充
*/
LinearGradient( float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile);
演示一下:
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.RED, Shader.TileMode.MIRROR);
paint.setShader(linearGradient);
canvas.drawRect(200, 200, 600, 600, paint);
结果:
修改一下,扩大范围:
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, Color.GREEN, Color.RED, Shader.TileMode.MIRROR);
paint.setShader(linearGradient);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
然后在LinearGradient构造函数里面的Shader.TileMode模式,分别改成CLAMP、MIRROR和REPEAT。结果为:
LinearGradient的另一个构造函数:
/**
* 构造函数
* @colors[] 用colors数组线性填充
* @positions[] 每个position取值范围[0,1],并且和colors数组中对应位置的color一一对应
*/
LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
演示一下:
int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE};
float[] positions = {0f, 0.5f, 0.75f, 1f};
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(200, 200, 600, 600, paint);
结果:
修改一下,扩大范围:
int[] colors = {Color.GREEN, Color.GRAY, Color.RED, Color.BLUE};
float[] positions = {0f, 0.5f, 0.75f, 1f};
LinearGradient linearGradient = new LinearGradient(200, 200, 600, 600, colors, positions, Shader.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
一样的转换三种模式看看:
RadialGradient
RadialGradient:它也用来创建渐变效果,和LinearGradient不同的是,LinearGradient是线性渐变,而RadialGradient是径向渐变,也就是从中心向四周发散渐变,RadialGradient也有两个构造函数。先来看看第一个:
/**
*构造函数
* @centerX 圆中心的X轴坐标
* @centerY 圆中心的Y轴坐标
* @radius 圆半径
* @centerColor 圆中心颜色
* @edgeColor 圆边缘颜色
* @tileMode 径向渐变Shader.TileMode模式填充
*/
RadialGradient( float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)
演示一下:
float radius = 400f;
RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.RED, Color.YELLOW, Shader.TileMode.CLAMP);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, paint);
结果:
扩大一下范围:
float radius = 300f;
RadialGradient gradient = new RadialGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, Color.RED, Color.YELLOW, Shader.TileMode.REPEAT);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
同样转换三种模式看看:
RadialGradient的另一个构造函数:
/**
* 构造函数
* @colors[] color数组分布在圆的中心和边缘之间
* @stops[] 取值范围在[0.0f,1.0f],并且和colors数组中对应位置的color一一对应,如果为null,颜色均匀的分布在中心和边缘之间
*/
RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[],TileMode tileMode)
结果类似LinearGradient第二个构造函数,下来自己去演示。
SweepGradient
SweepGradient:用来创建围绕一个中心点360度沿顺时针旋转渐变效果。
/**
* 构造函数
* @cx 圆中心的X轴坐标
* @cy 圆中心的Y轴坐标
* @color0 开始旋转起始颜色,起始点在3点钟方向,顺时针
* @color1 结束旋转终止颜色,终止点也在3点钟方向
*/
SweepGradient( float cx, float cy, int color0, int color1)
演示一下:
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
结果:
修改一下形状:
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, Color.GREEN, Color.RED);
paint.setShader(gradient);
canvas.drawRect(0, (getMeasuredHeight() - getMeasuredWidth()) / 2, getMeasuredWidth(), (getMeasuredHeight() + getMeasuredWidth()) / 2, paint);
结果:
SweepGradient的另一个构造函数:
/**
* 构造函数
* @colors[] color数组顺时针分布
* @positions[] 每个position取值范围[0,1],并且和colors数组中对应位置的color一一对应
*/
SweepGradient(float cx, float cy, int colors[], float positions[])
演示一下:
int[] colors = {Color.GREEN, Color.YELLOW, Color.BLACK, Color.BLUE, Color.RED};
float[] positions = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};
SweepGradient gradient = new SweepGradient(getMeasuredWidth() / 2, getMeasuredHeight() / 2, colors, positions);
paint.setShader(gradient);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
结果:
ComposeShader
ComposeShader结合Xfermode模式,是两个Shader的组合模式,ComposeShader有两个构造函数:
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
Xfermode可以用于实现新绘制的像素与Canvas上对应位置已有的像素按照混合规则进行颜色混合,Xfermode 有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,前两个已废弃,PorterDuffXfermode初始化时需要传入PorterDuff.Mode 即:PorterDuffXfermode(PorterDuff.Mode mode),所以上面第一个构造函数是第二个构造函数的一种情况,我们只看第一个构造函数就可以了:
/**
* 构造函数
* @shaderA 目标像素DST
* @shaderB 源像素SRC
* @mode 新绘制的像素与Canvas上对应位置已有的像素按照混合规则进行颜色混合
*/
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
演示一下:
// 创建BitmapShader对象
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR,
Shader.TileMode.MIRROR);
// 创建LinearGradient并设置渐变颜色数组,平铺效果为镜像
LinearGradient linearGradient = new LinearGradient(0, 0, 0, 100, new int[] {
Color.WHITE, Color.LTGRAY, Color.TRANSPARENT, Color.GREEN }, null,
Shader.TileMode.MIRROR);
ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
paint.setShader(composeShader);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, getMeasuredWidth() / 2, paint);
结果:
介绍完了,继续学习API
Paint.setShadowLayer(float radius ,float dx,float dy,int color)
Paint.setShadowLayer(float radius ,float dx,float dy,int color) //在图形下面设置阴影层,产生阴影效果
/**
* @radius radius为阴影半径,半径越大,阴影面积越大,越模糊;反之,半径越小,阴影面积越小,也越清晰,radius=0时,阴影消失
* @dx dx为阴影在x轴上的偏移值
* @dy dy为阴影在y轴上的偏移值
* @color color为阴影的颜色
*/
Paint.setShadowLayer( float radius, float dx, float dy, int color);
演示一下:
paint.setColor(Color.RED);
paint.setShadowLayer(20, 0, 0, Color.YELLOW);
paint.setTextSize(100);
canvas.drawText("I am Layne", 200, 300, paint);
结果:
改一下:
paint.setShadowLayer(20,50, 50, Color.YELLOW);
结果:
改一下:
paint.setShadowLayer(1,50, 50, Color.YELLOW);
结果:
再改一下:
paint.setShadowLayer(0,50, 50, Color.YELLOW);
结果:
添加阴影:
paint.setColor(Color.RED);
paint.setShadowLayer(30, 0, 0, Color.BLACK);
setLayerType(LAYER_TYPE_SOFTWARE, paint);//要注意加上这句
canvas.drawCircle(400, 800, 100, paint);
结果:
关注个人公众号,主推 Android 技术文章