Android贝塞尔曲线实现加入购物车抛物线动画

2022-10-07,,,,

本文实例为大家分享了android贝塞尔曲线实现加入购物车抛物线动画的具体代码,供大家参考,具体内容如下

先上图看效果

步骤:

a.确定动画的起终点
b.在起终点之间使用二次贝塞尔曲线填充起终点之间的点的轨迹
c.设置属性动画,valueanimator插值器,获取中间点的坐标
d.将执行动画的控件的x、y坐标设为上面得到的中间点坐标
e.开启属性动画
f.当动画结束时的操作

获取控件在屏幕中的绝对坐标:

int[] parentlocation = new int[2];
mrlayout.getlocationinwindow(parentlocation);

计算开始坐标和结束坐标:

//开始掉落的商品的起始点:商品起始点-父布局起始点+该商品图片的一半
float startx = startloc[0] - parentlocation[0] + iv.getwidth() / 2;
float starty = startloc[1] - parentlocation[1] + iv.getheight() / 2;

//商品掉落后的终点坐标:购物车起始点-父布局起始点+购物车图片的1/5
float tox = endloc[0] - parentlocation[0] + mcart.getwidth() / 5;
float toy = endloc[1] - parentlocation[1];

贝塞尔曲线以及属性动画:

path path = new path();
        //移动到起始点(贝塞尔曲线的起点)
        path.moveto(startx, starty);
        //使用二次萨贝尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
        path.quadto((startx + tox) / 2, starty, tox, toy);
        //mpathmeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,
        // 如果是true,path会形成一个闭环
        mpathmeasure = new pathmeasure(path, false);

        //属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
        valueanimator valueanimator = valueanimator.offloat(0, mpathmeasure.getlength());
        valueanimator.setduration(1000);
        // 匀速线性插值器
        valueanimator.setinterpolator(new linearinterpolator());
        valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
            @override
            public void onanimationupdate(valueanimator animation) {
                // 当插值计算进行时,获取中间的每个值,
                // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
                float value = (float) animation.getanimatedvalue();
                // 获取当前点坐标封装到mcurrentposition
                // boolean getpostan(float distance, float[] pos, float[] tan) :
                // 传入一个距离distance(0<=distance<=getlength()),然后会计算当前距
                // 离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
                mpathmeasure.getpostan(value, mcurrentposition, null);//mcurrentposition此时就是中间距离点的坐标值
                // 移动的商品图片(动画图片)的坐标设置为该中间点的坐标
                goods.settranslationx(mcurrentposition[0]);
                goods.settranslationy(mcurrentposition[1]);
            }
        });
//      五、 开始执行动画
        valueanimator.start();

//      六、动画结束后的处理
        valueanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {

            }

            //当动画结束后:
            @override
            public void onanimationend(animator animation) {
                // 购物车的数量加1
                i++;
                mcount.settext(string.valueof(i));
                // 把移动的图片imageview从父布局里移除
                mrlayout.removeview(goods);
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });

xml里的写法:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    <relativelayout
            android:id="@+id/rl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <button
                android:id="@+id/add"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="加入购物车"/>
            <imageview
                android:layout_torightof="@id/add"
                android:id="@+id/goods"
                android:src="@mipmap/ic_launcher"
                android:layout_width="50dp"
                android:layout_height="50dp"
                />
            <textview
                android:id="@+id/count"
                android:layout_marginleft="300dp"
                android:layout_margintop="70dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="0"/>

            <imageview
                android:id="@+id/cart"
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:layout_marginleft="300dp"
                android:layout_margintop="240dp"
                android:src="@drawable/ic_shopping_cart"
                />
        </relativelayout>

</linearlayout>

使用了butterknife和自己封装的baseactivity,要使用的话需要自行修改代码。

完整代码:

import android.animation.animator;
import android.animation.valueanimator;
import android.graphics.path;
import android.graphics.pathmeasure;
import android.view.view;
import android.view.animation.linearinterpolator;
import android.widget.button;
import android.widget.imageview;
import android.widget.relativelayout;
import android.widget.textview;

import com.xp.baseapp.r;
import com.xp.baseapp.base.baseactivity;

import butterknife.bindview;
import butterknife.onclick;

public class shoppingcartanimationactivity extends baseactivity {

    @bindview(r.id.add)
    button madd;
    @bindview(r.id.rl)
    relativelayout mrlayout;
    @bindview(r.id.count)
    textview mcount;
    @bindview(r.id.cart)
    imageview mcart;
    @bindview(r.id.goods)
    imageview mgoods;
    private pathmeasure mpathmeasure;
    /**
     * 贝塞尔曲线中间过程的点的坐标
     */
    private float[] mcurrentposition = new float[2];
    /**
     * 购物车中的商品数量
     */
    private int i = 0;

