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( 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();
         // ★★★★★获取当前点坐标封装到mCurrentPosition
         // boolean getPosTan(float distance, float[] pos, float[] tan) :
         // 传入一个距离distance(0<=distance<=getLength()),然后会计算当前距
         // 离的坐标点和切线,pos会自动填充上坐标,这个方法很重要。
         mPathMeasure.getPosTan(value, mCurrentPosition, null ); //mCurrentPosition此时就是中间距离点的坐标值
         // 移动的商品图片(动画图片)的坐标设置为该中间点的坐标
         goods.setTranslationX(mCurrentPosition[ 0 ]);
         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 = 0 ;
 
   @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();
     }
   }
 
   /**
    * ★★★★★把商品添加到购物车的动画效果★★★★★
    * @param iv
    */
   private void addCart( ImageView iv) {
//   一、创造出执行动画的主题---imageview
     //代码new一个imageview,图片资源是上面的imageview的图片
     // (这个图片就是执行动画的图片,从开始位置出发,经过一个抛物线(贝塞尔曲线),移动到购物车里)
     final ImageView goods = new ImageView(MainActivity. this );
     goods.setImageDrawable(iv.getDrawable());
//   一、创造出执行动画的主题---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 ];
  &;padding:0px 0px 0px 5px;background:rgb(255,255,255);border-left:3px solid rgb(108,226,108);width:640px;font-size:14px;clear:both;border-top:0px;border-right:0px;border-bottom:0px;float:none;height:auto;vertical-align:baseline;font-family:Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;min-height:auto;color:#000000;">rl.addView(goods, params);
相关文章
相关标签/搜索