Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫
本文實(shí)例為大家分享了Android貝塞爾曲線實(shí)現(xiàn)加入購(gòu)物車拋物線動(dòng)畫的具體代碼,供大家參考,具體內(nèi)容如下
先上圖看效果
步驟:
a.確定動(dòng)畫的起終點(diǎn)
b.在起終點(diǎn)之間使用二次貝塞爾曲線填充起終點(diǎn)之間的點(diǎn)的軌跡
c.設(shè)置屬性動(dòng)畫,ValueAnimator插值器,獲取中間點(diǎn)的坐標(biāo)
d.將執(zhí)行動(dòng)畫的控件的x、y坐標(biāo)設(shè)為上面得到的中間點(diǎn)坐標(biāo)
e.開(kāi)啟屬性動(dòng)畫
f.當(dāng)動(dòng)畫結(jié)束時(shí)的操作
獲取控件在屏幕中的絕對(duì)坐標(biāo):
int[] parentLocation = new int[2]; mRLayout.getLocationInWindow(parentLocation);
計(jì)算開(kāi)始坐標(biāo)和結(jié)束坐標(biāo):
//開(kāi)始掉落的商品的起始點(diǎn):商品起始點(diǎn)-父布局起始點(diǎn)+該商品圖片的一半 float startX = startLoc[0] - parentLocation[0] + iv.getWidth() / 2; float startY = startLoc[1] - parentLocation[1] + iv.getHeight() / 2; //商品掉落后的終點(diǎn)坐標(biāo):購(gòu)物車起始點(diǎn)-父布局起始點(diǎn)+購(gòu)物車圖片的1/5 float toX = endLoc[0] - parentLocation[0] + mCart.getWidth() / 5; float toY = endLoc[1] - parentLocation[1];
貝塞爾曲線以及屬性動(dòng)畫:
Path path = new Path(); ? ? ? ? //移動(dòng)到起始點(diǎn)(貝塞爾曲線的起點(diǎn)) ? ? ? ? path.moveTo(startX, startY); ? ? ? ? //使用二次薩貝爾曲線:注意第一個(gè)起始坐標(biāo)越大,貝塞爾曲線的橫向距離就會(huì)越大,一般按照下面的式子取即可 ? ? ? ? path.quadTo((startX + toX) / 2, startY, toX, toY); ? ? ? ? //mPathMeasure用來(lái)計(jì)算貝塞爾曲線的曲線長(zhǎng)度和貝塞爾曲線中間插值的坐標(biāo), ? ? ? ? // 如果是true,path會(huì)形成一個(gè)閉環(huán) ? ? ? ? mPathMeasure = new PathMeasure(path, false); ? ? ? ? //屬性動(dòng)畫實(shí)現(xiàn)(從0到貝塞爾曲線的長(zhǎng)度之間進(jìn)行插值計(jì)算,獲取中間過(guò)程的距離值) ? ? ? ? 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) { ? ? ? ? ? ? ? ? // 當(dāng)插值計(jì)算進(jìn)行時(shí),獲取中間的每個(gè)值, ? ? ? ? ? ? ? ? // 這里這個(gè)值是中間過(guò)程中的曲線長(zhǎng)度(下面根據(jù)這個(gè)值來(lái)得出中間點(diǎn)的坐標(biāo)值) ? ? ? ? ? ? ? ? float value = (Float) animation.getAnimatedValue(); ? ? ? ? ? ? ? ? // 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition ? ? ? ? ? ? ? ? // boolean getPosTan(float distance, float[] pos, float[] tan) : ? ? ? ? ? ? ? ? // 傳入一個(gè)距離distance(0<=distance<=getLength()),然后會(huì)計(jì)算當(dāng)前距 ? ? ? ? ? ? ? ? // 離的坐標(biāo)點(diǎn)和切線,pos會(huì)自動(dòng)填充上坐標(biāo),這個(gè)方法很重要。 ? ? ? ? ? ? ? ? mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此時(shí)就是中間距離點(diǎn)的坐標(biāo)值 ? ? ? ? ? ? ? ? // 移動(dòng)的商品圖片(動(dòng)畫圖片)的坐標(biāo)設(shè)置為該中間點(diǎn)的坐標(biāo) ? ? ? ? ? ? ? ? goods.setTranslationX(mCurrentPosition[0]); ? ? ? ? ? ? ? ? goods.setTranslationY(mCurrentPosition[1]); ? ? ? ? ? ? } ? ? ? ? }); // ? ? ?五、 開(kāi)始執(zhí)行動(dòng)畫 ? ? ? ? valueAnimator.start(); // ? ? ?六、動(dòng)畫結(jié)束后的處理 ? ? ? ? valueAnimator.addListener(new Animator.AnimatorListener() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onAnimationStart(Animator animation) { ? ? ? ? ? ? } ? ? ? ? ? ? //當(dāng)動(dòng)畫結(jié)束后: ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onAnimationEnd(Animator animation) { ? ? ? ? ? ? ? ? // 購(gòu)物車的數(shù)量加1 ? ? ? ? ? ? ? ? i++; ? ? ? ? ? ? ? ? mCount.setText(String.valueOf(i)); ? ? ? ? ? ? ? ? // 把移動(dòng)的圖片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="加入購(gòu)物車"/> ? ? ? ? ? ? <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; ? ? /** ? ? ?* 貝塞爾曲線中間過(guò)程的點(diǎn)的坐標(biāo) ? ? ?*/ ? ? private float[] mCurrentPosition = new float[2]; ? ? /** ? ? ?* 購(gòu)物車中的商品數(shù)量 ? ? ?*/ ? ? 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); ? ? } ? ? /** ? ? ?* 把商品添加到購(gòu)物車的動(dòng)畫效果 ? ? ?* ? ? ?* @param iv ? ? ?*/ ? ? private void addCart(ImageView iv) { // ? ? ?一、創(chuàng)造出執(zhí)行動(dòng)畫的主題---imageview ? ? ? ? //代碼new一個(gè)imageview,圖片資源是上面的imageview的圖片 ? ? ? ? // (這個(gè)圖片就是執(zhí)行動(dòng)畫的圖片,從開(kāi)始位置出發(fā),經(jīng)過(guò)一個(gè)拋物線(貝塞爾曲線),移動(dòng)到購(gòu)物車?yán)? ? ? ? ? final ImageView goods = new ImageView(ShoppingCartAnimationActivity.this); ? ? ? ? goods.setImageDrawable(iv.getDrawable()); ? ? ? ? RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100); ? ? ? ? mRLayout.addView(goods, params); // ? ? ? ?二、計(jì)算動(dòng)畫開(kāi)始/結(jié)束點(diǎn)的坐標(biāo)的準(zhǔn)備工作 ? ? ? ? //得到父布局的起始點(diǎn)坐標(biāo)(用于輔助計(jì)算動(dòng)畫開(kāi)始/結(jié)束時(shí)的點(diǎn)的坐標(biāo)) ? ? ? ? int[] parentLocation = new int[2]; ? ? ? ? mRLayout.getLocationInWindow(parentLocation); ? ? ? ? //得到商品圖片的坐標(biāo)(用于計(jì)算動(dòng)畫開(kāi)始的坐標(biāo)) ? ? ? ? int startLoc[] = new int[2]; ? ? ? ? iv.getLocationInWindow(startLoc); ? ? ? ? //得到購(gòu)物車圖片的坐標(biāo)(用于計(jì)算動(dòng)畫結(jié)束后的坐標(biāo)) ? ? ? ? int endLoc[] = new int[2]; ? ? ? ? mCart.getLocationInWindow(endLoc); // ? ? ? ?三、正式開(kāi)始計(jì)算動(dòng)畫開(kāi)始/結(jié)束的坐標(biāo) ? ? ? ? //開(kāi)始掉落的商品的起始點(diǎn):商品起始點(diǎn)-父布局起始點(diǎn)+該商品圖片的一半 ? ? ? ? float startX = startLoc[0] - parentLocation[0] + iv.getWidth() / 2; ? ? ? ? float startY = startLoc[1] - parentLocation[1] + iv.getHeight() / 2; ? ? ? ? //商品掉落后的終點(diǎn)坐標(biāo):購(gòu)物車起始點(diǎn)-父布局起始點(diǎn)+購(gòu)物車圖片的1/5 ? ? ? ? float toX = endLoc[0] - parentLocation[0] + mCart.getWidth() / 5; ? ? ? ? float toY = endLoc[1] - parentLocation[1]; // ? ? ? ?四、計(jì)算中間動(dòng)畫的插值坐標(biāo)(貝塞爾曲線)(其實(shí)就是用貝塞爾曲線來(lái)完成起終點(diǎn)的過(guò)程) ? ? ? ? //開(kāi)始繪制貝塞爾曲線 ? ? ? ? Path path = new Path(); ? ? ? ? //移動(dòng)到起始點(diǎn)(貝塞爾曲線的起點(diǎn)) ? ? ? ? path.moveTo(startX, startY); ? ? ? ? //使用二次薩貝爾曲線:注意第一個(gè)起始坐標(biāo)越大,貝塞爾曲線的橫向距離就會(huì)越大,一般按照下面的式子取即可 ? ? ? ? path.quadTo((startX + toX) / 2, startY, toX, toY); ? ? ? ? //mPathMeasure用來(lái)計(jì)算貝塞爾曲線的曲線長(zhǎng)度和貝塞爾曲線中間插值的坐標(biāo), ? ? ? ? // 如果是true,path會(huì)形成一個(gè)閉環(huán) ? ? ? ? mPathMeasure = new PathMeasure(path, false); ? ? ? ? //屬性動(dòng)畫實(shí)現(xiàn)(從0到貝塞爾曲線的長(zhǎng)度之間進(jìn)行插值計(jì)算,獲取中間過(guò)程的距離值) ? ? ? ? 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) { ? ? ? ? ? ? ? ? // 當(dāng)插值計(jì)算進(jìn)行時(shí),獲取中間的每個(gè)值, ? ? ? ? ? ? ? ? // 這里這個(gè)值是中間過(guò)程中的曲線長(zhǎng)度(下面根據(jù)這個(gè)值來(lái)得出中間點(diǎn)的坐標(biāo)值) ? ? ? ? ? ? ? ? float value = (Float) animation.getAnimatedValue(); ? ? ? ? ? ? ? ? // 獲取當(dāng)前點(diǎn)坐標(biāo)封裝到mCurrentPosition ? ? ? ? ? ? ? ? // boolean getPosTan(float distance, float[] pos, float[] tan) : ? ? ? ? ? ? ? ? // 傳入一個(gè)距離distance(0<=distance<=getLength()),然后會(huì)計(jì)算當(dāng)前距 ? ? ? ? ? ? ? ? // 離的坐標(biāo)點(diǎn)和切線,pos會(huì)自動(dòng)填充上坐標(biāo),這個(gè)方法很重要。 ? ? ? ? ? ? ? ? mPathMeasure.getPosTan(value, mCurrentPosition, null);//mCurrentPosition此時(shí)就是中間距離點(diǎn)的坐標(biāo)值 ? ? ? ? ? ? ? ? // 移動(dòng)的商品圖片(動(dòng)畫圖片)的坐標(biāo)設(shè)置為該中間點(diǎn)的坐標(biāo) ? ? ? ? ? ? ? ? goods.setTranslationX(mCurrentPosition[0]); ? ? ? ? ? ? ? ? goods.setTranslationY(mCurrentPosition[1]); ? ? ? ? ? ? } ? ? ? ? }); // ? ? ?五、 開(kāi)始執(zhí)行動(dòng)畫 ? ? ? ? valueAnimator.start(); // ? ? ?六、動(dòng)畫結(jié)束后的處理 ? ? ? ? valueAnimator.addListener(new Animator.AnimatorListener() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onAnimationStart(Animator animation) { ? ? ? ? ? ? } ? ? ? ? ? ? //當(dāng)動(dòng)畫結(jié)束后: ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onAnimationEnd(Animator animation) { ? ? ? ? ? ? ? ? // 購(gòu)物車的數(shù)量加1 ? ? ? ? ? ? ? ? i++; ? ? ? ? ? ? ? ? mCount.setText(String.valueOf(i)); ? ? ? ? ? ? ? ? // 把移動(dòng)的圖片imageView從父布局里移除 ? ? ? ? ? ? ? ? mRLayout.removeView(goods); ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onAnimationCancel(Animator animation) { ? ? ? ? ? ? } ? ? ? ? ? ? @Override ? ? ? ? ? ? public void onAnimationRepeat(Animator animation) { ? ? ? ? ? ? } ? ? ? ? }); ? ? } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android防止點(diǎn)擊過(guò)快造成多次響應(yīng)事件的解決方法
btn點(diǎn)擊用戶可能只點(diǎn)擊了一次但是后臺(tái)響應(yīng)了多次,像一些表單的提交出現(xiàn)這種問(wèn)題比較棘手,本篇文章主要介紹Android防止點(diǎn)擊過(guò)快造成多次響應(yīng)事件的解決方法,有興趣的可以了解一下。2016-12-12Android判斷用戶是否允許了攝像頭權(quán)限實(shí)例代碼
本篇文章主要介紹了Android判斷用戶是否允許了攝像頭權(quán)限實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Android 如何定制vibrator的各種震動(dòng)模式M 具體方法
這篇文章介紹了Android 如何定制vibrator的各種震動(dòng)模式M 具體方法,有需要的朋友可以參考一下2013-09-09Android 日期和時(shí)間的使用實(shí)例詳解
這篇文章主要介紹了Android 日期和時(shí)間的使用實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12Android 7.0中拍照和圖片裁剪適配的問(wèn)題詳解
這篇文章主要介紹了Android 7.0中拍照和圖片裁剪適配的相關(guān)問(wèn)題,文中通過(guò)示例代碼介紹的很詳細(xì),對(duì)大家具有一定的參考價(jià)值,有需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-02-02android開(kāi)發(fā)教程之listview顯示sqlite數(shù)據(jù)
這篇文章主要介紹了android使用listview顯示sqlite數(shù)據(jù)的方法,需要的朋友可以參考下2014-03-03