Android利用二階貝塞爾曲線實現(xiàn)添加購物車動畫詳解
一、引入
- 其實之前一直以為像餓了么或者是美團外賣那種把商品添加到購物車的動畫會很難做,但是實際做起來好像并沒有想象中的那么難哈哈。
- 布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager
- 動畫主要使用二階貝塞爾曲線與屬性動畫
- 消息傳遞使用EventBus普通事件

二、大致思路

1、如圖所示主要有三個點,起點、終點、以及貝塞爾曲線的控制點
2、起點即點擊的View的位置,一般來說用如下方式即可取得。startPosition[0]為x軸開始坐標,startPosition[1]為Y軸終點坐標,兩點可以看作對角線上面的兩個端點(左上角x坐標,右下角y坐標)
//貝塞爾起始數(shù)據(jù)點 int[] startPosition = new int[2]; view.getLocationOnScreen(startPosition);
3、終點即購物車籃子的位置,與起點類似
mShoppingCart.getLocationInWindow(endPosition);
4、控制點,我選的控制點為上圖的C點,即A點的y坐標,B點的X坐標
controlPosition[0] = endPosition[0]; controlPosition[1] = startPosition[1];
5、需要注意的地方,我不清楚是不是因為我的布局的問題,獲取到的點擊的A點總是會有一個偏移,后來經(jīng)同事提醒,減去了TabLayout的坐標的y軸坐標即位置才可以。
// 起點 int[] startPosition; // 終點 int[] endPosition = new int[2]; // 貝塞爾控制點 int[] controlPosition = new int[2]; // tablayout位置 int[] tablayoutPosition = new int[2]; startPosition = data.getStartPosition(); mShoppingCart.getLocationInWindow(endPosition); mTabLayout.getLocationInWindow(tablayoutPosition); // 處理起點y坐標偏移的問題 startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight(); // 終點進行一下居中處理 endPosition[0] = endPosition[0] + (mShoppingCart.getWidth() / 2); controlPosition[0] = endPosition[0]; controlPosition[1] = startPosition[1];
6、通過Path的quadTo方法繪制貝塞爾曲線,使用PathMeasure獲取點的坐標(借助ValueAnimator.ofFloat()配合getPosTan()來獲取坐標)
Path path = new Path();
path.moveTo(startPosition[0], startPosition[1]);
path.quadTo(controlPosition[0], controlPosition[1], endPosition[0], endPosition[1]);
PathMeasure pathMeasure = new PathMeasure();
// false表示path路徑不閉合
pathMeasure.setPath(path, false);
// ofFloat是一個生成器
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());
// 勻速線性插值器
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setDuration(800);
valueAnimator.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
pathMeasure.getPosTan(value, currentPosition, null);
imageView.setX(currentPosition[0]);
imageView.setY(currentPosition[1]);
});
valueAnimator.start();
7、下面是用屬性動畫給購物車籃子做了一個放大縮小的動畫效果
// mShoppingCart是View ObjectAnimator shoppingCartX = ObjectAnimator.ofFloat(mShoppingCart, "scaleX", 1.0f, 1.3f, 1.0f); ObjectAnimator shoppingCartY = ObjectAnimator.ofFloat(mShoppingCart, "scaleY", 1.0f, 1.3f, 1.0f); shoppingCartX.setInterpolator(new AccelerateInterpolator()); shoppingCartY.setInterpolator(new AccelerateInterpolator()); AnimatorSet shoppingCart = new AnimatorSet(); shoppingCart .play(shoppingCartX) .with(shoppingCartY); shoppingCart.setDuration(800); shoppingCart.start();
三、稍完整的大部分代碼
private void AddAnimation(AddEventBean data) {
// 起點
int[] startPosition;
// 終點
int[] endPosition = new int[2];
// 貝塞爾控制點
int[] controlPosition = new int[2];
// 當前位置
float[] currentPosition = new float[2];
// tablayout位置
int[] tablayoutPosition = new int[2];
startPosition = data.getStartPosition();
mShoppingCart.getLocationInWindow(endPosition);
mTabLayout.getLocationInWindow(tablayoutPosition);
// 處理起點y坐標偏移的問題
startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight();
// 終點進行一下居中處理
endPosition[0] = endPosition[0] + (mShoppingCart.getWidth() / 2);
controlPosition[0] = endPosition[0];
controlPosition[1] = startPosition[1];
final ImageView imageView = new ImageView(this);
mConView.addView(imageView);
imageView.setImageResource(R.drawable.specialadd);
imageView.getLayoutParams().width = getResources().getDimensionPixelSize(R.dimen.dp_px_30);
imageView.getLayoutParams().height = getResources().getDimensionPixelSize(R.dimen.dp_px_30);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setVisibility(View.VISIBLE);
imageView.setX(startPosition[0]);
imageView.setY(startPosition[1]);
Path path = new Path();
path.moveTo(startPosition[0], startPosition[1]);
path.quadTo(controlPosition[0], controlPosition[1], endPosition[0], endPosition[1]);
PathMeasure pathMeasure = new PathMeasure();
// false表示path路徑不閉合
pathMeasure.setPath(path, false);
// ofFloat是一個生成器
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());
// 勻速線性插值器
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setDuration(800);
valueAnimator.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
pathMeasure.getPosTan(value, currentPosition, null);
imageView.setX(currentPosition[0]);
imageView.setY(currentPosition[1]);
});
valueAnimator.start();
ObjectAnimator shoppingCartX = ObjectAnimator.ofFloat(mShoppingCart, "scaleX", 1.0f, 1.3f, 1.0f);
ObjectAnimator shoppingCartY = ObjectAnimator.ofFloat(mShoppingCart, "scaleY", 1.0f, 1.3f, 1.0f);
shoppingCartX.setInterpolator(new AccelerateInterpolator());
shoppingCartY.setInterpolator(new AccelerateInterpolator());
AnimatorSet shoppingCart = new AnimatorSet();
shoppingCart
.play(shoppingCartX)
.with(shoppingCartY);
shoppingCart.setDuration(800);
shoppingCart.start();
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
//當動畫結(jié)束后:
@Override
public void onAnimationEnd(Animator animation) {
goodsChange(data);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
四、大致寫下布局(同時也算留做備份)

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" ... ...> <RelativeLayout ... ...> 頂部常駐的toolbar </RelativeLayout> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <android.support.design.widget.AppBarLayout ... ...> <android.support.design.widget.CollapsingToolbarLayout ... ... app:layout_scrollFlags="scroll|exitUntilCollapsed"> <LinearLayout ... ...> TabLayout上面的View </LinearLayout> </android.support.design.widget.CollapsingToolbarLayout> <android.support.design.widget.TabLayout ... ... /> </android.support.design.widget.AppBarLayout> <RelativeLayout ... ... android:fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> </android.support.design.widget.CoordinatorLayout> <LinearLayout ... ...> 最下面的購物車一欄 </LinearLayout> </LinearLayout>
五、推薦資源
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
- Android把商品添加到購物車的動畫效果(貝塞爾曲線)
- Android實現(xiàn)代碼畫虛線邊框背景效果
- Android編程實現(xiàn)ImageView圖片拋物線動畫效果的方法
- Android實現(xiàn)在map上畫出路線的方法
- Android App中使用SurfaceView制作多線程動畫的實例講解
- Android仿天貓商品拋物線加入購物車動畫
- Android補間動畫基本使用(位移、縮放、旋轉(zhuǎn)、透明)
- Android動畫之漸變動畫(Tween Animation)詳解 (漸變、縮放、位移、旋轉(zhuǎn))
- Android開發(fā)之圖形圖像與動畫(二)Animation實現(xiàn)圖像的漸變/縮放/位移/旋轉(zhuǎn)
- Android利用Canvas標點畫線并加入位移動畫(1)
相關文章
Android程序報錯程序包org.apache.http不存在問題的解決方法
這篇文章主要介紹了Android程序報錯"程序包org.apache.http不存在——Android 6.0已經(jīng)不支持HttpClient" 問題的解決方法,感興趣的小伙伴們可以參考一下2016-06-06
Android自定義ViewGroup實現(xiàn)彈性滑動效果
這篇文章主要為大家詳細介紹了Android自定義ViewGroup實現(xiàn)彈性滑動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
android獲取及監(jiān)聽手機網(wǎng)絡狀態(tài)
大家好,本篇文章主要講的是android獲取及監(jiān)聽手機網(wǎng)絡狀態(tài),感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01
詳解Android的內(nèi)存優(yōu)化--LruCache
LruCache是基于Lru算法實現(xiàn)的一種緩存機制。本文對LruCache的概念和實現(xiàn)原理進行介紹,通過實例分析和使用介紹,讓大家更好的了解LruCache,下面跟著小編一起來看下吧2016-12-12
Android自定義View設定到FrameLayout布局中實現(xiàn)多組件顯示的方法 分享
Android自定義View設定到FrameLayout布局中實現(xiàn)多組件顯示的方法 分享,需要的朋友可以參考一下2013-05-05
Android實現(xiàn)類似360,QQ管家那樣的懸浮窗
用到的就是WindowManager以及WindowManager.LayoutParams,對這個LayoutParams做文章,當設置為屬性后,然后,創(chuàng)建一個View,將這個View添加到WindowManager中就行2013-06-06

