#本文基于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


核心代码:

packagecom.zlsam.learngraphics.patheffect;

importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.CornerPathEffect;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.util.AttributeSet;
importandroid.view.View;

/**
*Createdbyzhanglongon16/8/18.
*/
publicclassCornerPathEffectViewextendsView{

privateintmCornerRadius=5;
privatePathmPath;
privatePaintmPaint;

publicCornerPathEffectView(Contextcontext){
this(context,null);
}

publicCornerPathEffectView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}

publicCornerPathEffectView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);

if(isInEditMode()){
//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newCornerPathEffect(mCornerRadius));
return;
}

//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newCornerPathEffect(mCornerRadius));
}

@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawPath(mPath,mPaint);
}

publicvoidsetCornerRadius(intradius){
mCornerRadius=radius;
mPaint.setPathEffect(newCornerPathEffect(mCornerRadius));
invalidate();
}

publicintgetCornerRadius(){
returnmCornerRadius;
}
}


运行效果:


DashPathEffect


核心代码:


packagecom.zlsam.learngraphics.patheffect;

importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.DashPathEffect;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.util.AttributeSet;
importandroid.view.View;

/**
*Createdbyzhanglongon16/8/18.
*/
publicclassDashPathEffectViewextendsView{

privatefloatmSolidLength=1F;
privatefloatmVirtualLength=1F;
privatefloatmPhase=0;
privatePathmPath;
privatePaintmPaint;


publicDashPathEffectView(Contextcontext){
this(context,null);
}

publicDashPathEffectView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}

publicDashPathEffectView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
if(isInEditMode()){
//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase));
return;
}

//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase));
}

@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawPath(mPath,mPaint);
}

publicvoidsetSolidLength(floatlength){
mSolidLength=length;
mPaint.setPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase));
invalidate();
}

publicfloatgetSolidLength(){
returnmSolidLength;
}

publicvoidsetVirtualLength(floatlength){
mVirtualLength=length;
mPaint.setPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase));
invalidate();
}

publicfloatgetVirtualLength(){
returnmVirtualLength;
}

publicvoidsetPhase(floatphase){
mPhase=phase;
mPaint.setPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase));
invalidate();
}

publicfloatgetPhase(){
returnmPhase;
}

}


运行效果:


PathDashPathEffect


核心代码:


packagecom.zlsam.learngraphics.patheffect;

importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.graphics.PathDashPathEffect;
importandroid.graphics.RectF;
importandroid.util.AttributeSet;
importandroid.view.View;

/**
*Createdbyzhanglongon16/8/18.
*/
publicclassPathDashPathEffectViewextendsView{

privatePathmPath;
privatePaintmPaint;

publicPathDashPathEffectView(Contextcontext){
this(context,null);
}

publicPathDashPathEffectView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}

publicPathDashPathEffectView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
if(isInEditMode()){
//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);

Pathpath=newPath();
path.addOval(newRectF(0,0,10,20),Path.Direction.CCW);
mPaint.setPathEffect(newPathDashPathEffect(path,20,0,PathDashPathEffect.Style.ROTATE));
return;
}

//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
Pathpath=newPath();
path.addOval(newRectF(0,0,10,20),Path.Direction.CCW);
mPaint.setPathEffect(newPathDashPathEffect(path,20,0,PathDashPathEffect.Style.ROTATE));
}

@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawPath(mPath,mPaint);
}
}


运行结果:


DiscretePathEffect


核心代码:


packagecom.zlsam.learngraphics.patheffect;

importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.DiscretePathEffect;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.util.AttributeSet;
importandroid.view.View;

/**
*Createdbyzhanglongon16/8/18.
*/
publicclassDiscretePathEffectViewextendsView{

privatefloatmSegmentLength=1.0F;
privatefloatmDeviation=1.0F;
privatePathmPath;
privatePaintmPaint;

publicDiscretePathEffectView(Contextcontext){
this(context,null);
}

publicDiscretePathEffectView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}

publicDiscretePathEffectView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
if(isInEditMode()){
//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newDiscretePathEffect(mSegmentLength,mDeviation));
return;
}

//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newDiscretePathEffect(mSegmentLength,mDeviation));
}

@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawPath(mPath,mPaint);
}

publicvoidsetSegmentLength(floatlength){
mSegmentLength=length;
mPaint.setPathEffect(newDiscretePathEffect(mSegmentLength,mDeviation));
invalidate();
}

