我们经常看到不管是某宝还是某东都有加入购物车的动画。就是在点击某个商品后,这个商品变成小的缩略图移动到购物车里面去。
今天突然想着把原来做过的这么一个动画贴出来供大家学习。
先看效果图。gif工具不忍直视,真实操作是很流畅的一个抛物线。
首先从效果图看出来我们需要几个东西。
1,动画的开始位置
2,动画的结束位置
3,动画移动的图片(这里为了简单用一个小圆点,可以换成商品的缩略图)
4,动画的弧度如何处理
5,动画完成后在哪里操作数据
6,连续点击图片动画时如何new出多个动画
那么我们就来解决这问题
首相定义一个ShoppingCartAnim类,定义几个必要常量
**
* 购物车添加动画
*/
public class ShoppingCartAnim {
private ImageView buyImg;//播放动画的参照imageview
private int[] start_location = new int[2];// 这是用来存储动画开始位置的X、Y坐标;
private int[] end_location = new int[2];// 这是用来存储动画结束位置的X、Y坐标;
private static Handler mThreadHandler;//数据操作的非ui线程回调
public ViewGroup root;//动画层
private static Thread thread;//数据操作的非ui线程
}
其中我定义了一个线程和一个handler,这样做的目的就是为了在动画结束后做一些操作不要影响下一次点击动画。所以放在非ui’线程中。
这样,当我们连续点击动画的时候,不会因为上一次的数据没有操作完而动画出现卡顿。
在静态方法中实例化子线程
static {
thread = new Thread(new Runnable() {
@Override
public void run() {
//非ui线程
//这里执行动画完成后的数据处理,例如将商品加入购物车
//发送消息给ui线程
mThreadHandler.sendEmptyMessage(0);
}
});
mThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
//线程操作完以后要及时关闭
thread.interrupt();
//ui操作
//这里做动画结束后的处理,例如购物车抖动动画
break;
}
}
};
定义构造方法
public ShoppingCartAnim(Activity activity) {
buyImg = new ImageView(activity);//buyImg是动画的图片
buyImg.setImageResource(R.drawable.sign);// 设置buyImg的图片
//buyImg.setImageBitmap(bitmap);//也可以设置bitmap,可以用商品缩略图来播放动画
root = (ViewGroup) activity.getWindow().getDecorView();//创建一个动画层
root.addView(buyImg);//将动画参照imageview放入
}
定义将imageview放到动画层并放在起始坐标位置的方法
/** * 将image图片添加到动画层并放在起始坐标位置 * * @param view 播放动画的view * @param location 起始位置 * @return */
private View addViewFromAnimLayout(View view, int[] location) {
int x = location[0];
int y = location[1];
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
lp.leftMargin = x;
lp.topMargin = y;
view.setLayoutParams(lp);
return view;
}
我们将一个需要播放的imageview和起始坐标传入,就得到了这个imageview的具体起始位置,播放动画的时候就可以直接拿来用了。因为位置的问题已经在其中调整好了。
最重要的部分
提供一个外界调用的接口,传入动画的起始参照目标和结束参照目标
public void startAnim(View startView, View endView) {
// 这是获取起始目标view在屏幕的X、Y坐标(这也是动画开始的坐标)
startView.getLocationInWindow(start_location);
// 购物车结束位置
endView.getLocationInWindow(end_location);
//将动画图片和起始坐标绘制成新的view,用于播放动画
//将image图片添加到动画层
/**这里为什么不直接传一个图片而是传一个imageview呢? * 因为我这样做的目的是clone动画播放控件,为什么要clone呢? * 因为如果用户连续点击添加购物车的话,如果只用一个imageview去播放动画的话,这个动画就会成还没播放完就回到原点重新播放。 * 而如果clone一个imageview去播放,那么这个动画还没播放完,用户再点击添加购物车以后我们还是clone一个新的imageview去播放。 * 这样动画就会出现好几个点而不是一个点还没播放完又缩回去。 * 说的通俗点,就是依靠这个方法,把参照对象和起始位置穿进去,得到一个clone的对象来播放动画 */View run_view = addViewFromAnimLayout(buyImg, start_location);
// 计算位移
int endX = end_location[0] - start_location[0];
int endY = end_location[1] - start_location[1];
//平移动画 绘制X轴 0到结束的x轴
TranslateAnimation translateAnimationX = new TranslateAnimation(0,
endX, 0, 0);
//设置线性插值器
translateAnimationX.setInterpolator(new LinearInterpolator());
// 动画重复执行的次数
translateAnimationX.setRepeatCount(0);
//设置动画播放完以后消失,终止填充
translateAnimationX.setFillAfter(true);
//平移动画 绘制Y轴
TranslateAnimation translateAnimationY = new TranslateAnimation(0, 0,
0, endY);
translateAnimationY.setInterpolator(new AccelerateInterpolator());
translateAnimationY.setRepeatCount(0);
translateAnimationX.setFillAfter(true);
//将两个动画放在动画播放集合里
// 设置false使每个子动画都使用自己的插值器
AnimationSet set = new AnimationSet(false);
//设置动画播放完以后消失,终止填充
set.setFillAfter(false);
set.addAnimation(translateAnimationY);
set.addAnimation(translateAnimationX);
set.setDuration(800);// 动画的执行时间
/** * 动画开始播放的时候,参照对象要显示出来,如果不显示的话这个动画会看不到任何东西。 * 因为不管用户点击几次动画,播放的imageview都是从参照对象buyImg中clone来的 * */
buyImg.setVisibility(View.VISIBLE);
run_view.startAnimation(set);
// 动画监听事件
set.setAnimationListener(new Animation.AnimationListener() {
// 动画的开始
@Override
public void onAnimationStart(Animation animation) {
}
//动画重复中
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
// 动画的结束
@Override
public void onAnimationEnd(Animation animation) {
//动画播放完以后,参照对象要隐藏
buyImg.setVisibility(View.GONE);
//结束后访问数据
thread.start();
}
});
}
每一句代码我都解释的很详细了。
你也可以在动画集合中加入旋转缩放什么的,也是很简单的。
还有一个细节操作,这个操作在activity中:
/** * 内存过低时及时处理动画产生的未处理冗余tg */
@Override
public void onLowMemory() {
// TODO Auto-generated method stub
if (cartAnimation != null) {
try {
cartAnimation.root.removeAllViews();
} catch (Exception e) {
e.printStackTrace();
}
super.onLowMemory();
}
}
主要部分已经完成了。
看看怎么调用。
CartAnimation cartAnimation = new CartAnimation(getActivity()); cartAnimation.setAnim(startImageview, shoppingCartView);
不需要任何的返回值,你需要操作数据完全可以在线程中操作。
好了,这就是添加购物车动画,你可以在这之上发挥想象添加更加炫酷的动画。
不过还是总结一下细节。(嗯,细节很重要!)
1,动画的播放依据是activity,如何考虑内存消耗问题
2,动画播放完以后如何操作数据才能让动画不卡顿
3,连续点击动画如何让动画不单例运行
最主要的问题我已经解决了,你只需要发挥想象创建更多的复杂炫酷的动画。