溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android利用二階貝塞爾曲線實(shí)現(xiàn)添加購物車動(dòng)畫詳解

發(fā)布時(shí)間:2020-08-30 11:58:16 來源:腳本之家 閱讀:154 作者:張欽 欄目:移動(dòng)開發(fā)

一、引入

  1. 其實(shí)之前一直以為像餓了么或者是美團(tuán)外賣那種把商品添加到購物車的動(dòng)畫會(huì)很難做,但是實(shí)際做起來好像并沒有想象中的那么難哈哈。
  2. 布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager
  3. 動(dòng)畫主要使用二階貝塞爾曲線與屬性動(dòng)畫
  4. 消息傳遞使用EventBus普通事件

Android利用二階貝塞爾曲線實(shí)現(xiàn)添加購物車動(dòng)畫詳解

二、大致思路

Android利用二階貝塞爾曲線實(shí)現(xiàn)添加購物車動(dòng)畫詳解

1、如圖所示主要有三個(gè)點(diǎn),起點(diǎn)、終點(diǎn)、以及貝塞爾曲線的控制點(diǎn)

2、起點(diǎn)即點(diǎn)擊的View的位置,一般來說用如下方式即可取得。startPosition[0]為x軸開始坐標(biāo),startPosition[1]為Y軸終點(diǎn)坐標(biāo),兩點(diǎn)可以看作對(duì)角線上面的兩個(gè)端點(diǎn)(左上角x坐標(biāo),右下角y坐標(biāo))

//貝塞爾起始數(shù)據(jù)點(diǎn)
int[] startPosition = new int[2];
view.getLocationOnScreen(startPosition);

3、終點(diǎn)即購物車籃子的位置,與起點(diǎn)類似

mShoppingCart.getLocationInWindow(endPosition);

4、控制點(diǎn),我選的控制點(diǎn)為上圖的C點(diǎn),即A點(diǎn)的y坐標(biāo),B點(diǎn)的X坐標(biāo)

controlPosition[0] = endPosition[0];
controlPosition[1] = startPosition[1];

5、需要注意的地方,我不清楚是不是因?yàn)槲业牟季值膯栴},獲取到的點(diǎn)擊的A點(diǎn)總是會(huì)有一個(gè)偏移,后來經(jīng)同事提醒,減去了TabLayout的坐標(biāo)的y軸坐標(biāo)即位置才可以。

// 起點(diǎn)
int[] startPosition;
// 終點(diǎn)
int[] endPosition = new int[2];
// 貝塞爾控制點(diǎn)
int[] controlPosition = new int[2];
// tablayout位置
int[] tablayoutPosition = new int[2];

startPosition = data.getStartPosition();
mShoppingCart.getLocationInWindow(endPosition);
mTabLayout.getLocationInWindow(tablayoutPosition);
// 處理起點(diǎn)y坐標(biāo)偏移的問題
startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight();
// 終點(diǎn)進(jìn)行一下居中處理
endPosition[0] = endPosition[0] + (mShoppingCart.getWidth() / 2);
controlPosition[0] = endPosition[0];
controlPosition[1] = startPosition[1];

6、通過Path的quadTo方法繪制貝塞爾曲線,使用PathMeasure獲取點(diǎn)的坐標(biāo)(借助ValueAnimator.ofFloat()配合getPosTan()來獲取坐標(biāo))

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是一個(gè)生成器
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、下面是用屬性動(dòng)畫給購物車籃子做了一個(gè)放大縮小的動(dòng)畫效果

// 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) {
 // 起點(diǎn)
 int[] startPosition;
 // 終點(diǎn)
 int[] endPosition = new int[2];
 // 貝塞爾控制點(diǎn)
 int[] controlPosition = new int[2];
 // 當(dāng)前位置
 float[] currentPosition = new float[2];
 // tablayout位置
 int[] tablayoutPosition = new int[2];

 startPosition = data.getStartPosition();
 mShoppingCart.getLocationInWindow(endPosition);
 mTabLayout.getLocationInWindow(tablayoutPosition);
 // 處理起點(diǎn)y坐標(biāo)偏移的問題
 startPosition[1] = startPosition[1] - tablayoutPosition[1] - mTabLayout.getHeight();
 // 終點(diǎn)進(jìn)行一下居中處理
 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是一個(gè)生成器
 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) {

 }

 //當(dāng)動(dòng)畫結(jié)束后:
 @Override
 public void onAnimationEnd(Animator animation) {
 goodsChange(data);
 }

 @Override
 public void onAnimationCancel(Animator animation) {

 }

 @Override
 public void onAnimationRepeat(Animator animation) {

 }
 });
}

四、大致寫下布局(同時(shí)也算留做備份)

Android利用二階貝塞爾曲線實(shí)現(xiàn)添加購物車動(dòng)畫詳解

<?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>

五、推薦資源

View的位置參數(shù)

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI