Android:视图绘制(五) ------Paint进阶之PathEffect

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010635353/article/details/52701298

本文主要介绍一下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();
    }
}

猜你喜欢

转载自blog.csdn.net/u010635353/article/details/52701298