想玩好一些自定义控件效果,什么动画,Paint,Canvas这三方面必须要学好,Paint里面的函数会分几篇博客讲解,也许讲的不太好或者有不对的地方,希望能指出来,一起学习,一起提供!
首先看下系统给Paint类都有那么函数:
先把最简单的使用函数说下:
mPaint.setColor(Color.RED);//设置画笔的颜色 mPaint.setTextSize(60);//设置文字大小 mPaint.setStrokeWidth(5);//设置画笔的宽度 mPaint.setAntiAlias(true);//设置抗锯齿功能 true表示抗锯齿 false则表示不需要这功能
上面都是很容易理解的方法,现在讲下其他重要的函数
1:paint.setStyle()设置填充样式的,它接受的形参是Style,是Paint类中的一个枚举,源码:
从图中可以看出它有三个值可以选择,写个例子直观点:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.RED);//设置画笔的颜色 mPaint.setTextSize(60);//设置文字大小 mPaint.setStrokeWidth(5);//设置画笔的宽度 mPaint.setAntiAlias(true);//设置抗锯齿功能 true表示抗锯齿 false则表示不需要这功能 mPaint.setStyle(Paint.Style.FILL); canvas.drawCircle(200,200,160,mPaint); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(600,200,160,mPaint); }我画了2个圆,只是设置了style不一样而已,效果如下:
刚说了Style枚举定义了三个值还有一个Fill_AND_STROKE我做实验发现其效果和FILL一样,具体有啥不同,不太清楚,
2:public void setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置添加阴影
参数说明:
radius 表示阴影的倾斜度
dx:水平方向位移的像素
dy:垂直方向位移的像素
shadowColor:设置阴影的颜色
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setTextSize(60);//设置文字大小 mPaint.setStrokeWidth(5);//设置画笔的宽度 mPaint.setAntiAlias(true);//设置抗锯齿功能 true表示抗锯齿 false则表示不需要这功能 mPaint.setShadowLayer(10,10,10,Color.RED); canvas.drawText("杭州西湖风景真好",200,200,mPaint); }效果:
3:public void setTextAlign(Align align)//设置文字对齐方式
Align也是Paint类中定义的枚举,源码如下:
public enum Align { /** * The text is drawn to the right of the x,y origin */ LEFT (0), /** * The text is drawn centered horizontally on the x,y origin */ CENTER (1), /** * The text is drawn to the left of the x,y origin */ RIGHT (2); private Align(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; }取值为left,right,center,刚开始以为很简单,后来写demo发现不对,经过搜索发现canvas.drawText()调用这函数没我们从字面上理解的那么简单,这会涉及到一个基线问题,记得苦逼的小时候老师叫我们写26个字母,关键是还要抄10篇,哪时候本子是四格的,要求字母不能超过这个范围否则叫我们重写,多么失败的中国教育,如图:
如果要写字母的是这样的,
其中第三个横线为基线,如图:
我们看下canvast的drawText(String text, float x, float y, Paint paint)方法四个参数代表什么意思,以前一直以为x和y是表示view所在矩形的左上角左标为原点,如图:
其实真正的原点是这个,
上面的杭 子下面做下角为原点,红色的线为基线,从图可以看出y所代表的是基线的位置,这个我们可以写个例子验证下是否正确,如果你画一条线,我们知道画一条线,需要知道二个点的坐标,然后连接起来,然后绘制一段文字,这文字的原点以所画的起点为原点,效果:
drawText出来的文字文字位置和它的绘制时设置文字的大小有关,设置的坐标以及基线有关,而基线又是和y轴坐标有关,
接着讲public void setTextAlign(Align align)这个方法我现在分别给Align的值为LEFT,CENTER,RIGHT效果分别是:
Left:
center:
right:
发现当设置center和right和我们想象的效果根本不是一回事,这到底是什么原因呢?之前一直以为绘制文字是从x轴坐标点向右开始绘制文字,通常都是这种思维,这就是drawText()方法中x坐标有关,x代表所要绘制文字所在矩形的相对位置。相对位置就是指指定点(x,y)在在所要绘制矩形的位置,这才是x坐标正确的理解其意义,可以理解x是以它所在矩形为参考物
上面长方形也就是文字所绘制的矩形,现在用图来直观的看下刚才设置left,center,right
上面是以设置left为标准,然后设置center和right可以把left这个为标准,就可以看出来效果了,再补充一点如果起点不是从0开始的话,在设置center和right的时候,要向右移动x所给的值距离,比如我起点是从100开始,
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int baseLineX = 100 ; int baseLineY = 200; mPaint.setStrokeWidth(10); //画基线 mPaint.setColor(Color.BLUE); canvas.drawLine(baseLineX, baseLineY, 1080, baseLineY, mPaint); //写文字 mPaint.setColor(Color.RED); mPaint.setTextSize(90); mPaint.setTextAlign(Paint.Align.RIGHT); canvas.drawText("杭州西湖风景真好", baseLineX, baseLineY, mPaint); }我设置的是right,效果:
如果baseLineX为0就是刚好什么都看不见,但是现在的baseLineX为100,就相当于那个绘制的矩形向右移动100px,就看见一个好字了,好了这个方法讲到这里,真累
4:public native void setAlpha(int a)//设置透明都
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); mPaint.setAlpha(100); canvas.drawText("杭州西湖风景真美",0,500,mPaint); }绘制了二段一样的文字对比下效果:
5:public native void setTextScaleX(float scaleX)设置水平位置拉伸
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); mPaint.setTextScaleX(1.2f); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); }效果:
6:public native void setFakeBoldText(boolean fakeBoldText);设置文字是否为粗体
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); mPaint.setFakeBoldText(true); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); }效果:
7:public native void setStrikeThruText(boolean strikeThruText);设置是否带有删除线效果
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); mPaint.setStrikeThruText(true); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); }效果:
8:public native void setUnderlineText(boolean underlineText);是否设置下划线
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); mPaint.setUnderlineText(true); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); }效果:
9: /**
* Set the paint's horizontal skew factor for text. The default value
* is 0. For approximating oblique text, use values around -0.25.
*
* @param skewX set the paint's skew factor in X for drawing text.
*/
public native void setTextSkewX(float skewX);设置字体水平倾斜度,上面的注释表明默认为-0.25
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); mPaint.setTextSkewX(-0.5f); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); }效果:
10:public Typeface setTypeface(Typeface typeface);设置字体样式
它有四种取值
public static final int NORMAL = 0; public static final int BOLD = 1; public static final int ITALIC = 2; public static final int BOLD_ITALIC = 3;除了使用过系统默认的字体,也可以自己自定义字体,比如你可以用小米或者其他手机它的字体取出来放在assets文件夹下然后加载进去
Typeface类提供了几个重载的创建自定义字体的方法:
Typeface create(String familyName, int style) //直接通过指定字体名来加载系统中自带的文字样式
create(Typeface family, int style) //通过其它Typeface变量来构建文字样式
createFromAsset(AssetManager mgr, String path) //通过从Asset中获取外部字体来显示字体样式
createFromFile(String path)//直接从路径创建
createFromFile(File path)//从外部路径来创建字体样式
defaultFromStyle(int style)//创建默认字体
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mPaint.setColor(Color.BLUE); mPaint.setTextSize(90); Typeface typeFace = Typeface.defaultFromStyle(Typeface.BOLD); mPaint.setTypeface(typeFace); canvas.drawText("杭州西湖风景真美", 0, 300, mPaint); }效果:
11: public PathEffect setPathEffect(PathEffect effect)设置路径样式,我们看见要传递的参数Patheffect,
public class PathEffect { protected void finalize() throws Throwable { nativeDestructor(native_instance); } private static native void nativeDestructor(long native_patheffect); long native_instance; }发现PathEffect就这个方法,这说明肯定有它子类,ctrl+T,查看下它的类结构:
发现它有6个子类,现在一一讲解下它派生的几个子类有什么作用,之前是我们用canvas的drawText()绘制文字作为demo,现在不能使用drawText,因为我们从上面的类PathEffect就知道跟Path有关,而Path意思是路径的意思,和线差不多,比如一些图形都是Path构成的,比如三角形,矩形,
void moveTo (float x1, float y1):直线的开始点;即将直线路径的绘制点定在(x1,y1)的位置;
void lineTo (float x2, float y2):直线的结束点,又是下一次绘制直线路径的开始点;lineTo()可以一直用;
void close ():如果连续画了几条直线,但没有形成闭环,调用Close()会将路径首尾点连接起来,形成闭环;
这其中close()方法很重要,现在画一个三角形没有调用close()和调用close()方法的区别,
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(10);
Path path = new Path();
path.moveTo(10, 10);
path.lineTo(10, 100);
path.lineTo(500, 100);
canvas.drawPath(path, paint);
}
效果:
发现这个并没有组成一个三角形,最后一根线并没有连接起来,现在我调用path的claose()方法,
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(10);
Path path = new Path();
path.moveTo(200, 10);
path.lineTo(200, 100);
path.lineTo(500, 100);
path.close();
canvas.drawPath(path, paint);
}
效果:
现在是调用了close()方法后形成了一个闭环,从而构建了一个三角形,比如我再绘制一个不规则的六边形,
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setColor(Color.BLUE);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(10);
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(30, 400);
path.lineTo(100, 700);
path.lineTo(400, 700);
path.lineTo(600, 400);
path.lineTo(400, 100);
path.close();
canvas.drawPath(path, paint);
}
效果:
path类做了一个基本的了解以后再来叫PathEffect几个子类的作用:
1):CornerPathEffect——圆形拐角效果 它的作用就是将Path的各个连接线段之间的夹角用一种更平滑的方式连接,类似于圆弧与切线的效果,我们初中学过外切圆,如图:
我们发现上面二根线连接起来是很尖的,能不能把这二根线连接起来的角平滑点,这就用到了CornerPahtEffect类,而CornerPathEffect的构造函数只有一个要求形参是一个radius就是半径的意思,我把它设置为120,
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
path.moveTo(100,600);
path.lineTo(400,100);
path.lineTo(700,900);
canvas.drawPath(path,paint);
paint.setColor(Color.RED);
CornerPathEffect cornerPathEffect = new CornerPathEffect(120);
paint.setPathEffect(cornerPathEffect);
canvas.drawPath(path,paint);
}
效果:
现在我们利用这个知识画一个医院的心电图:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
path.moveTo(100,600);
path.lineTo(200,300);
path.lineTo(400,700);
path.lineTo(500,300);
path.lineTo(600,300);
path.lineTo(700,600);
path.lineTo(800,600);
path.lineTo(900,300);
path.lineTo(1000,600);
path.lineTo(1100,300);
paint.setColor(Color.RED);
CornerPathEffect cornerPathEffect = new CornerPathEffect(120);
paint.setPathEffect(cornerPathEffect);
canvas.drawPath(path,paint);
}
效果:
2):DashPathEffect——虚线效果 将Path的线段虚线化 构造函数如下:
public DashPathEffect(float intervals[], float phase)
参数说明:
intervals[]:表示组成虚线的各个线段的长度;整条虚线就是由intervals[]中这些基本线段循环组成的,这线段包含实线和虚线,虚线是看不见的,但它也占空间,所以intervals的数组的长度必须是大于等于2,而数组的长度必须是2的倍数,如果是奇数的话最后一个将不起作用,因为它是虚线啊,所以有和没有都没什么作用,
phase:开始绘制的偏移值
现在画一个带虚线的矩形:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
path.moveTo(100,300);
path.lineTo(100,700);
path.lineTo(600,700);
path.lineTo(600,300);
path.close();
float[] intervals = {20,10,20,10,20,10,20,10};
paint.setColor(Color.RED);
DashPathEffect dashPathEffect = new DashPathEffect(intervals, 12f);
paint.setPathEffect(dashPathEffect);
canvas.drawPath(path,paint);
}
效果:
这个矩形的长度为400,宽度为500,设置的虚线+实线=30,而这数组的长度为8,4组虚实线,拿400/30,500/30算算这有多少格,如图:
每一个竖线就是组{20,10,20,10,20,10,20,10}数组,
3): DiscretePathEffect——离散路径效果,作用是打散Path的线段,使得在原来路径的基础上发生打散效果,它的构造函数如下:
public DiscretePathEffect(float segmentLength, float deviation)
参数说明:
segmentLength:表示将原来的路径切成多长的线段。如果值为2,那么这个路径就会被切成一段段由长度为2的小线段。所以这个值越小,所切成的小线段越多;这个值越大,所切成的小线段越少
deviation:表示被切成的每个小线段的可偏移距离。值越大,就表示每个线段的可偏移距离就越大,就显得越凌乱,值越小,每个线段的可偏移原位置的距离就越小
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
Path path = new Path();
path.moveTo(100,300);
path.lineTo(100,700);
path.lineTo(600,700);
path.lineTo(600,300);
path.close();
float[] intervals = {20,10,20,10,20,10,20,10};
paint.setColor(Color.RED);
DiscretePathEffect discretePathEffect = new DiscretePathEffect(1, 15);
paint.setPathEffect(discretePathEffect);
canvas.drawPath(path,paint);
}
效果:
4):PathDashPathEffect——这个类的作用是使用Path图形来填充当前的路径,这个不知道用什么名词来解释,还是直接看效果看好,它的构造函数如下:
PathDashPathEffect(Path shape, float advance, float phase,Style style)
参数说明:
Path shape:表示给路径添加的图形形状;
float advance:表示两个图形形状路径间的距离。
float phase:路径绘制偏移距离,
Style style:表示在遇到转角时,转角处以什么形式和其他的连接,它的值有三个:
现在把这三个值设置后通过效果直接观看,
MORPH:
ROTATE:
TRANSLATE:
总结:
其中ROTATE的情况下,线段连接处的图形转换以旋转到与下一段移动方向相一致的角度进行转转,MORPH时图形会以发生拉伸或压缩等变形的情况与下一段相连接,TRANSLATE时,图形会以位置平移的方式与下一段相连接
实例:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStrokeWidth(10);
paint.setColor(Color.GREEN);
paint.setStyle(Paint.Style.STROKE);
Path shape = new Path();//创建添加图形的形状
shape.addCircle(10, 10, 5, Direction.CCW);//三角形
Path path = new Path();
path.moveTo(100,300);
path.lineTo(100,800);
path.lineTo(600,800);
path.lineTo(600, 300);
path.close();
paint.setColor(Color.RED);
PathDashPathEffect effect = new PathDashPathEffect(shape, 20, 15, PathDashPathEffect.Style.MORPH);
paint.setPathEffect(effect);
canvas.drawPath(path,paint);
}
效果: