本文实例为大家分享了Android实现购物车添加商品动画的具体代码,供大家参考,具体内容如下
实现需求:
在商品列表页面,从列表Item 添加商品的时候,需要一个动画,仿佛是是往购物车里添加商品。
实现思路:
- 获取起始点与终点的坐标,利用PathMeasure 绘制贝塞尔曲线;
- 为点击的Item 商品View 设置属性动画;
- 监听属性动画的update,改变View 的坐标;
实现效果:
实现中会用到 PathMeasure 类:
我们主要使用它两个方法:
1、获取长度:
代码语言:javascript复制/** //获取弧线的总长度(周长)
* 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);//系统调用native 方法;
}
2、获取坐标:
代码语言:javascript复制/**
* 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() 之间的值,根据这个值 PathMeasure 会计算出当前点的坐标封装到 pos 中。上面这句话我们可以这么来理解,不管实际 Path 多么的复杂,PathMeasure 都相当于做了一个事情,就是把 Path “拉直”,然后给了我们一个接口(getLength)告诉我们path的总长度,然后我们想要知道具体某一点的坐标,只需要用相对的distance去取即可,这样就省去了自己用函数模拟path,然后计算获取点坐标的过程。
代码如下:
代码语言:javascript复制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) {
// 购物车的数量加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);
}
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助。