    @override
    protected int getcontentviewid() {
        return r.layout.activity_shopping_cart_animation;
    }

    @override
    protected void init() {

    }

    @onclick(r.id.add)
    public void addgood(view v) {
        addcart(mgoods);
    }

    /**
     * 把商品添加到购物车的动画效果
     *
     * @param iv
     */
    private void addcart(imageview iv) {
//      一、创造出执行动画的主题---imageview
        //代码new一个imageview,图片资源是上面的imageview的图片
        // (这个图片就是执行动画的图片,从开始位置出发,经过一个抛物线(贝塞尔曲线),移动到购物车里)
        final imageview goods = new imageview(shoppingcartanimationactivity.this);
        goods.setimagedrawable(iv.getdrawable());
        relativelayout.layoutparams params = new relativelayout.layoutparams(100, 100);
        mrlayout.addview(goods, params);

//        二、计算动画开始/结束点的坐标的准备工作
        //得到父布局的起始点坐标(用于辅助计算动画开始/结束时的点的坐标)
        int[] parentlocation = new int[2];
        mrlayout.getlocationinwindow(parentlocation);

        //得到商品图片的坐标(用于计算动画开始的坐标)
        int startloc[] = new int[2];
        iv.getlocationinwindow(startloc);

        //得到购物车图片的坐标(用于计算动画结束后的坐标)
        int endloc[] = new int[2];
        mcart.getlocationinwindow(endloc);


//        三、正式开始计算动画开始/结束的坐标
        //开始掉落的商品的起始点:商品起始点-父布局起始点+该商品图片的一半
        float startx = startloc[0] - parentlocation[0] + iv.getwidth() / 2;
        float starty = startloc[1] - parentlocation[1] + iv.getheight() / 2;

        //商品掉落后的终点坐标:购物车起始点-父布局起始点+购物车图片的1/5
        float tox = endloc[0] - parentlocation[0] + mcart.getwidth() / 5;
        float toy = endloc[1] - parentlocation[1];

//        四、计算中间动画的插值坐标(贝塞尔曲线)(其实就是用贝塞尔曲线来完成起终点的过程)
        //开始绘制贝塞尔曲线
        path path = new path();
        //移动到起始点(贝塞尔曲线的起点)
        path.moveto(startx, starty);
        //使用二次萨贝尔曲线:注意第一个起始坐标越大,贝塞尔曲线的横向距离就会越大,一般按照下面的式子取即可
        path.quadto((startx + tox) / 2, starty, tox, toy);
        //mpathmeasure用来计算贝塞尔曲线的曲线长度和贝塞尔曲线中间插值的坐标,
        // 如果是true,path会形成一个闭环
        mpathmeasure = new pathmeasure(path, false);

        //属性动画实现(从0到贝塞尔曲线的长度之间进行插值计算,获取中间过程的距离值)
        valueanimator valueanimator = valueanimator.offloat(0, mpathmeasure.getlength());
        valueanimator.setduration(1000);
        // 匀速线性插值器
        valueanimator.setinterpolator(new linearinterpolator());
        valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
            @override
            public void onanimationupdate(valueanimator animation) {
                // 当插值计算进行时,获取中间的每个值,
                // 这里这个值是中间过程中的曲线长度(下面根据这个值来得出中间点的坐标值)
                float value = (float) animation.getanimatedvalue();
                // 获取当前点坐标封装到mcurrentposition
                // boolean getpostan(float distance, float[] pos, float[] tan) :
                // 传入一个距离distance(0<=distance<=getlength()),然后会计算当前距
                // 离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
                mpathmeasure.getpostan(value, mcurrentposition, null);//mcurrentposition此时就是中间距离点的坐标值
                // 移动的商品图片(动画图片)的坐标设置为该中间点的坐标
                goods.settranslationx(mcurrentposition[0]);
                goods.settranslationy(mcurrentposition[1]);
            }
        });
//      五、 开始执行动画
        valueanimator.start();

//      六、动画结束后的处理
        valueanimator.addlistener(new animator.animatorlistener() {
            @override
            public void onanimationstart(animator animation) {

            }

            //当动画结束后:
            @override
            public void onanimationend(animator animation) {
                // 购物车的数量加1
                i++;
                mcount.settext(string.valueof(i));
                // 把移动的图片imageview从父布局里移除
                mrlayout.removeview(goods);
            }

            @override
            public void onanimationcancel(animator animation) {

            }

            @override
            public void onanimationrepeat(animator animation) {

            }
        });
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

《Android贝塞尔曲线实现加入购物车抛物线动画.doc》

下载本文的Word格式文档,以方便收藏与打印。