Android Paint之PathEffect详解

尊重原创,转载请标明出处    http://blog.csdn.net/abcdef314159

在之前讲Android Paint的使用详解的时候,其中有一个方法setPathEffect(PathEffect effect)没有详细介绍,这篇就结合代码来介绍一下,在之前说过PathEffect共有6个子类ComposePathEffect,CornerPathEffect,DashPathEffect,DiscretePathEffect,PathDashPathEffect,SumPathEffect,这些类代码量都很少,这里先一个个介绍

CornerPathEffect将Path的线段之间的夹角变成圆角。构造函数,其中radius为圆角的半径

    /**
     * Transforms geometries that are drawn (either STROKE or FILL styles) by
     * replacing any sharp angles between line segments into rounded angles of
     * the specified radius.
     * @param radius Amount to round sharp angles between line segments.
     */
    public CornerPathEffect(float radius) {
        native_instance = nativeCreate(radius);
    }

看一下代码

public class PathEffectView extends View {
	private Paint mPaint;
	private int marging = 82;
	private CornerPathEffect mCornerPathEffect[];
	private Path mPath[];

	public PathEffectView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	private void init() {
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setColor(Color.BLACK);
		mPaint.setStyle(Style.STROKE);
		mPaint.setStrokeWidth(6);
		mCornerPathEffect = new CornerPathEffect[8];
		mPath = new Path[8];
		for (int i = 0; i < mPath.length; i++) {
			Path path = new Path();
			path.moveTo(i * marging, marging);
			path.lineTo(300 + i * marging, 180);
			path.lineTo(400 + i * marging, 600);
			path.lineTo(200 + i * marging, 1000);
			path.lineTo(110 + i * marging, 1200);
			mPath[i] = path;
			mCornerPathEffect[i] = new CornerPathEffect(i * 10);
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
		for (int i = 0; i < mPath.length; i++) {
			mPaint.setPathEffect(mCornerPathEffect[i]);
			canvas.drawPath(mPath[i], mPaint);
		}
	}
}

运行结果为

DashPathEffect主要用于画虚线。构造函数,看注释,intervals必须大于大于2,phase是偏移量

    /**
     * The intervals array must contain an even number of entries (>=2), with
     * the even indices specifying the "on" intervals, and the odd indices
     * specifying the "off" intervals. phase is an offset into the intervals
     * array (mod the sum of all of the intervals). The intervals array
     * controls the length of the dashes. The paint's strokeWidth controls the
     * thickness of the dashes.
     * Note: this patheffect only affects drawing with the paint's style is set
     * to STROKE or FILL_AND_STROKE. It is ignored if the drawing is done with
     * style == FILL.
     * @param intervals array of ON and OFF distances
     * @param phase offset into the intervals array
     */
    public DashPathEffect(float intervals[], float phase) {
        if (intervals.length < 2) {
            throw new ArrayIndexOutOfBoundsException();
        }
        native_instance = nativeCreate(intervals, phase);
    }

看一下代码

public class PathEffectView extends View {
	private Paint mPaint;
	private int marging = 82;
	private DashPathEffect mCornerPathEffect[];
	private Path mPath[];

	public PathEffectView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	private void init() {
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setColor(Color.BLACK);
		mPaint.setStyle(Style.STROKE);
		mPaint.setStrokeWidth(6);
		mCornerPathEffect = new DashPathEffect[8];
		mPath = new Path[8];
		for (int i = 0; i < mPath.length; i++) {
			Path path = new Path();
			path.moveTo(i * marging, marging);
			path.lineTo(300 + i * marging, 180);
			path.lineTo(400 + i * marging, 600);
			path.lineTo(200 + i * marging, 1000);
			path.lineTo(110 + i * marging, 1200);
			mPath[i] = path;
			mCornerPathEffect[i] = new DashPathEffect(
					new float[] { 1, 2, 4, 8 }, 1);
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
		for (int i = 0; i < mPath.length; i++) {
			mPaint.setPathEffect(mCornerPathEffect[i]);
			canvas.drawPath(mPath[i], mPaint);
		}
	}
}

运行结果为

这里phase的偏移量是指偏移指定长度的位置开始画,但总长度还是不变,我们改一下再看看

