#本文基于android sdk 22
在android graphics模块中有一类特效类叫做“path effect”,他们有一个共同的基类“PathEffect”。这些path effect的唯一目的就是给path增加特效,换句话话说只有当paint的style为“STROKE”或者“FILL_AND_STROKE”时,path effect才会生效。添加path effect的方式很简单,只需要调用Paint.setPathEffect()即可。
截止到android sdk 22,共有6中内置的PathEffect。下面表格列出这六种path effect,并给出基本说明。
CornerPathEffect | 处理path的各个连接点,可以产生圆角效果,可以控制圆角半径 | DashPathEffect | 生成虚线效果,可以分别控制实点和虚点长度,可以控制偏移量 | PathDashPathEffect | 类似于DashPathEffect, 只不过增加了控制实点形状的能力 | DiscretePathEffect | 沿着path产生毛边的效果,可以控制毛边颗粒间距,以及毛边颗粒偏移距离 | ComposePathEffect | 可以将任意两种path effect的效果,比如CornerPathEffect和DashPathEffect。不过要注意的是它与SumPathEffect的不同,ComposePathEffect的叠加效果相当于,先生效效果A,然后以A为基础生效效果B。 | SumPathEffect | 可以叠加任意两种path effect的效果,与Compose不同的是,它相当于同时生效A和B,然后在视图上将两种效果生硬的上下叠加起来。 |
下面是各种path effect的实例代码和运行结果。
CornerPathEffect
核心代码: package com.zlsam.learngraphics.patheffect;
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.View;
/** * Created by zhanglong on 16/8/18. */ public class CornerPathEffectView extends View {
private int mCornerRadius = 5; private Path mPath; private Paint mPaint;
public CornerPathEffectView(Context context) { this(context, null); }
public CornerPathEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public CornerPathEffectView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);
if (isInEditMode()) { // Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new CornerPathEffect(mCornerRadius)); return; }
// Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new CornerPathEffect(mCornerRadius)); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); }
public void setCornerRadius(int radius) { mCornerRadius = radius; mPaint.setPathEffect(new CornerPathEffect(mCornerRadius)); invalidate(); }
public int getCornerRadius() { return mCornerRadius; } }
|
运行效果: 
DashPathEffect
核心代码:
package com.zlsam.learngraphics.patheffect;
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.View;
/** * Created by zhanglong on 16/8/18. */ public class DashPathEffectView extends View {
private float mSolidLength = 1F; private float mVirtualLength = 1F; private float mPhase = 0; private Path mPath; private Paint mPaint;
public DashPathEffectView(Context context) { this(context, null); }
public DashPathEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public DashPathEffectView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (isInEditMode()) { // Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase)); return; }
// Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase)); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); }
public void setSolidLength(float length) { mSolidLength = length; mPaint.setPathEffect(new DashPathEffect(new float[]{mSolidLength, mVirtualLength}, mPhase)); invalidate(); }
public float getSolidLength() { return mSolidLength; }
public void setVirtualLength(float length) { mVirtualLength = length; mPaint.setPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase)); invalidate(); }
public float getVirtualLength() { return mVirtualLength; }
public void setPhase(float phase) { mPhase = phase; mPaint.setPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase)); invalidate(); }
public float getPhase() { return mPhase; }
}
|
运行效果: 
PathDashPathEffect
核心代码:
package com.zlsam.learngraphics.patheffect;
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathDashPathEffect; import android.graphics.RectF; import android.util.AttributeSet; import android.view.View;
/** * Created by zhanglong on 16/8/18. */ public class PathDashPathEffectView extends View {
private Path mPath; private Paint mPaint;
public PathDashPathEffectView(Context context) { this(context, null); }
public PathDashPathEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public PathDashPathEffectView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (isInEditMode()) { // Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN);
Path path = new Path(); path.addOval(new RectF(0, 0, 10, 20), Path.Direction.CCW); mPaint.setPathEffect(new PathDashPathEffect(path, 20, 0, PathDashPathEffect.Style.ROTATE)); return; }
// Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); Path path = new Path(); path.addOval(new RectF(0, 0, 10, 20), Path.Direction.CCW); mPaint.setPathEffect(new PathDashPathEffect(path, 20, 0, PathDashPathEffect.Style.ROTATE)); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); } }
|
运行结果: 
DiscretePathEffect
核心代码:
package com.zlsam.learngraphics.patheffect;
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DiscretePathEffect; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.View;
/** * Created by zhanglong on 16/8/18. */ public class DiscretePathEffectView extends View {
private float mSegmentLength = 1.0F; private float mDeviation = 1.0F; private Path mPath; private Paint mPaint;
public DiscretePathEffectView(Context context) { this(context, null); }
public DiscretePathEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public DiscretePathEffectView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (isInEditMode()) { // Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new DiscretePathEffect(mSegmentLength, mDeviation)); return; }
// Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new DiscretePathEffect(mSegmentLength, mDeviation)); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); }
public void setSegmentLength(float length) { mSegmentLength = length; mPaint.setPathEffect(new DiscretePathEffect(mSegmentLength, mDeviation)); invalidate(); }
public float getSegmentLength() { return mSegmentLength; }
public void setDeviation(float deviation) { mDeviation = deviation; mPaint.setPathEffect(new DiscretePathEffect(mSegmentLength, mDeviation)); invalidate(); }
public float getDeviation() { return mDeviation; } }
|
运行结果: 
ComposePathEffect
核心代码:
package com.zlsam.learngraphics.patheffect;
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ComposePathEffect; import android.graphics.CornerPathEffect; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.View;
/** * Created by zhanglong on 16/8/18. */ public class ComposePathEffectView extends View {
private int mCornerRadius = 0; private float mSolidLength = 1F; private float mVirtualLength = 1F; private float mPhase = 0; private Path mPath; private Paint mPaint;
public ComposePathEffectView(Context context) { this(context, null); }
public ComposePathEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public ComposePathEffectView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); if (isInEditMode()) { // Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new ComposePathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); return; }
// Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new ComposePathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); }
public void setCornerRadius(int radius) { mCornerRadius = radius; mPaint.setPathEffect(new ComposePathEffect(new DashPathEffect(new float[]{mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public int getCornerRadius() { return mCornerRadius; }
public void setSolidLength(float length) { mSolidLength = length; mPaint.setPathEffect(new ComposePathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public float getSolidLength() { return mSolidLength; }
public void setVirtualLength(float length) { mVirtualLength = length; mPaint.setPathEffect(new ComposePathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public float getVirtualLength() { return mVirtualLength; }
public void setPhase(float phase) { mPhase = phase; mPaint.setPathEffect(new ComposePathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public float getPhase() { return mPhase; }
}
|
运行结果: 
SumPathEffect
核心代码:
package com.zlsam.learngraphics.patheffect;
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.CornerPathEffect; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.Path; import android.graphics.SumPathEffect; import android.util.AttributeSet; import android.view.View;
/** * Created by zhanglong on 16/8/18. */ public class SumPathEffectView extends View {
private int mCornerRadius = 0; private float mSolidLength = 1F; private float mVirtualLength = 1F; private float mPhase = 0; private Path mPath; private Paint mPaint;
public SumPathEffectView(Context context) { this(context, null); }
public SumPathEffectView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public SumPathEffectView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);
if (isInEditMode()) { // Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new SumPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); return; }
// Init path mPath = new Path(); mPath.moveTo(10, 100); mPath.lineTo(60, 10); mPath.lineTo(120, 190); mPath.lineTo(180, 40); mPath.lineTo(240, 160); mPath.lineTo(300, 10); mPath.lineTo(360, 190); mPath.lineTo(420, 40); mPath.lineTo(480, 160);
// Init paint mPaint = new Paint(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(2); mPaint.setColor(Color.CYAN); mPaint.setPathEffect(new SumPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawPath(mPath, mPaint); }
public void setCornerRadius(int radius) { mCornerRadius = radius; mPaint.setPathEffect(new SumPathEffect(new DashPathEffect(new float[]{mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public int getCornerRadius() { return mCornerRadius; }
public void setSolidLength(float length) { mSolidLength = length; mPaint.setPathEffect(new SumPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public float getSolidLength() { return mSolidLength; }
public void setVirtualLength(float length) { mVirtualLength = length; mPaint.setPathEffect(new SumPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public float getVirtualLength() { return mVirtualLength; }
public void setPhase(float phase) { mPhase = phase; mPaint.setPathEffect(new SumPathEffect(new DashPathEffect(new float[] {mSolidLength, mVirtualLength}, mPhase), new CornerPathEffect(mCornerRadius))); invalidate(); }
public float getPhase() { return mPhase; } }
|
运行结果: 
本文的示例×××地址:https://github.com/zhanglong1/AndroidGraphicsDemo
|