Android自定义动画框架让View实现Path动画

这篇博客主要是从canvas.drawPath()的效果来让View去实现moveTo,LineTo,CubicTo,Quato的Path动画

1:首先看一下Path的moveTo,LineTo,CubicTo的效果

package voice.yuekong.com.animationtest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.View;

/**
 * Created by Zhongqi.Shao on 2016/12/21.
 */
public class AnimationView extends View {
    public AnimationView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(7);
        paint.setColor(Color.RED);

        Path path = new Path();
        path.moveTo(100, 100);
        path.cubicTo(200, 150, 300, 300, 400, 200);

        canvas.drawPath(path, paint);

    }
}
 
效果如图:

2:为了更方便实现View的lineTo,moveTo,cubicTo动画一般实现View的平移,贝塞尔曲线运动我们都会到ObjectAnimator smallCircleAnim = ObjectAnimator.ofObject(mSmallCircleView, "Center", null, Path);让View沿着Path路线走,这次实现的目标就是让View通过属性动画改变自身属性translationX和translationY来实现moveTo,cubicTo,lineTo动画。最终会用这种形式来完成View的Path动画

AnimatorPath animatorPath = new AnimatorPath();
animatorPath.moveto(0, 0);
animatorPath.cubicto(dp2px(-200), dp2px(200), dp2px(-300), dp2px(100), dp2px(-200), dp2px(0));
ObjectAnimator objAnimator = ObjectAnimator.ofObject(this, "PointPath", new MyEvalueTor(), animatorPath.getPoints().toArray());

Path动画最终都是改变View的translationX和translationY来改变位置,如果我们想让View移动到具体位置Endpoint(X,Y)简称moveTo或者想让View缓慢平移到具体位置EndPoint(X,Y)简称lineTo或者让View按照贝塞尔曲线移动到具体位置EndPoint(X,Y)简称CubicTo,只需要记录每个过程的终点的偏移量和这段过程的动作,然后通过ObjectAnimator的估值器TypeEvaluator去计算出每个过程的点的位置,具体代码如下:PathPoint.java是每个运动过程的终点偏移量和贝塞尔三阶过程必须要的控制点位置

package voice.yuekong.com.animationtest;

/**
 * Created by Zhongqi.Shao on 2016/12/22.
 */
public class PointPath {

    public static final int MOVE = 0x01;
    public static final int LINE_TO = 0x02;
    public static final int CUBIC_TO = 0x03;

    //偏移量
    public float mX;
    public float mY;
    public float cubicX1;
    public float cubicY1;
    public float cubicX2;
    public float cubicY2;
    public int mAction;


    public PointPath(int action, float x, float y) {
        mAction = action;
        mX = x;
        mY = y;
    }

    public PointPath(float x1, float y1, float x2, float y2, float endX, float endY) {
        mAction = CUBIC_TO;
        cubicX1 = x1;
        cubicY1 = y1;
        cubicX2 = x2;
        cubicY2 = y2;
        mX = endX;
        mY = endY;
    }

    public static PointPath moveTo(float x, float y) {
        return new PointPath(MOVE, x, y);
    }

    public static PointPath lineTo(float x, float y) {
        return new PointPath(LINE_TO, x, y);
    }

    public static PointPath cubicTo(float x1, float y1, float x2, float y2, float endX, float endY) {
        return new PointPath(x1, y1, x2, y2, endX, endY);
    }
}

然后AnimationPath.java记录了所有终点的位置

package voice.yuekong.com.animationtest;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Zhongqi.Shao on 2016/12/22.
 */
public class AnimatorPath {

    private List<PointPath> pointPathList = new ArrayList<PointPath>();

    public void moveto(float x, float y) {
        pointPathList.add(PointPath.moveTo(x, y));
    }

    public void lineto(float x, float y) {
        pointPathList.add(PointPath.lineTo(x, y));
    }

    public void cubicto(float x1, float y1, float x2, float y2, float endX, float endY) {
        pointPathList.add(PointPath.cubicTo(x1, y1, x2, y2, endX, endY));
    }

    public List<PointPath> getPoints() {
        return pointPathList;
    }
}
MainActivity实现的就是将布局中的按钮按照框架提供的方法进行贝塞尔曲线三阶移动,移动过程中进行ScaleX和ScaleY的扩大动画从而实现水波纹ripple
的效果,动画结束后让另外3个View依次执行扩大动画从0-1的过程
package voice.yuekong.com.animationtest;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.RelativeLayout;