			mCornerPathEffect[i] = new DashPathEffect(new float[] { 10, 20, 40,
					80 }, i * 10);

运行结果

先画长度为10的实线,再画长度为20的虚线,接着画长度为40的实线,最后画长度为80的虚线,看一下起始位置,每次的最开始都不一样,因为每次偏移的都不一样,但总长度是不变的,因为上面的线只是左右平移,长度并没有减少,看到上面的线是越来越短,其实这是一种巧合,因为后面到虚线了,看不到了。通俗一点来说就是,线的开始位置和终止位置都没有改变,线就像一个无限长的绳,偏移量就相当于绳往下(后)拽的距离。我们打印看一下长度就知道了,修改一下

		for (int i = 0; i < mPath.length; i++) {
			mPaint.setPathEffect(mCornerPathEffect[i]);
			canvas.drawPath(mPath[i], mPaint);
			PathMeasure measure = new PathMeasure(mPath[i], false);
			Log.d("wld_________", measure.getLength() + "");
		}

看一下log,长度都一样,没有变。

DiscretePathEffect切断线段,segmentLength是指定切断的长度,deviation为切断之后线段的偏移量,随机的,小于等于deviation。

    /**
     * Chop the path into lines of segmentLength, randomly deviating from the
     * original path by deviation.
     */
    public DiscretePathEffect(float segmentLength, float deviation) {
        native_instance = nativeCreate(segmentLength, deviation);
    }

看一下代码

public class PathEffectView extends View {
	private Paint mPaint;
	private int marging = 82;
	private DiscretePathEffect mPathEffect[];
	private Path mPath[];

	public PathEffectView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	private void init() {
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setColor(Color.BLACK);
		mPaint.setStyle(Style.STROKE);
		mPaint.setStrokeWidth(6);
		mPathEffect = new DiscretePathEffect[8];
		mPath = new Path[8];
		for (int i = 0; i < mPath.length; i++) {
			Path path = new Path();
			path.moveTo(i * marging, marging);
			path.lineTo(300 + i * marging, 180);
			path.lineTo(400 + i * marging, 600);
			path.lineTo(200 + i * marging, 1000);
			path.lineTo(110 + i * marging, 1200);
			mPath[i] = path;
			mPathEffect[i] = new DiscretePathEffect(10, 3 * i);
		}
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
		for (int i = 0; i < mPath.length; i++) {
			mPaint.setPathEffect(mPathEffect[i]);
			canvas.drawPath(mPath[i], mPaint);
		}
	}
}

运行结果

第一条线偏移量为0,所以看不出来有什么变化。下面再来修改一下,每隔长度为1就中断一次,3*i是偏移的最大长度,

			mPathEffect[i] = new DiscretePathEffect(1, 3 * i);

感觉有点像磁铁一样,我们来测量一下他的长度

for (int i = 0; i < mPath.length; i++) {
			mPaint.setPathEffect(mPathEffect[i]);
			canvas.drawPath(mPath[i], mPaint);
			PathMeasure measure = new PathMeasure(mPath[i], false);
			Log.d("wld__________", measure.getLength() + "");
		}

不可思议,每个长度都一样,还和之前测的一样,一点都没变。

PathDashPathEffect和DashPathEffect差不多,不同的是PathDashPathEffect可以通过自定义图形来绘制path,先看一下他的代码

    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;
    }

