首頁 > 軟體

Android貝塞爾曲線實現加入購物車拋物線動畫

2022-06-29 18:02:36

本文範例為大家分享了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) {

            }
        });
    }
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com