美文网首页自定义控件
Android实现购物车动画

Android实现购物车动画

作者: 喵先生CN | 来源:发表于2018-07-28 13:26 被阅读0次

需求:点击触发事件,使一个圆点或者其他的什么东西,移动到另外一个元素上。由于是使用坐标进行移动,所以,会出现起始点位置,中途的路径,落点位置的点不是中心点,而是做小X轴和Y轴焦点。当然这个案例会考虑这个问题,并解决这个问题。

示例图

Android实现购物车动画示例图

activity_main.xml代码

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" android:id="@+id/activity_main">


    <Button android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试" android:id="@+id/btn"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="8dp"
            android:layout_marginRight="8dp"
            app:layout_constraintRight_toRightOf="parent"/>
    <Button android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="盒子"
            android:id="@+id/box"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="8dp"
            app:layout_constraintLeft_toLeftOf="parent"/>
</android.support.constraint.ConstraintLayout>

MainActivity代码

这个方法就没啥说的了.初始化调用,以及动画效果在这里定义.

package cn.meaoo.myapplication;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
/**
 * 物体抛物线移动
 * Created by meaoo on 2017/4/14.
 */
public class MainActivity extends AppCompatActivity {
    private View startView, toView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_dot);
        //起始点元素
        startView = findViewById(R.id.btn);
        //结束点元素
        toView = findViewById(R.id.box);

        startView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                clickK();
            }
        });
    }

    private void clickK() {
        FlyView fly = new FlyView(this, startView, toView, R.drawable.ic_action_name);
        fly.startFly(3000, new Runnable() {
            @Override
            public void run() {
                //结束点元素动画变化
                toView.animate().scaleX(1.5f);
                toView.animate().scaleY(1.3f);
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        toView.animate().scaleX(1f);
                        toView.animate().scaleY(1f);
                    }
                }, 200);
            }
        });
    }
}

FlyView代码

使物体进行点与点之间移动方法,或者叫两点之间飞行,并使用贝塞尔曲线,进行一个比较漂亮的飞行姿态.并处理动画效果

package cn.meaoo.myapplication;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;

/**
 * Created by meaoo on 2017/4/14.
 */

public class FlyView {
    private View fromView, toView;

    private ImageView flyView;
    private ViewGroup container;
    private Bitmap bitmap;


    //贝塞尔曲线中间过程的点的坐标
    final float[] mCurrentPosition = new float[2];
    //计算中间动画的插值坐标
    Path mPath = new Path();
    PathMeasure mPathMeasure = new PathMeasure();
    int actionBarHeight = 0;

    public FlyView(AppCompatActivity context, View from, View to, int flyImageResourceId) {
        fromView = from;
        toView = to;
        container = (ViewGroup) ((ViewGroup) context.findViewById(android.R.id.content)).getChildAt(0);
        flyView = new ImageView(context);
        flyView.setLayoutParams(new LayoutParams(100, 100));
        // flyView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        flyView.setImageResource(flyImageResourceId);

        //由于imageView无法获取宽度和高度,这里使用bitmap获取
        bitmap = BitmapFactory.decodeResource(flyView.getResources(), flyImageResourceId);

        ActionBar bar = context.getSupportActionBar();
        if (bar != null) {
            actionBarHeight = bar.getHeight();
        }
    }


    /**
     * 启动飞行
     *
     * @param duration
     * @param runnable
     */
    public void startFly(long duration, @Nullable final Runnable runnable) {
        //添加移动飞行物体到页面
        container.addView(flyView);
        //起始点坐标
        //计算起始点中心点
        final float startX = CommonUtil.getContentX(fromView) + fromView.getWidth() / 2;
        final float startY = CommonUtil.getContentY(fromView) + fromView.getHeight() / 2;

        //设置移动点起始坐标
        if (bitmap != null) {
            CommonUtil.setContentX(flyView, startX - bitmap.getWidth() / 2);
            CommonUtil.setContentY(flyView, startY - bitmap.getHeight() / 2);
        } else {
            CommonUtil.setContentX(flyView, startX);
            CommonUtil.setContentY(flyView, startY);
        }

        final float flyX = CommonUtil.getContentX(flyView);
        final float flyY = CommonUtil.getContentY(flyView);

        //结束点坐标
        //结束点坐标的Y轴不进行居中计算原因是为了实现当物体移动到结束点的时候,即刻消失的效果,
        //而不是当物体移动到结束点中心再小时
        final float toX = CommonUtil.getContentX(toView) + toView.getWidth() / 2;
        final float toY = CommonUtil.getContentY(toView);


        //贝塞尔曲线起始点
        mPath.moveTo(flyX, flyY);
        //使用二次贝塞尔曲线
        //第一个参数,值越大,横向移动距离越大
        mPath.quadTo((startX + toX) / 2, startY, toX, toY);

        //计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标
        //第二个参数如果是true,Path将会形成一个闭环
        mPathMeasure.setPath(mPath, false);

        //从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值
        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.getLength());
        //动画执行时长,毫秒单位
        valueAnimator.setDuration(duration);