    /**
     * Dash the drawn path by stamping it with the specified shape. This only
     * applies to drawings when the paint's style is STROKE or STROKE_AND_FILL.
     * If the paint's style is FILL, then this effect is ignored. The paint's
     * strokeWidth does not affect the results.
     * @param shape The path to stamp along
     * @param advance spacing between each stamp of shape
     * @param phase amount to offset before the first shape is stamped
     * @param style how to transform the shape at each position as it is stamped
     */
    public PathDashPathEffect(Path shape, float advance, float phase,
                              Style style) {
        native_instance = nativeCreate(shape.ni(), advance, phase,
                                       style.native_style);
    }

    private static native long nativeCreate(long native_path, float advance,
                                           float phase, int native_style);

shape是填充的图形,这个图形可以自己绘制,advance是图形之间的间距,phase是path的偏移量,其中有3种style,TRANSLATE是指图形以平移的方式填充path,ROTATE会根据path的旋转而旋转,MORPH和ROTATE差不多,不过有一点就是MORPH会在转角的连接处以平滑的方式连接,下面看一下代码

public class PathEffectView extends View {
	private Paint mPaint;
	private int marging = 82;
	private PathEffect mPathEffect1;
	private PathEffect mPathEffect2;
	private PathEffect mPathEffect3;
	private Path mPath;

	public PathEffectView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	private void init() {
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setStyle(Style.STROKE);
		mPaint.setStrokeWidth(6);
		mPaint.setColor(Color.RED);
		mPath = new Path();
		mPath.moveTo(0, marging);
		mPath.lineTo(300, 180);
		mPath.lineTo(400, 600);
		mPath.lineTo(200, 1000);
		mPath.lineTo(800, 1200);
		Path p = new Path();
		p.addRect(0, 0, 64, 12, Path.Direction.CCW);
		mPathEffect1 = new PathDashPathEffect(p, 128, 0,
				android.graphics.PathDashPathEffect.Style.MORPH);
		mPathEffect2 = new PathDashPathEffect(p, 128, 0,
				android.graphics.PathDashPathEffect.Style.ROTATE);
		mPathEffect3 = new PathDashPathEffect(p, 128, 0,
				android.graphics.PathDashPathEffect.Style.TRANSLATE);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
		mPaint.setPathEffect(mPathEffect1);
		canvas.drawPath(mPath, mPaint);
		canvas.translate(200, 0);
		mPaint.setPathEffect(mPathEffect2);
		canvas.drawPath(mPath, mPaint);
		canvas.translate(200, 0);
		mPaint.setPathEffect(mPathEffect3);
		canvas.drawPath(mPath, mPaint);
	}
}

看一下运行效果,

自定义了一个矩形,前两个矩形的方向会随着path的方向而改变,且第一个在连接处会以平滑的方式过渡。下面着重说一下PathDashPathEffect的几个参数,第一个是自定义的图形,这个就不在多说,主要分析一下第二个和第三个参数,第二个参数是图形的间距,这个间距是指第一个图形和第二个图形起始位置的间距,修改一下代码,全部用MORPH模式来测试

		mPathEffect1 = new PathDashPathEffect(p, 0, 0,
				android.graphics.PathDashPathEffect.Style.MORPH);
		mPathEffect2 = new PathDashPathEffect(p, 64, 0,
				android.graphics.PathDashPathEffect.Style.MORPH);
		mPathEffect3 = new PathDashPathEffect(p, 228, 0,
				android.graphics.PathDashPathEffect.Style.MORPH);

看一下运行的结果

第一个是没有间距的,第二个间距等于矩形的宽度,所以正好相当于矩形首尾相连中间没有间隙,最后一个有间隙。再看第三个参数,就是偏移量,这个和第二个参数有关,在1到advance中间时,偏移的距离在逐渐减少,当偏移量等于advance的倍数的时候,偏移的距离为0,当偏移量大于advance的时候,会对他求余。我们看一下代码

