本文實例為大家分享了Android實現(xiàn)購物車添加商品動畫的具體代碼,供大家參考,具體內容如下
實現(xiàn)需求:
在商品列表頁面,從列表Item 添加商品的時候,需要一個動畫,仿佛是是往購物車里添加商品。
實現(xiàn)思路:
- 獲取起始點與終點的坐標,利用PathMeasure 繪制貝塞爾曲線;
- 為點擊的Item 商品View 設置屬性動畫;
- 監(jiān)聽屬性動畫的update,改變View 的坐標;
實現(xiàn)效果:
實現(xiàn)中會用到 PathMeasure 類:
我們主要使用它兩個方法:
1、獲取長度:
1
2
3
4
5
6
7
|
/** //獲取弧線的總長度(周長) * Return the total length of the current contour, or 0 if no path is * associated with this measure object. */ public float getLength() { return native_getLength(native_instance); //系統(tǒng)調用native 方法; } |
2、獲取坐標:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** * Pins distance to 0 <= distance <= getLength(), and then computes the * corresponding position and tangent. Returns false if there is no path, * or a zero-length path was specified, in which case position and tangent * are unchanged. * * @param distance The distance along the current contour to sample * @param pos If not null, eturns the sampled position (x==[0], y==[1]) * @param tan If not null, returns the sampled tangent (x==[0], y==[1]) * @return false if there was no path associated with this measure object */ public boolean getPosTan( float distance, float pos[], float tan[]) { if (pos != null && pos.length < 2 || tan != null && tan.length < 2 ) { throw new ArrayIndexOutOfBoundsException(); } return native_getPosTan(native_instance, distance, pos, tan); } |
方法 getPosTan(float distance, float pos[],float tan[]) - path 為 null ,返回 false
distance 為一個 0 - getLength() 之間的值,根據(jù)這個值 PathMeasure 會計算出當前點的坐標封裝到 pos 中。上面這句話我們可以這么來理解,不管實際 Path 多么的復雜,PathMeasure 都相當于做了一個事情,就是把 Path “拉直”,然后給了我們一個接口(getLength)告訴我們path的總長度,然后我們想要知道具體某一點的坐標,只需要用相對的distance去取即可,這樣就省去了自己用函數(shù)模擬path,然后計算獲取點坐標的過程。
代碼如下:
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
|
public class GoodsListActivity extends AppCompatActivity { private RelativeLayout mRootRl; private RecyclerView mGoodsRecyclerView; private ImageView mCarImageView; private TextView mCountTv; private List<Bitmap> mBitmapList = new ArrayList<>(); private PathMeasure mPathMeasure; private float [] mCurrentPosition = new float [ 2 ]; private int mCount = 0 ; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_goods_list); initView(); initData(); GoodsAdapter goodsAdapter = new GoodsAdapter(mBitmapList); mGoodsRecyclerView.setLayoutManager( new LinearLayoutManager( this )); mGoodsRecyclerView.setAdapter(goodsAdapter); } private void initView(){ mGoodsRecyclerView = (RecyclerView)findViewById(R.id.recyclerView); mCarImageView = (ImageView)findViewById(R.id.imageview_shop_car); mCountTv = (TextView)findViewById(R.id.tv_count); mRootRl = (RelativeLayout)findViewById(R.id.rl_root); } private void initData(){ mBitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.car)); mBitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.car)); mBitmapList.add(BitmapFactory.decodeResource(getResources(), R.drawable.car)); } class GoodsAdapter extends RecyclerView.Adapter<GoodsViewHolder>{ private List<Bitmap> mData; public GoodsAdapter(List<Bitmap> data) { mData = data; } @Override public GoodsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(GoodsListActivity. this ) .inflate(R.layout.rv_goods_item, parent, false ); return new GoodsViewHolder(itemView); } @Override public void onBindViewHolder( final GoodsViewHolder holder, int position) { holder.ivGood.setImageBitmap(mData.get(position)); holder.tvBuy.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { addGoodToCar(holder.ivGood); } }); } @Override public int getItemCount() { return mData != null ? mData.size() : 0 ; } } private void addGoodToCar(ImageView imageView){ final ImageView view = new ImageView(GoodsListActivity. this ); view.setImageDrawable(imageView.getDrawable()); RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( 100 , 100 ); mRootRl.addView(view, layoutParams); //二、計算動畫開始/結束點的坐標的準備工作 //得到父布局的起始點坐標(用于輔助計算動畫開始/結束時的點的坐標) int [] parentLoc = new int [ 2 ]; mRootRl.getLocationInWindow(parentLoc); //得到商品圖片的坐標(用于計算動畫開始的坐標) int startLoc[] = new int [ 2 ]; imageView.getLocationInWindow(startLoc); //得到購物車圖片的坐標(用于計算動畫結束后的坐標) int endLoc[] = new int [ 2 ]; mCarImageView.getLocationInWindow(endLoc); float startX = startLoc[ 0 ] - parentLoc[ 0 ] + imageView.getWidth()/ 2 ; float startY = startLoc[ 1 ] - parentLoc[ 1 ] + imageView.getHeight()/ 2 ; //商品掉落后的終點坐標:購物車起始點-父布局起始點+購物車圖片的1/5 float toX = endLoc[ 0 ] - parentLoc[ 0 ] + mCarImageView.getWidth() / 5 ; float toY = endLoc[ 1 ] - parentLoc[ 1 ]; //開始繪制貝塞爾曲線 Path path = new Path(); path.moveTo(startX, startY); //使用二次薩貝爾曲線:注意第一個起始坐標越大,貝塞爾曲線的橫向距離就會越大,一般按照下面的式子取即可 path.quadTo((startX + toX) / 2 , startY, toX, toY); mPathMeasure = new PathMeasure(path, false ); //屬性動畫 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(); mPathMeasure.getPosTan(value, mCurrentPosition, null ); view.setTranslationX(mCurrentPosition[ 0 ]); view.setTranslationY(mCurrentPosition[ 1 ]); } }); valueAnimator.addListener( new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { // 購物車的數(shù)量加1 mCount++; mCountTv.setText(String.valueOf(mCount)); // 把移動的圖片imageview從父布局里移除 mRootRl.removeView(view); //shopImg 開始一個放大動畫 Animation scaleAnim = AnimationUtils.loadAnimation(GoodsListActivity. this , R.anim.shop_car_scale); mCarImageView.startAnimation(scaleAnim); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); valueAnimator.start(); } class GoodsViewHolder extends RecyclerView.ViewHolder{ private ImageView ivGood; private TextView tvBuy; public GoodsViewHolder(View itemView) { super (itemView); ivGood = (ImageView)itemView.findViewById(R.id.iv_goods); tvBuy = (TextView) itemView.findViewById(R.id.tv_buy); } } } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/happy_horse/article/details/67646202