publicfloatgetSegmentLength(){
returnmSegmentLength;
}

publicvoidsetDeviation(floatdeviation){
mDeviation=deviation;
mPaint.setPathEffect(newDiscretePathEffect(mSegmentLength,mDeviation));
invalidate();
}

publicfloatgetDeviation(){
returnmDeviation;
}
}


运行结果:


ComposePathEffect


核心代码:


packagecom.zlsam.learngraphics.patheffect;

importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.ComposePathEffect;
importandroid.graphics.CornerPathEffect;
importandroid.graphics.DashPathEffect;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.util.AttributeSet;
importandroid.view.View;

/**
*Createdbyzhanglongon16/8/18.
*/
publicclassComposePathEffectViewextendsView{

privateintmCornerRadius=0;
privatefloatmSolidLength=1F;
privatefloatmVirtualLength=1F;
privatefloatmPhase=0;
privatePathmPath;
privatePaintmPaint;

publicComposePathEffectView(Contextcontext){
this(context,null);
}

publicComposePathEffectView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}

publicComposePathEffectView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);
if(isInEditMode()){
//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newComposePathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
return;
}

//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newComposePathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
}

@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawPath(mPath,mPaint);
}

publicvoidsetCornerRadius(intradius){
mCornerRadius=radius;
mPaint.setPathEffect(newComposePathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicintgetCornerRadius(){
returnmCornerRadius;
}

publicvoidsetSolidLength(floatlength){
mSolidLength=length;
mPaint.setPathEffect(newComposePathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicfloatgetSolidLength(){
returnmSolidLength;
}

publicvoidsetVirtualLength(floatlength){
mVirtualLength=length;
mPaint.setPathEffect(newComposePathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicfloatgetVirtualLength(){
returnmVirtualLength;
}

publicvoidsetPhase(floatphase){
mPhase=phase;
mPaint.setPathEffect(newComposePathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicfloatgetPhase(){
returnmPhase;
}

}


运行结果:


SumPathEffect


核心代码:


packagecom.zlsam.learngraphics.patheffect;

importandroid.content.Context;
importandroid.graphics.Canvas;
importandroid.graphics.Color;
importandroid.graphics.CornerPathEffect;
importandroid.graphics.DashPathEffect;
importandroid.graphics.Paint;
importandroid.graphics.Path;
importandroid.graphics.SumPathEffect;
importandroid.util.AttributeSet;
importandroid.view.View;

/**
*Createdbyzhanglongon16/8/18.
*/
publicclassSumPathEffectViewextendsView{

privateintmCornerRadius=0;
privatefloatmSolidLength=1F;
privatefloatmVirtualLength=1F;
privatefloatmPhase=0;
privatePathmPath;
privatePaintmPaint;

publicSumPathEffectView(Contextcontext){
this(context,null);
}

publicSumPathEffectView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
}

publicSumPathEffectView(Contextcontext,AttributeSetattrs,intdefStyleAttr){
super(context,attrs,defStyleAttr);

if(isInEditMode()){
//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newSumPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
return;
}

//Initpath
mPath=newPath();
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);

//Initpaint
mPaint=newPaint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(2);
mPaint.setColor(Color.CYAN);
mPaint.setPathEffect(newSumPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
}

@Override
protectedvoidonDraw(Canvascanvas){
super.onDraw(canvas);
canvas.drawPath(mPath,mPaint);
}

publicvoidsetCornerRadius(intradius){
mCornerRadius=radius;
mPaint.setPathEffect(newSumPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicintgetCornerRadius(){
returnmCornerRadius;
}

publicvoidsetSolidLength(floatlength){
mSolidLength=length;
mPaint.setPathEffect(newSumPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicfloatgetSolidLength(){
returnmSolidLength;
}

publicvoidsetVirtualLength(floatlength){
mVirtualLength=length;
mPaint.setPathEffect(newSumPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicfloatgetVirtualLength(){
returnmVirtualLength;
}

publicvoidsetPhase(floatphase){
mPhase=phase;
mPaint.setPathEffect(newSumPathEffect(newDashPathEffect(newfloat[]{mSolidLength,mVirtualLength},mPhase),newCornerPathEffect(mCornerRadius)));
invalidate();
}

publicfloatgetPhase(){
returnmPhase;
}
}


运行结果:


本文的示例×××地址:https://github.com/zhanglong1/AndroidGraphicsDemo