	private void init() {
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setStyle(Style.STROKE);
		mPaint.setStrokeWidth(6);
		mPaint.setColor(Color.RED);
		mPath = new Path();
		mPath.moveTo(0, marging);
		mPath.lineTo(300, 180);
		mPath.lineTo(400, 600);
		mPath.lineTo(200, 1000);
		mPath.lineTo(800, 1200);
		Path p = new Path();
		p.addRect(0, 0, 64, 12, Path.Direction.CCW);
		mPathEffect1 = new PathDashPathEffect(p, 128, 128,
				android.graphics.PathDashPathEffect.Style.MORPH);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawColor(Color.WHITE);
		mPaint.setPathEffect(mPathEffect1);
		canvas.drawPath(mPath, mPaint);
	}

看一下运行结果

我们看到是没有偏移的,在修改一下代码

		mPathEffect1 = new PathDashPathEffect(p, 128, 129,
				android.graphics.PathDashPathEffect.Style.MORPH);

看一下结果,

我们看到偏移量已经达到最大,其实129和1的结果是一样的,因为129对128求余所得结果也是1,偏移量从1到128逐渐增大时,偏移的距离逐渐减少,且当偏移量为128时则没有偏移。再来改一下代码

		mPathEffect1 = new PathDashPathEffect(p, 28, 1,
				android.graphics.PathDashPathEffect.Style.MORPH);

前面已经分析过,当advance大于自定义图形的尺寸时才会出现间隙(这里主要是对MORPH这个style,当style为TRANSLATE时,advance必须大于自定义图形的高时才会出现间隙,这个也很好理解),所以这个就会是一条看不到间隙的path,且有偏移,我们看一下

当把代码在改一下的时候

		mPathEffect1 = new PathDashPathEffect(p, 28, 28,
				android.graphics.PathDashPathEffect.Style.MORPH);

会看到没有偏移的,这个截图就不在贴出。如果还是不太明白,也可以看一下下面这个视频PathDashPathEffect视频

ComposePathEffect是一种组合模式,把两种path所具有的特性组合起来,先看一下源码

    /**
     * Construct a PathEffect whose effect is to apply first the inner effect
     * and the the outer pathEffect (e.g. outer(inner(path))).
     */
    public ComposePathEffect(PathEffect outerpe, PathEffect innerpe) {
        native_instance = nativeCreate(outerpe.native_instance,
                                       innerpe.native_instance);
    }

    private static native long nativeCreate(long nativeOuterpe,
                                            long nativeInnerpe);

他会会首先将innerpe的特性表现出来,然后再增加outerpe的效果,我们看一下代码

public class PathEffectView extends View {
	private Paint mPaint;
	private int marging = 82;
	private PathEffect mEffects[];
	private Path mPath;

	public PathEffectView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	private void init() {
		mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaint.setStyle(Style.STROKE);
		mPaint.setStrokeWidth(6);
		mPaint.setColor(Color.RED);
		mPath = new Path();
		mPath.moveTo(0, marging);
		mPath.lineTo(300, 180);
		mPath.lineTo(400, 600);
		mPath.lineTo(200, 1000);
		mPath.lineTo(800, 1200);
		Path p = new Path();
		p.addRect(0, 0, 64, 12, Path.Direction.CCW);
		mEffects = new PathEffect[3];
		mEffects[0] = new CornerPathEffect(80);
		mEffects[1] = new DashPathEffect(new float[] { 20, 10, 5, 10 }, 0);
		mEffects[2] = new ComposePathEffect(mEffects[1], mEffects[0]);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		for (int i = 0; i < mEffects.length; i++) {
			mPaint.setPathEffect(mEffects[i]);
			canvas.drawPath(mPath, mPaint);
			canvas.translate(200, 0);
		}
	}
}

在看一下运行结果

第一个是圆角的,第二个是虚线的,所以组合的第三个就是圆角到虚线的。在修改一下代码,调换一下组合的位置,

		mEffects[2] = new ComposePathEffect(mEffects[0], mEffects[1]);

看一下运行结果

我们看到组合模式基本上没变,这是因为我们先提取的是第二个图的效果,再提取的是第一个的,所以看不到上面效果,我们在改一下代码