        //匀速插值器
        LinearInterpolator linearInterpolator = new LinearInterpolator();
        valueAnimator.setInterpolator(linearInterpolator);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                //当插值计算进行时,获取中间的每个值
                //这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
                float value = (Float) valueAnimator.getAnimatedValue();
                // 获取当前点坐标封装到mCurrentPosition
                //传入一个距离distance(0<=distance<=getLength()),然后会计算当前距离的坐标点和切线,pos会自动填充上坐标,这个方法很重要
                mPathMeasure.getPosTan(value, mCurrentPosition, null);
                //移动(动画图片)的坐标设置为该中间点的坐标
                flyView.setTranslationX(mCurrentPosition[0] - flyView.getWidth() / 2);
                flyView.setTranslationY(mCurrentPosition[1]);

            }
        });
        //动画监听
        valueAnimator.addListener(new Animator.AnimatorListener() {
            //动画启动
            @Override
            public void onAnimationStart(Animator animator) {
            }

            //动画结束
            @Override
            public void onAnimationEnd(Animator animator) {
                if (runnable != null) {
                    runnable.run();
                }
                container.removeView(flyView);
            }

            //动画取消
            @Override
            public void onAnimationCancel(Animator animator) {
                container.removeView(flyView);
            }

            //动画重复执行
            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });

        //执行动画
        valueAnimator.start();
    }
}

CommonUtil代码

由于系统提供方法无法准确获取元素准确坐标.所以使用该方法辅助FlyView计算开始点和结束点以及移动物体在屏幕中的真实坐标

package cn.meaoo.myapplication;

import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;

/**
 * Created by meaoo on 2017/4/14.
 */

public class CommonUtil {

    /**
     * 获取view在全局中的X轴坐标点
     *
     * @param view
     * @return
     */
    public static float getContentX(View view) {
        int x = 0;
        Activity context = (Activity) view.getContext();
        View content = ((ViewGroup) context.findViewById(android.R.id.content)).getChildAt(0);
        x += view.getX();
        ViewGroup parent = (ViewGroup) view.getParent();

        while (parent != content) {
            x += parent.getX();
            parent = (ViewGroup) parent.getParent();
        }
        return x;
    }

    /**
     * 获取view在全局中的Y轴坐标点
     *
     * @param view
     * @return
     */
    public static float getContentY(View view) {
        int y = 0;
        Activity context = (Activity) view.getContext();
        View content = ((ViewGroup) context.findViewById(android.R.id.content)).getChildAt(0);
        y += view.getY();
        ViewGroup parent = (ViewGroup) view.getParent();

        while (parent != content) {
            y += parent.getY();
            parent = (ViewGroup) parent.getParent();
        }
        return y;
    }

    /**
     * 设置view在全局中的X轴坐标点
     *
     * @param view
     * @param x
     */
    public static void setContentX(View view, float x) {
        Activity context = (Activity) view.getContext();
        View content = ((ViewGroup) context.findViewById(android.R.id.content)).getChildAt(0);
        ViewGroup parent = (ViewGroup) view.getParent();
        while (parent != content) {
            x -= parent.getX();
            parent = (ViewGroup) parent.getParent();
        }
        view.setX(x);
    }

    /**
     * 设置view在全局中的Y轴坐标点
     *
     * @param view
     * @param y
     */
    public static void setContentY(View view, float y) {
        Activity context = (Activity) view.getContext();
        View content = ((ViewGroup) context.findViewById(android.R.id.content)).getChildAt(0);
        ViewGroup parent = (ViewGroup) view.getParent();
        while (parent != content) {
            y -= parent.getY();
            parent = (ViewGroup) parent.getParent();
        }
        view.setY(y);
    }

}

结尾

如果对上面的内容有任何疑问或者问题,无法得到解决,请与我联系

//发送邮件或者添加QQ,请说明什么问题,以及文章链接,这样方便对您的问题进行更详尽的回答
E-mail:m@meaoo.cn
QQ : 774540069

源码下载

GitHub
码云

相关文章

网友评论

    本文标题:Android实现购物车动画

    本文链接:https://www.haomeiwen.com/subject/mjdomftx.html