现实生活中作画,有两样东西必不可少:笔、纸,Android中图形绘制类似于此.
在Android中:
Paint类就是我们的“画笔”,为绘图定义各种参数:颜色、文本样式、图形样式等等.
Canvas类就是我们的“画布”,Canvas类中提供了若干方法用于绘制各种图案:点、线、矩形、圆等等.
掌握Graphics绘图是自定义组件的基础,Android给了我们一支笔(Paint)和一张纸(Canvas),画出什么样的图形取决于我们的想象力以及对Graphics绘图的掌握程度.
一、Paint类
Paint类就是画笔,用来设置绘图时的参数,比如:笔的颜色、笔的粗细等等.
使用方法很简单,首先定义一个画笔对象:
Paint mPaint = new Paint();
然后就可以调用Paint以set开头的相关方法为画笔设置各种参数了:
mPaint.set......
最后,在画布Canvas调用相关绘图方法的时候把画笔对象Paint传进去就可以了:
canvas.draw......(其它参数.....,mPaint);
这里先简单介绍画笔的几个方法,后面会详细介绍画笔Paint.
1、画笔颜色设置
画笔肯定有颜色了,Paint设置颜色的方法有下面3个,我们一般使用的是第1个
public void setColor(int color) 设置颜色值
public void setAlpha(int a) 设置透明度
public void setARGB(int a, int r, int g, int b) 指定透明度、红、绿、蓝定义一种颜色
2、画笔填充样式设置
public void setStyle(Style style)
设置画笔填充样式,可选值:
Paint.Style.FILL 填充内部(默认值)
Paint.Style.STROKE 仅描边
Paint.Style.FILL_AND_STROKE 填充内部和描边
上图是调用Canvas画了一个正方形在Paint三种填充样式下的效果,FILL就是内部填充满画笔的颜色,STROKE就是图形是空心的,在外层描边,所以STROKE看着比FILL大一点,FILL_AND_STROKE就是两者的综合.
3、画笔宽度、抗锯齿功能
public void setStrokeWidth(float width) 设置画笔宽度
当画笔填充样式为STROKE 、FILL_AND_STROKE 的时候,我们可以设置画笔的宽度,画笔宽度越大,描的边就越“厚”.
public void setAntiAlias(boolean aa)
aa为true时开启抗锯齿功能
我们画一条直线,细看是直的,但是仔细看会发现直线上有很多锯齿,就像电锯一样,这个方法可以让绘图过程中的线条更加平滑.
4、文本样式设置
Canvas是可以绘制文字的,文字的颜色就是Paint设置的颜色,Paint还可以为文字设置下面这些样式:
public void setTextSize(float textSize)
设置文本字体大小,单位px
public void setTextAlign(Align align)
设置文本对齐方式,可选值:Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT
绘制文本时会有个起始点坐标,三个值分别代表:在起始坐标的左边绘制文本、以起始坐标为中心绘制文本、在起始坐标的右边绘制文本
public void setTextScaleX(float scaleX)
将文本沿X轴水平缩放,默认值为1,大于1时会沿X轴水平放大文本,小于1会沿X轴水平缩放文本
public void setTextSkewX(float skewX)
设置文本倾斜程度,范围:-1~1,正负表示倾斜的方向,默认0不倾斜
public void setUnderlineText(boolean underlineText)
给文本添加下划线,true添加下划线
public void setFakeBoldText(boolean fakeBoldText)
文本粗体设置,true表示设置为粗体
public void setStrikeThruText(boolean strikeThruText)
文本添加删除线,true表示添加删除线
5、copy画笔、重置画笔
public void set(Paint src)
为当前画笔设置一个画笔
在绘图时,有时我们会用到不止一种画笔,如果有个画笔的属性都设置好了,另外一个画笔也需要这些属性,就可以直接copy过来
public void reset()
重置画笔,恢复初始状态
二、Canvas类
Canvas类提供了大量以draw...开头的方法用于绘图.
一般在绘图前,先创建Paint对象,定义绘制的颜色、样式等参数. 因为Paint对象可以重置(reset),所以除非有必要,Paint对象创建一个就可以了,然后再调用Canvas的绘图方法.
Canvas绘图方法主要有下面几种类型:
颜色
点
线
矩形
圆
路径
文字
位图
Canvas操作
1、前置
在讲绘图方法前,我们先自定义一个简单的XView继承自View,不然光介绍API不是很好理解.
public class XView extends View {
private Paint mPaint;
public XView(Context context) {
this(context, null);
}
public XView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();//初始化画笔
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
gogogo(canvas);
}
/**
* 后面所有测试代码都在gogogo中
*/
private void gogogo(Canvas canvas){
...
}
}
然后在布局文件中使用:
自定义的View在布局文件中写法:完整包名+类名
2、绘制颜色
public void drawRGB(int r, int g, int b)
public void drawARGB(int a, int r, int g, int b)
public void drawColor(int color)
这里绘制的是整个画布的颜色
测试:
private void gogogo(Canvas canvas){
canvas.drawColor(Color.GRAY);
}
3、绘制点
虽然是点,但并不表面它很小,点的大小取决于画笔宽度,也就是Paint.setStrokeWidth(...)参数的大小.
关于点,请看Point类、PointF类.
public void drawPoint(float x, float y, Paint paint)
在(x,y)处绘制一个点
public void drawPoints(float[] pts, Paint paint)
连续绘制多个点:pts是一个数组,从下标0开始,每2个数组元素确定一个点,数组长度必须是2的倍数
public void drawPoints(float[] pts, int offset, int count,Paint paint)
连续绘制多个点:pts是一个数组,从下标offset开始取数组元素,每2个数组元素确定一个点,一共取count个元素
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(20f);
mPaint.setColor(Color.BLUE);
canvas.drawPoint(50, 50, mPaint);//点(50,50)
mPaint.setStrokeWidth(30f);
mPaint.setColor(Color.RED);
canvas.drawPoints(new float[]{50, 100, 110, 100, 188, 100}, mPaint);//点(50,100)、(110,100)、(188,100)
mPaint.setColor(Color.BLACK);
canvas.drawPoints(new float[]{50, 150, 110, 150, 188, 150}, 2, 4, mPaint);//点(110,150)、(188,150)
4、绘制线段
两个点确定一条线段,所以,绘制一条线段时需要两个点的坐标,线段的粗细由画笔宽度决定
public void drawLine(float startX,float startY,float stopX,float stopY,Paint paint)
在(startX,startY)和(stopX,stopY) 两个点之间绘制一条线段
public void drawLines(float[] pts, Paint paint)
绘制多条线段:pts是一个数组,从下标0开始,每4个数组元素确定一条线段,数组长度必须是4的倍数
public void drawLines(float[] pts, int offset, int count,Paint paint)
绘制多条线段:pts是一个数组,从下标offset开始取数组元素,每4个数组元素确定一条线段,一共取count个元素,数组长度必须是4的倍数
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形笔触
mPaint.setStrokeWidth(20f);
mPaint.setColor(Color.BLUE);
canvas.drawLine(50, 50, 500, 50, mPaint);//点(50,50)到(500,50)的线段
mPaint.setColor(Color.RED);
canvas.drawLines(new float[]{50, 50, 50, 500, 500, 50, 500, 500}, mPaint);//点(50,50)到(50,500)的线段、点(500,50)到(500,500)的线段
mPaint.setColor(Color.BLACK);
canvas.drawLines(new float[]{50, 50, 50, 500, 500, 500, 0, 0}, 2, 4, mPaint);//点(50,50)到(500,500)的线段
}
5、绘制矩形
矩形分为:直角矩形、圆角矩形.(正方形也是矩形的一种)
关于矩形,请看Rect类、RectF类.
直角矩形:
public void drawRect(float left, float top, float right, float bottom, Paint paint)
public void drawRect(Rect r, Paint paint)
public void drawRect(RectF rect, Paint paint)
这三个重载方法绘制矩形的本质是完全一样的,使用哪一个随意
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(20f);
mPaint.setColor(Color.BLUE);
canvas.drawRect(50, 50, 200, 200, mPaint);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.RED);
canvas.drawRect(new Rect(50,250,200,400),mPaint);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(Color.BLACK);
canvas.drawRect(new RectF(50f,450f,200f,600f),mPaint);
}
圆角矩形:
public void drawRoundRect(RectF rect, float rx, float ry, Paint paint)
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint)
相对于直角矩形,圆角矩形多了两个参数:rx、ry
这两个参数用来指定4个角的弧度,分别是圆角处的水平半径、竖直半径
若rx=ry,则4个拐角是半径为rx的圆的1/4圆弧,
若rx≠ry,则4个拐角是长半轴、短半轴为rx、ry的椭圆的1/4圆弧.
这二个重载方法绘制矩形的本质是完全一样的,不同的是第二个需要在>=5.0(API 21)的版本使用
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
canvas.drawRoundRect(50, 50, 600, 600, 20, 40, mPaint);
mPaint.setColor(Color.RED);
canvas.drawRoundRect(new RectF(100, 100, 400, 400), 30, 30, mPaint);
}
6、绘制圆
我把椭圆、圆、弧、扇形统一归类到“圆”中.
(1)椭圆
椭圆的大小由它的外切矩形决定.
public void drawOval(RectF oval, Paint paint)
public void drawOval(float left, float top, float right, float bottom, Paint paint)
这两个重载方法本质一样,都是通过定义椭圆的外切矩形来绘制椭圆
(第二个重载方法需要在>=5.0(API 21)的版本使用)
(2)圆
圆的大小由圆心、半径决定.
public void drawCircle(float cx, float cy, float radius, Paint paint)
圆心:(cx,cy) 半径:radius
(3)弧、扇形
弧和扇形相似,都是椭圆上一部分,而椭圆又是由它的外切矩形决定
public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
public void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter,Paint paint)
startAngle:开始角度
sweepAngle:弧线、扇形所占角度,正数顺时针,负数逆时针
useCenter:是否使用中心点,true表示扇形、false表示弧线
(第二个重载方法需要在>=5.0(API 21)的版本使用)
当矩形长、宽一样时,就是正方形,椭圆就变成圆,弧线、扇形就是圆上一部分了
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
RectF rectF = new RectF(50, 50, 600, 400);
canvas.drawOval(rectF, mPaint);//椭圆
mPaint.setColor(Color.RED);
canvas.drawCircle(325, 225, 175, mPaint);//圆
mPaint.setColor(Color.YELLOW);
canvas.drawArc(rectF, 90, 90, false, mPaint);//弧
mPaint.setStyle(Paint.Style.FILL);
canvas.drawArc(rectF, 180, 90, false, mPaint);//弧
canvas.drawArc(rectF, 30, 30, true, mPaint);//扇形
canvas.drawArc(rectF, -30, 30, true, mPaint);//扇形
}
7、绘制文字
(1)从指定位置开始绘制文字:
public void drawText(String text, float x, float y, Paint paint)
public void drawText(char[] text, int index, int count, float x, float y,Paint paint)
public void drawText(String text, int start, int end, float x, float y,Paint paint)
public void drawText(CharSequence text, int start, int end, float x, float y,
Paint paint)
文字内容:text
部分内容:index、count------从下标index开始取,一共取count个数组元素;start、end------从字符串索引start到end处的部分字符串
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLUE);
mPaint.setTextSize(40);//设置字体大小
String text = "随风飘扬的Smile";
canvas.drawText(text, 50, 50, mPaint);//绘制文字
mPaint.setTextSkewX(-0.5f);//向左倾斜
canvas.drawText(text, 50, 100, mPaint);
mPaint.setTextSkewX(0.5f);//向右倾斜
canvas.drawText(text, 50, 150, mPaint);
mPaint.setUnderlineText(true);//下划线
canvas.drawText(text.toCharArray(), 1, 2, 50, 200, mPaint);
mPaint.setFakeBoldText(true);//粗体
mPaint.setStrikeThruText(true);//删除线
canvas.drawText(text, 2, text.length() - 1, 50, 250, mPaint);
mPaint.setTextScaleX(1.5f);//水平放大文本
canvas.drawText(text, 50, 300, mPaint);
}
(2)沿着Path路径绘制文字:
关于Path,请看Path类基本操作.
public void drawTextOnPath(String text, Path path, float hOffset,
float vOffset, Paint paint)
public void drawTextOnPath(char[] text, int index, int count,Path path,
float hOffset, float vOffset, Paint paint)
hOffset:文字与路径起始点的水平偏移距离
vOffset:文字与路径竖直方向偏移量,>0往Path下方偏移,<0往Path上方偏移
同样支持截取部分内容:从index处取元素,一共取count个元素
测试:
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(5);
mPaint.setColor(Color.BLUE);
mPaint.setTextSize(30);
String text = "随风飘扬的Smile";
Path path = new Path();
path.moveTo(50, 200);
path.lineTo(666, 666);//线段
//沿着Path绘制文字
canvas.drawTextOnPath(text+"1", path, 0, 0, mPaint);
canvas.drawTextOnPath(text+"2", path, 0, -30, mPaint);
canvas.drawTextOnPath(text+"3", path, 300, 0, mPaint);
canvas.drawTextOnPath(text+"4", path, 0, 30, mPaint);
canvas.drawTextOnPath(text+"5", path, 300, 30, mPaint);
Path path2 = new Path();
path2.addCircle(300,300,200, Path.Direction.CCW);//逆向圆,文字沿着逆向
canvas.drawTextOnPath(text, path2, 0, 0, mPaint);//沿着Path绘制文字
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, mPaint);//绘制Path---线段
canvas.drawPath(path2, mPaint);//绘制Path---圆
}
(3)指定文字位置:
public void drawPosText(String text, float[] pos,Paint paint)
public void drawPosText(char[] text, int index, int count,float[] pos,Paint paint)
text:文子内容
pos:每个文字的坐标位置,一个坐标需要2个参数,所以数组pos长度必须是2的倍数
index:第一个文字索引
count:一共绘制count个文字
测试
private void gogogo(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.BLUE);
mPaint.setAntiAlias(true); //使用抗锯齿功能
mPaint.setTextSize(50);
float[] textPos = new float[]{10, 50,
20, 100,
30, 150,
40, 200,
50, 250,
60, 300,
};
canvas.drawPosText("随风飘扬的笑", textPos, mPaint);
}
8、绘制位图
public void drawBitmap(Bitmap bitmap, float left, float top,Paint paint)
将bitmap绘制在画布上,同时指定位图相左上角位置(left,top),bitmap大小与原图一样,不进行缩放
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst,Paint paint)
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst,Paint paint)
从bitmap中抠出大小为src的矩形区域绘制到画布dst矩形处,bitmap会缩放适应src区域,所以src与dst的大小与比例关系影响最终绘制效果
若src为null,就将原bitmap绘制到dst处,bitmap会缩放适应src区域
测试
private void gogogo(Canvas canvas) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
canvas.drawBitmap(bitmap, 50, 50, mPaint);
canvas.drawBitmap(bitmap, new Rect(0, 0, 30, 30), new Rect(100, 100, 400, 400), mPaint);
}