/**
 * Created by Zhongqi.Shao on 2016/12/22.
 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mButton;
    private String TAG_BTN = "TAG_BTN";
    private boolean isReveal = false;
    private RelativeLayout mContainer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.animation_layout);
        mButton = (Button) findViewById(R.id.btn);
        mButton.setOnClickListener(this);
        mButton.setTag(TAG_BTN);
        mContainer = (RelativeLayout) findViewById(R.id.rl_container);
    }

    @Override
    public void onClick(View view) {
        if (view.getTag().equals(TAG_BTN)) {
            startAnimator(view);
        }
    }

    private void startAnimator(final View view) {
        final float startX = view.getX();
        AnimatorPath animatorPath = new AnimatorPath();
        animatorPath.moveto(0, 0);
        animatorPath.cubicto(dp2px(-200), dp2px(200), dp2px(-300), dp2px(100), dp2px(-200), dp2px(0));
        ObjectAnimator objAnimator = ObjectAnimator.ofObject(this, "PointPath", new MyEvalueTor(), animatorPath.getPoints().toArray());
        objAnimator.setDuration(2000);
        objAnimator.setInterpolator(new LinearInterpolator());
        objAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                if (Math.abs(startX - view.getX()) > dp2px(5)) {
                    if (!isReveal) {
                        PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat("scaleX", 1, 13);
                        PropertyValuesHolder holderY = PropertyValuesHolder.ofFloat("scaleY", 1, 13);
                        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holderX, holderY);
                        animator.setDuration(1000);
                        animator.addListener(new Animator.AnimatorListener() {
                            @Override
                            public void onAnimationStart(Animator animator) {

                            }

                            @Override
                            public void onAnimationEnd(Animator animator) {
                                mContainer.setBackgroundColor(MainActivity.this.getResources().getColor(R.color.appColor));
                                mButton.setVisibility(View.INVISIBLE);
                                for (int i = 0; i < mContainer.getChildCount(); i++) {
                                    View childView = mContainer.getChildAt(i);
                                    if (childView.getId() == R.id.btn) {
                                        continue;
                                    }
                                    ViewPropertyAnimator animatorView = childView.animate().scaleX(1).scaleY(1);
                                    animatorView.setDuration(1000);
                                    animator.setStartDelay(i * 100);
                                    animator.start();
                                }

                            }

                            @Override
                            public void onAnimationCancel(Animator animator) {

                            }

                            @Override
                            public void onAnimationRepeat(Animator animator) {

                            }
                        });
                        animator.start();
                    }
                    isReveal = true;
                }
            }
        });
        objAnimator.start();
    }

    public void setPointPath(PointPath point) {
        mButton.setTranslationX(point.mX);
        mButton.setTranslationY(point.mY);
    }


    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    private int sp2px(int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

}

package voice.yuekong.com.animationtest;

import android.animation.TypeEvaluator;

/**
 * Created by Zhongqi.Shao on 2016/12/22.
 */
public class MyEvalueTor implements TypeEvaluator<PointPath> {
    @Override
    public PointPath evaluate(float t, PointPath startPointPath, PointPath endPointPath) {
        float x = 0f;
        float y = 0f;
        float oneMinus = 1 - t;
        switch (endPointPath.mAction) {
            case PointPath.MOVE:
                x = endPointPath.mX;
                y = endPointPath.mY;
                break;
            case PointPath.LINE_TO:
                x = startPointPath.mX + (endPointPath.mX - startPointPath.mX) * t;
                y = startPointPath.mY + (endPointPath.mY - startPointPath.mY) * t;
                break;
            case PointPath.CUBIC_TO:
                x = oneMinus * oneMinus * oneMinus * startPointPath.mX +
                        3 * t * oneMinus * oneMinus * endPointPath.cubicX1 +
                        3 * t * t * oneMinus * endPointPath.cubicX2 +
                        endPointPath.mX * t * t * t;
                y = oneMinus * oneMinus * oneMinus * startPointPath.mY +
                        3 * t * oneMinus * oneMinus * endPointPath.cubicY1 +
                        3 * t * t * oneMinus * endPointPath.cubicY2 +
                        endPointPath.mY * t * t * t;
                break;
        }
        return PointPath.moveTo(x, y);
    }
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 Android定义对号,可以使用自定义 View 实现打钩动画功能。以下是实现方法: 首先,创建一个类来实现定义 View,这个类需要继承自 View,然后重写 onDraw 方法。在 onDraw 方法中,使用 Canvas 和 Path 对象来绘制对号的形状。 在绘制对号之前,需要先设置对号的起点和终点坐标,可以通过计算 View 的宽度和高度来确定这些坐标。然后,使用 Path 对象来创建对号的形状,具体方法如下: 1. 创建一个 Path 对象。 2. 使用 moveTo 方法将画笔移动到对号的起点。 3. 使用 lineTo 方法将画笔画出对号的一条线段。 4. 使用 moveTo 方法将画笔移动到对号的另一个起点。 5. 使用 lineTo 方法将画笔画出对号的另一条线段。 在绘制 Path 对象之后,可以使用 Paint 对象来设置对号的样式,例如颜色和宽度等。最后,在 onDraw 方法中调用 Canvas 的 drawPath 方法来将对号绘制出来。 另外,为了实现打钩动画,还需要使用 ValueAnimator 对象来控制 Path 的绘制过程。具体方法如下: 1. 创建一个 ValueAnimator 对象,并设置动画的起始值和结束值。 2. 在动画的监听器中,使用 ValueAnimator 的 getAnimatedValue 方法来获取当前动画的进度。 3. 根据当前进度,计算出对号的绘制进度,并使用 PathMeasure 对象来获取对应位置的 Path。 4. 在 onDraw 方法中,使用 Canvas 的 drawPath 方法来绘制当前的 Path。 最后,将自定义 View 添加到布局中即可实现定义对号的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值