天天看點

Android把商品添加到購物車的動畫效果(貝塞爾曲線)

當我們寫商城類的項目的時候,一般都會有加入購物車的功能,加入購物車的時候會有一些抛物線動畫,具體代碼如下:

實作效果如圖:

Android把商品添加到購物車的動畫效果(貝塞爾曲線)

思路:

  1. 确定動畫的起終點
  2. 在起終點之間使用二次貝塞爾曲線填充起終點之間的點的軌迹
  3. 設定屬性動畫,ValueAnimator插值器,擷取中間點的坐标
  4. 将執行動畫的控件的x、y坐标設為上面得到的中間點坐标
  5. 開啟屬性動畫
  6. 當動畫結束時的操作

難點:

PathMeasure的使用 

- getLength() 

- boolean getPosTan(float distance, float[] pos, float[] tan) 的了解

涉及到的知識點:

如何擷取控件在螢幕中的絕對坐标

?

1 2 3

//得到父布局的起始點坐标(用于輔助計算動畫開始/結束時的點的坐标)

int

[] parentLocation =

new

int

[

2

];

rl.getLocationInWindow(parentLocation);

如何使用貝塞爾曲線以及屬性動畫插值器ValueAnimator

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

//    四、計算中間動畫的插值坐标(貝塞爾曲線)(其實就是用貝塞爾曲線來完成起終點的過程)

//開始繪制貝塞爾曲線

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(

, 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[

]);

goods.setTranslationY(mCurrentPosition[

1

]);

}

});

//   五、 開始執行動畫

valueAnimator.start();

所有代碼:

?

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

package

cn.c.com.beziercurveanimater;

import

android.animation.Animator;

import

android.animation.ValueAnimator;

import

android.graphics.Bitmap;

import

android.graphics.BitmapFactory;

import

android.graphics.Path;

import

android.graphics.PathMeasure;

import

android.os.Bundle;

import

android.support.v7.app.AppCompatActivity;

import

android.support.v7.widget.LinearLayoutManager;

import

android.support.v7.widget.RecyclerView;

import

android.view.LayoutInflater;

import

android.view.View;

import

android.view.ViewGroup;

import

android.view.animation.LinearInterpolator;

import

android.widget.ImageView;

import

android.widget.RelativeLayout;

import

android.widget.TextView;

import

java.util.ArrayList;

public

class

MainActivity

extends

AppCompatActivity {

private

RecyclerView mRecyclerView;

private

ImageView cart;

private

ArrayList<Bitmap> bitmapList =

new

ArrayList<>();

private

RelativeLayout rl;

private

PathMeasure mPathMeasure;

private

float

[] mCurrentPosition =

new

float

[

2

];

private

TextView count;

private

int

i =

;

@Override

protected

void

onCreate(Bundle savedInstanceState) {

super

.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initView();

initImg();

MyAdapter myAdapter =

new

MyAdapter(bitmapList);

mRecyclerView.setAdapter(myAdapter);

mRecyclerView.setLayoutManager(

new

LinearLayoutManager(MainActivity.

this

));

}

private

void

initImg() {

bitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.coin));

bitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.coin1));

bitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.coin91));

}

private

void

initView() {

mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);

cart = (ImageView) findViewById(R.id.cart);

rl = (RelativeLayout) findViewById(R.id.rl);

count = (TextView) findViewById(R.id.count);

}

class

MyAdapter

extends

RecyclerView.Adapter<MyVH> {

private

ArrayList<Bitmap> bitmapList;

public

MyAdapter(ArrayList<Bitmap> bitmapList) {

this

.bitmapList = bitmapList;

}

@Override

public

MyVH onCreateViewHolder(ViewGroup parent,

int

viewType) {

LayoutInflater inflater = LayoutInflater.from(MainActivity.

this

);

View itemView = inflater.inflate(R.layout.item, parent,

false

);

MyVH myVH =

new

MyVH(itemView);

return

myVH;

}

@Override

public

void

onBindViewHolder(

final

MyVH holder,

final

int

position) {

holder.iv.setImageBitmap(bitmapList.get(position));

holder.buy.setOnClickListener(

new

View.OnClickListener() {

@Override

public

void

onClick(View v) {

addCart(holder.iv);

}

});

}

@Override

public

int

getItemCount() {

return

bitmapList.size();

}

}

private

void

addCart( ImageView iv) {

//   一、創造出執行動畫的主題---imageview

//代碼new一個imageview,圖檔資源是上面的imageview的圖檔

// (這個圖檔就是執行動畫的圖檔,從開始位置出發,經過一個抛物線(貝塞爾曲線),移動到購物車裡)

final

ImageView goods =

new

ImageView(MainActivity.

this

);

goods.setImageDrawable(iv.getDrawable());

RelativeLayout.LayoutParams params =

new

RelativeLayout.LayoutParams(

100

,

100

);

rl.addView(goods, params);

//    二、計算動畫開始/結束點的坐标的準備工作

//得到父布局的起始點坐标(用于輔助計算動畫開始/結束時的點的坐标)

int

[] parentLocation =

new

int

[

2

];

rl.getLocationInWindow(parentLocation);

//得到商品圖檔的坐标(用于計算動畫開始的坐标)

int

startLoc[] =

new

int

[

2

];

iv.getLocationInWindow(startLoc);

//得到購物車圖檔的坐标(用于計算動畫結束後的坐标)

int

endLoc[] =

new

int

[

2

];

cart.getLocationInWindow(endLoc);

//    三、正式開始計算動畫開始/結束的坐标

//開始掉落的商品的起始點:商品起始點-父布局起始點+該商品圖檔的一半

float

startX = startLoc[

] - parentLocation[

] + iv.getWidth() /

2

;

float

startY = startLoc[

1

] - parentLocation[

1

] + iv.getHeight() /

2

;

//商品掉落後的終點坐标:購物車起始點-父布局起始點+購物車圖檔的1/5

float

toX = endLoc[

] - parentLocation[

] + cart.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(

, 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[

]);

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++;

count.setText(String.valueOf(i));

// 把移動的圖檔imageview從父布局裡移除

rl.removeView(goods);

}

@Override

public

void

onAnimationCancel(Animator animation) {

}

@Override

public

void

onAnimationRepeat(Animator animation) {

}

});

}

class

MyVH

extends

RecyclerView.ViewHolder {

private

ImageView iv;

private

TextView buy;

public

MyVH(View itemView) {

super

(itemView);

iv = (ImageView) itemView.findViewById(R.id.iv);

buy = (TextView) itemView.findViewById(R.id.buy);

}

}

}

以上就是本文的全部内容,希望對大家的學習有所幫助,也希望大家多多支援腳本之家。