Android杂谈(21)补坑+Path动画

转载请注意:http://blog.csdn.net/wjzj000/article/details/59112306

本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)

写在前面

来到学校接近一周的时间算是啥也没干,这篇博客便是打响武装反抗惰性学习第一枪。

之前写过一篇博客:http://blog.csdn.net/wjzj000/article/details/53929870
是关于官方的侧滑控件DrawerLayout,它本身的用法比较简单。只是我隐隐约约记得之前留了一个坑:那个可以动的控制侧滑菜单打开关闭的按钮。
正好结合这Path动画,把这个坑填上。


填坑

上篇博客中使用的有动画效果的箭头是来自:https://github.com/ChrisRenke/DrawerArrowDrawable
的开源作品,具体的使用方式,各位看官可以移步这位大神的GitHub上,一睹傲视武林一统江湖的王者之霸气。
这里简单梳理一下源码,然后进去本次博客想记录的核心Path动画。

    public DrawerArrowDrawable(Resources resources, boolean rounded) {
        this.rounded = rounded;
        float density = resources.getDisplayMetrics().density;
        float strokeWidthPixel = STROKE_WIDTH_DP * density;
        halfStrokeWidthPixel = strokeWidthPixel / 2;

        linePaint = new Paint(SUBPIXEL_TEXT_FLAG | ANTI_ALIAS_FLAG);
        linePaint.setStrokeCap(rounded ? ROUND : BUTT);
        linePaint.setColor(BLACK);
        linePaint.setStyle(STROKE);
        linePaint.setStrokeWidth(strokeWidthPixel);

        int dimen = (int) (DIMEN_DP * density);
        bounds = new Rect(0, 0, dimen, dimen);

        Path first, second;
        JoinedPath joinedA, joinedB;

        // 按钮的第一条线的相关路径操作
        first = new Path();
        first.moveTo(5.042f, 20f);
        // 三阶贝塞尔曲线:说白了就是给定四个坐标点(moveTo为起始点,rCubicTo分别代表第一个控制点,第二个控制点,结束的点),系统通过贝塞尔方程画出一个线来。
        first.rCubicTo(8.125f, -16.317f, 39.753f, -27.851f, 55.49f, -2.765f);
        second = new Path();
        second.moveTo(60.531f, 17.235f);
        second.rCubicTo(11.301f, 18.015f, -3.699f, 46.083f, -23.725f, 43.456f);
        //将路径缩放到给定的屏幕密度。(让按钮大小适配屏幕)
        scalePath(first, density);
        scalePath(second, density);
        joinedA = new JoinedPath(first, second);

        //省略大量类似代码
    }

从这里我们基本可以看出,作者是直接使用多个Path来完成效果。
其中通过PathMeasure每个Path上的每个点的坐标位置,然后不断的重新绘制线段。
作者通过,如下代码进行回调,请求重绘:

new DrawerLayout.SimpleDrawerListener() {
     @Override public void onDrawerSlide(View drawerView, float slideOffset) {
            offset = slideOffset;
            if (slideOffset >= .995) {
                flipped = true;
                drawerArrowDrawable.setFlip(flipped);
            }
            //省略部分代码
public void setFlip(boolean flip) {
        this.flip = flip;
        invalidateSelf();
}

回调产生以后,就会调用作者封装的draw方法:

private void draw(Canvas canvas) {
        pathA.getPointOnLine(parameter, coordsA);
        pathB.getPointOnLine(parameter, coordsB);
        if (rounded) insetPointsForRoundCaps();
        //coordsA当前路线的坐标,通过PathMeasure的getPosTan方法获得。
        canvas.drawLine(coordsA[0], coordsA[1], coordsB[0], coordsB[1], linePaint);
}

通过PathMeasure的getPosTan获取中Path的多点坐标,然后重新绘制线段。完成动态效果。

OK,关于填坑,就到这里了。因为作者的源码比较的长。所以各位看官如果感兴趣的话,可以自己移步作者的GitHub之上就查看源码。

作者继承了android.graphics.drawable.Drawable来完成这个效果:
关于Drawable的官方解释:
Drawable是“可以绘制的东西”的一般抽象。通常你会处理Drawable作为绘制东西到屏幕上检索的资源类型; Drawable类提供了一个通用的API来处理可能采取各种形式的底层视觉资源。与View不同,Drawable没有任何功能来接收事件或与用户交互。


Path动画

接下来的内容是结合ValueAnimator结合Path实现动态的Path效果。
先看一下效果:

这里写图片描述

直接上代码:

初始化Path和Paint:


paint = new Paint();
paint.setColor(Color.DKGRAY);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);

path = new Path();
path.moveTo(50, 50);
for (int i = 0; i < 4; i++) {
    path.lineTo(450 - i * 50, 50 + i * 50);
    path.lineTo(450 - i * 50, 450 - i * 50);
    path.lineTo(50 + i * 50, 450 - i * 50);
    path.lineTo(50 + i * 50, 100 + i * 50);
}

setPathEffect:

        PathMeasure measure = new PathMeasure(path, false);
        length = measure.getLength();

        animator = ValueAnimator.ofFloat(0.0f, 1.0f);
        animator.setDuration(5000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                //这里使用了一个DashPathEffect的技巧
                paint.setPathEffect(createPathEffect(length, fraction));
                postInvalidate();
            }
        });
        animator.start();

private static PathEffect createPathEffect(float pathLength, float phase) {
        return new DashPathEffect(new float[]{pathLength, pathLength},
                pathLength - phase * pathLength);
 }

我们在new DashPathEffect的时候传入的间隔值就是Path的全长,因此这里是没有间断的。我们控制Path的动画,实际是控制DashPathEffect的偏移,也就是pathLength - phase * pathLength,这个值是从Path全长随动画的进度逐渐减小到0。
对应到我们的效果之上就是Path从什么都没有,然后知道全部展现。(因为此时的偏移为0)

onDraw()中很简单,就是调用drawPath:

    @Override
    public void onDraw(Canvas c) {
        super.onDraw(c);
        c.drawPath(path, paint); 
    }

原理非常的简单,我们可以理解是使用了DashPathEffect的错位效果。我们都知道DashPathEffect的一般制作效果是波浪线。
而这里我们使用它的偏移属性,做到了线段追逐的效果。
因为需要动态更新效果,所以使用动画进行控制。

PS:相关源码基本都存放于我的这个开源项目之中:
https://github.com/zhiaixinyang/PersonalCollect


尾声

OK,坑到这也填的差不多了。
学习虽好,但不要贪杯。有情不必终老,暗香浮动恰好。

最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值