本文主要介绍一下Paint的setPathEffect方法。直译就是设置画笔的路径效果。
把setPathEffect方法放到这个位置单独开一篇来讲,主要是因为这个方法有一个关键字 Path ,所以前两篇讲了一下 Path 类。这样大家看到这里的话。就能有一个很好的过度。没有看过之前文章的朋友,如有疑问,请移步《Android:视图绘制(三) ——Path介绍》,《Android:视图绘制(四) ——Path进阶》
既然说到这了,顺带说一句,canvas的drawXXX系列方法,其实也都可以算作画的是Path。所以setPathEffect方法对drawCircle(float cx, float cy, float radius, Paint paint)等方法也都是适用的。
PathEffect setPathEffect(PathEffect effect)
参数是一个PathEffect类型的对象,其派生了6个子类供我们选择,来设置样式。分别是:
CornerPathEffect : 圆角
DashPathEffect : 虚线
PathDashPathEffect : 以Path类型填充虚线(也有人翻译成 印章)
DiscretePathEffect : 离散
ComposePathEffect : 合并
SumPathEffect : 叠加
翻译可能不太准确(再请原谅我这蹩脚的翻译)。先看下效果:
其中前四个,是设置单个样式,后两个是设置复合样式。
下面我们一个一个的了解这几个子类的应用。
· CornerPathEffect(float radius)
用来设置Path的拐角处为圆角,其构造方法接受一个参数为圆角的大小。radius值越大,圆角越大,也就越圆滑。
Path path = new Path();
path.moveTo(200,500);
path.lineTo(400,100);
path.lineTo(600,500);
path.close();
canvas.drawPath(path, mPaintLine);
mPaintLine.setPathEffect(new CornerPathEffect(50));
mPaintLine.setColor(Color.parseColor("#4def6f"));
canvas.drawPath(path, mPaintLine);
mPaintLine.setPathEffect(new CornerPathEffect(100));
mPaintLine.setColor(Color.parseColor("#451395"));
canvas.drawPath(path, mPaintLine);
· DashPathEffect(float intervals[], float phase)
用来画虚线。第一个参数intervals[]是一个float型的数组,用来表示虚线的长度,如果设置的长度小于总长度,那么就重复铺满。一段虚线最少包括一段实线和一段空白线段,个数必须是偶数个。第二个参数phase是偏移量的意思,正数为向左偏移,负数为向右偏移。
Path path = new Path();
path.moveTo(0,200);
path.rLineTo(1000,0);
mPaintLine.setPathEffect(new DashPathEffect(new float[]{10,20},0));
canvas.drawPath(path, mPaintLine);
canvas.translate(0,100);
mPaintLine.setPathEffect(new DashPathEffect(new float[]{10,20,50,100},0));
canvas.drawPath(path, mPaintLine);
canvas.translate(0,100);
mPaintLine.setPathEffect(new DashPathEffect(new float[]{10,20,50,100},50));
canvas.drawPath(path, mPaintLine);
从这个效果图中可以看到两点:
第一:第一条线的基本组成部分,分别为10,20实线段和空线段组成的 。第二条线和第三条,为10,20,50,100 的实线段和空线段组成。
第二:第三条线段较第二条线段向左偏移了50。
· PathDashPathEffect(Path shape, float advance, float phase, Style style)
这个方法在前面我翻译成了以Path填充虚线。因为他的方法名就是在虚线的基础上加了个Path,作为一个敲了几年代码的coder,很容易联想到他的意思,也比较直观。也有人翻译成印章路径(这么说的一定是看过源码的,因为源码注释用到了stamp这个词,当然也比较形象)。第一个参数就是要用来填充的Path。
先看一个效果图。下图就是用一个小的circle填充的一个大的circle。
下面来介绍PathDashPathEffect方法的最后一个参数 Style。Style 是PathDashPathEffect类中的一个枚举型变量,并提供了三种可选的值。先看源码:
public enum Style {
TRANSLATE(0), //!< translate the shape to each position
ROTATE(1), //!< rotate the shape about its center
MORPH(2); //!< transform each point, and turn lines into curves
Style(int value) {
native_style = value;
}
int native_style;
}
TRANSLATE 不改变填充shape的形状和方向。只是平移。
ROTATE 不改变填充shape的形状。旋转shape,改变方向。
MORPH 改变填充shape的形状和方向。
// 随机生成一个路径
Path path = getPath();
// 填充用shape
Path shapePath = new Path();
shapePath.moveTo(0, 0);
shapePath.rLineTo(10, 10);
shapePath.rLineTo(-20, 0);
shapePath.close();
// 没设置 PathEffect
canvas.drawPath(path, mPaintLine);
// Style为TRANSLATE
canvas.translate(0, 200);
mPaintLine.setPathEffect(new PathDashPathEffect(shapePath, 20, 0, PathDashPathEffect.Style.TRANSLATE));
canvas.drawPath(path, mPaintLine);
// Style为ROTATE
canvas.translate(0, 200);
mPaintLine.setPathEffect(new PathDashPathEffect(shapePath, 20, 0, PathDashPathEffect.Style.ROTATE));
canvas.drawPath(path, mPaintLine);
// Style为MORPH
canvas.translate(0, 200);
mPaintLine.setPathEffect(new PathDashPathEffect(shapePath, 20, 0, PathDashPathEffect.Style.MORPH));
canvas.drawPath(path, mPaintLine);
· DiscretePathEffect(float segmentLength, float deviation)
离散效果,也有人叫铁锈效果。就是对原始路径在一个的范围内(范围就是设置的segmentLength 和 deviation)进行无规则的离散。
官方解释:
/**
* Chop the path into lines of segmentLength, randomly deviating from the
* original path by deviation.
*/
其实就是我在上面说的意思,把path分隔成segmentLength大小的lines,并以deviation为长度进行离散。
从这个效果图中可以看出 segmentLength 越小 离散效果越明显,deviation 越大 离散效果越明显。
// 随机生成一个路径
Path path = getPath();
// 没设置 PathEffect
canvas.drawPath(path, mPaintLine);
// segmentLength : 10, deviation : 10
canvas.translate(0, 200);
mPaintLine.setPathEffect(new DiscretePathEffect(10, 10));
canvas.drawPath(path, mPaintLine);
// segmentLength : 5, deviation : 10
canvas.translate(0, 200);
mPaintLine.setPathEffect(new DiscretePathEffect(5, 10));
canvas.drawPath(path, mPaintLine);
// segmentLength : 5, deviation : 5
canvas.translate(0, 200);
mPaintLine.setPathEffect(new DiscretePathEffect(5, 5));
canvas.drawPath(path, mPaintLine);
· ComposePathEffect 和 SumPathEffect
这块把 ComposePathEffect 和 SumPathEffect 放在一起讲,因为这两个都是复合样式,进行对比更加清晰。
ComposePathEffect :组合 ,把两种样式合成一种形式。
SumPathEffect :叠加,就是直接的把两种样式放在一起。
效果图如下:
用DashPathEffect 给大家做了个动画 :行驶的小车。先上个效果图。
主要用了Path的虚线效果,画了三条虚线,用ValueAnimator控制DashPathEffect的phase偏移量。注释比较详细就不在赘述了。
/**
* 行驶的小车Demo
*
* @author adong
* @date 2016-10-8 17:40:43
*/
public class CustomPaintView extends View {
private Paint mPaintRoad;
private Paint mPaintText;
// 偏移长度
private int phase;
// 用于存放汽车的Bitmap
private Bitmap bitmap;
public CustomPaintView(Context context, AttributeSet attrs) {
super(context, attrs);
// 画圆用到的paint
mPaintRoad = new Paint();
mPaintRoad.setStyle(Paint.Style.STROKE); // 描边
mPaintRoad.setStrokeWidth(5); // 宽度
mPaintRoad.setAlpha(100); // 透明度
mPaintRoad.setAntiAlias(true); // 抗锯齿
mPaintRoad.setColor(Color.parseColor("#B0C4DE")); // 设置颜色 亮钢兰色
mPaintText = new Paint();
mPaintText.setAlpha(50);
mPaintText.setTextSize(100);
mPaintText.setColor(Color.parseColor("#f5f5dc"));
// 获得小车的bitmap
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.car);
startAnimator();
}
public CustomPaintView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomPaintView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path pathText = new Path();
pathText.moveTo(400, 400);
pathText.lineTo(1200, 1200);
canvas.drawTextOnPath("欢迎光临阿东的博客", pathText, 0, 0, mPaintText);
// 虚线的路径
Path path = new Path();
path.moveTo(0, 100);
path.lineTo(2000, 100);
// 画最上面的虚线
mPaintRoad.setPathEffect(new DashPathEffect(new float[]{50, 10}, phase % 60));
canvas.drawPath(path, mPaintRoad);
// 平移画布 画中间较宽的虚线
canvas.translate(0, 300);
mPaintRoad.setStrokeWidth(25);
mPaintRoad.setPathEffect(new DashPathEffect(new float[]{200, 40}, phase));
canvas.drawPath(path, mPaintRoad);
// 画小车
canvas.drawBitmap(bitmap, 0, 0, mPaintRoad);
// 平移画布 画下面较宽的虚线
canvas.translate(0, 300);
mPaintRoad.setStrokeWidth(5);
mPaintRoad.setPathEffect(new DashPathEffect(new float[]{50, 10}, phase % 60));
canvas.drawPath(path, mPaintRoad);
}
/**
* 计算每次的位置
*/
private void startAnimator() {
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 240);
// 持续时间
valueAnimator.setDuration(1000);
// 设置动画一直重复
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 获得当前的长度
phase = (int) animation.getAnimatedValue();
// 重绘
invalidate();
}
});
valueAnimator.start();
}
}