		mEffects[1] = new DashPathEffect(new float[] { 200, 10, 5, 10 }, 0);

在看一下运行结果

OK,我们再来看最后一种SumPathEffect,他相当于把两种效果分别展示然后再组合在一起。还是用上面的代码简单的修改一下

		mEffects[1] = new DashPathEffect(new float[] { 20, 10, 5, 10 }, 0);
		mEffects[2] = new SumPathEffect(mEffects[1], mEffects[0]);

来看一下运行效果

OK,到目前为止,PathEffect的6种效果全部分析完毕。当然,如果想制作动态的效果,可以在onDraw方法中调用invalidate()方法,然后不停的修改偏移量就行了。

时间: 2024-10-22 02:57:54

Android Paint之PathEffect详解的相关文章

Android Paint之ColorFilter详解

尊重原创,转载请标明出处    http://blog.csdn.net/abcdef314159 在之前讲到Android Paint的使用详解的时候,其中setColorFilter(ColorFilter filter)方法没有讲,今天就来简单的分析一下,在Android中ColorFilter共有3个子类,ColorMatrixColorFilter,LightingColorFilter,PorterDuffColorFilter,今天先来看第一个ColorMatrixColorFil

Android Paint、Path详解

都是自己随手练习时写的一些小demo Paint类: package com.gyz; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient;

Android Matrix源码详解

尊重原创,转载请标明出处   http://blog.csdn.net/abcdef314159 Matrix是一个3*3的矩阵,通过矩阵执行对图像的平移,旋转,缩放,斜切等操作.先看一段代码 public static final int MSCALE_X = 0; //!< use with getValues/setValues public static final int MSKEW_X = 1; //!< use with getValues/setValues public st

Android:ViewPager扩展详解——带有导航的ViewPagerIndicator(附带图片缓存,异步加载图片)

大家都用过viewpager了, github上有对viewpager进行扩展,导航风格更加丰富,这个开源项目是ViewPagerIndicator,很好用,但是例子比较简单,实际用起来要进行很多扩展,比如在fragment里进行图片缓存和图片异步加载. 下面是ViewPagerIndicator源码运行后的效果,大家也都看过了,我多此一举截几张图: 下载源码请点击这里 ===========================================华丽的分割线==============

给 Android 开发者的 RxJava 详解

作者:扔物线 前言 我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个月,我也发现国内越来越多的人开始提及 RxJava .有人说『RxJava 真是太好用了』,有人说『RxJava 真是太难用了』,另外更多的人表示:我真的百度了也谷歌了,但我还是想问: RxJava 到底是什么? 鉴于 RxJava 目前这种既火爆又神秘的现状,而我又在一年的使用

Android四大组件--Activity详解

Android四大组件--Activity详解 分类: android android应用android开发 本文的主要内容包括1.activity的建立.配置和使用:2.activity的跳转和传值:3.startActivityForResult:4.activity的生命周期. 1.activity的建立.配置和使用 Activity是一个应用中的组件,它为用户提供一个可视的界面,方便用户操作,比如说拔打电话.照相.发邮件或者是浏览地图等.每个activity会提供一个可视的窗口,一般情况

Android技术18:Android中Adapter类详解

1.Adapter设计模式 Android中adapter接口有很多种实现,例如,ArrayAdapter,BaseAdapter,CursorAdapter,SimpleAdapter,SimpleCursorAdapter等,他们分别对应不同的数据源.例如,ArrayAdater对应List和数组数据源,而CursorAdapter对应Cursor对象(一般从数据库中获取的记录集).这些Adapter都需要getView方法返回当前列表项显示的View对象.当Model发生改变时,会调用Ba

Android学习Scroller(五)——详解Scroller调用过程以及View的重绘

MainActivity如下: package cc.ww; import android.os.Bundle; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; import android.app.Activity;

Android:调用webservice详解;

很多时候要用到android端调用webservice服务, 下面例子就是调用webservice 以及对流的多种方式处理: package com.example.android_webservice; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputSt