logo

【译】Android 路径动画

开发干货Romain Guy2018-09-25

【译】Android 路径动画

注:本文来自于Romain Guy大神 原文在此

我几个月以前就已经离开了Android小组,但是我依然喜欢写一些UI视觉效果的Demo给Android平台.目前在旅行的空闲时间里写了一些新的Demo,我希望能够教你们一些开发技巧.

这个新的Demo叫做Road Trip USA ,展示了如何实现路径动画效果,下面的视频展示了路径动画的效果:

看视频

路径动画的用途很广泛:可以展示一张地图,沿途画出每个城市的边界线,每个城市的名字,可以作为一个加载进度条,进度条可以在开始只描述一个起始点,然后随着线条的绘制逐渐展现整个进度条.

为了更好的理解下文,建议你先看看源码或者获取APK体验一下.

路径特效

路径动画主要是靠Android Api PathEffect实现.一个PathEffect对象可以传递给Canvas.drawPath()的Path对象.目前主要的PathEffect 类型有:

  • CornerPathEffect 把路径连线的夹角变为圆弧夹角
  • DashPathEffect 把路径的连线变为虚线
  • DiscreatePathEffect 把路径变为一系列随机的,偏离原路径的片段
  • PathDashPathEffect 使用另外一种路径作为模板来绘制当前的路径

这些类型又可以被ComposePathEffect或者SumPathEffect组合,举个例子:一个被连接在一起的线段通过使用ComposeEffect(包含CornetPathEffect和DashPathEffect)能够被转为圆角虚线的路径.如下图所示 image

逐步退出效果

下面的进度条实例就是使用了一个PathDashPathEffect和凹进去的箭头作为模板.因为PathEffect Api已经帮我们做了所有的工作,我们只需要创建一个路径就可以了:

private Path makeConvexArrow(float length, float height) {
    Path p = new Path();
    p.moveTo(0.0f, -height / 2.0f);
    p.lineTo(length - height / 4.0f, -height / 2.0f);
    p.lineTo(length, 0.0f);
    p.lineTo(length - height / 4.0f, height / 2.0f);
    p.lineTo(0.0f, height / 2.0f);
    p.lineTo(0.0f + height / 4.0f, 0.0f);
    p.close();
    return p;
}

// 构建一个路径  
Path path = new Path();
path.moveTo(32, 32);
path.lineTo(232, 32);

// 创建PathEffect  
PathEffect effect = new PathDashPathEffect(
    makeConvexArrow(24.0f, 14.0f),    // 模板
    36.0f,                            // 两个模板之间的距离
    0.0f,                             // 第一个模板的偏移距离
    PathDashPathEffect.Style.ROTATE); // 路径效果

// 把特效应用在路径上并画出来
paint.setPathEffect(effect);
canvas.drawPath(path, paint);

我们要实现逐步退出效果的关键是PathDashPathEffect的第三个参数phase(与第一个模板的偏移距离),使用动画改变这个值,就能够达到逐渐退出的效果. image

路径动画

路径动画也是使用了phase参数,但是现在使用的是DashPathEffect而不是PathDashPathEffect.你制定虚线的的长度,也可以制定虚线之间的间隙,传递给DashPathEffect的构造函数.只要让虚线和空隙的长度比路径本身长的话,就能够实现路径动画.

计算一个线段的长度非常简单,但是要计算一个随意的线段长度就有一点复杂了,因为我们需要计算二次或者三次曲线.幸运的是android提供给我们一个简单的api来计算这种路径的长度:

//创建一个路径  
Path path = makePath();

// 计算路径长度,就是这么简单
PathMeasure measure = new PathMeasure(path, false);
float length = measure.getLength();

// 设置DashPathEffect
PathEffect effect = new DashPathEffect(new float[] { length, length }, 0.0f);
paint.setPathEffect(effect);

canvas.drawPath(path, paint);

运行上面这段代码你会发现几乎和不使用PathEffect一样,这是因为DashPathEffect总是先画实线,所以我们需要先创建一个长度和路径长度一样的空隙

PathEffect effect = new DashPathEffect(new float[] { length, length }, length);

接下来我们只需要使用属性动画将phase的值从0变到length就可以了.你可以在StateView.java里面看到整个动画的实现.

描画箭头

箭头指示器的实现要更加多一点技巧,不再只是使用一个PathEffect,我们需要2个:一个DashPathEffect 用来画箭头的尾部,一个PathDashPathEffect用来画箭头顶部.PathDashPathEffect的advance参数设置为整个箭头从顶部到尾部的路径长度.

你可以在IntroView.java找到整个实现.

使用SVG 路径

在这个demo里面我使用SVG文件来存储路径信息,SVG文件加载虽然很慢,但是完全可以使用其他简化版二进制格式的方式来加载,因为我们只需要路径的几何数据,而不需要样式数据.

我选择了第三方库androidsvg,因为它使用简单,但是它却不能获取到SVG文件里面的路径信息.所以我使用一个自定义的Canvas,并且复写drawPath()方法:

Canvas canvas = new Canvas() {
    private final Matrix mMatrix = new Matrix();

    @Override
    public void drawPath(Path path, Paint paint) {
        Path dst = new Path();

        // 获取 matrix
        getMatrix(mMatrix);
        // 将 matrix应用到路径
        path.transform(mMatrix, dst);
        // 保存路径信息
        mPaths.add(new SvgPath(dst, new Paint(mSourcePaint)));
    }
};

// 加载一个svg文件
SVG svg = SVG.getFromResource(context, R.raw.map_usa);
// 将svg画出来
svg.renderToCanvas(canvas);

路径已经被预加载了,所以我们能够准确的获取到路径在各种屏幕上的大小.一个更加高级的版本你可以参考SvgHelper.java.这个实现增加能够让路径自动在它的容器里缩放.

其他的视觉效果

如果你仔细体验这个Demo,你将会注意到其他的几个视觉效果:

  • 黑白色转换,当把一行图片滑向左边的时候出现
  • 固定滑动,一种类似固定的卡片滑动效果,当上下滑动列表的时候出现
  • 并行滑动,不同的项目以不同的速度滑动
  • 动态实现标题栏透明,和Google Music效果类似

这些所有的效果都可以在MainActivity.java里找到.

本文已由作者授权发布,版权属于创宇前端。欢迎注明出处转载本文。本文链接:https://knownsec-fed.com/2018-09-25-yi-android-lu-jing-dong-hua/
想要看到更多来自知道创宇开发一线的分享,请搜索关注我们的微信公众号:创宇前端(KnownsecFED)。欢迎留言讨论,我们会尽可能回复。
感谢您的阅读。