Android仿每日优鲜购物车动画效果

龙旋

共 13164字,需浏览 27分钟

 · 2021-08-18

目录



前言

最近项目需要一个加入购物车的动画效果,费了一点时间做出来了,在这记录下方便以后使用。


实现效果

  • 小图



  • 圆形大图(仿每日优鲜)



使用方法

整个效果的核心类已经被我封装成了一个类了,直接使用即可。
public class AnimManager {    private WeakReference<Activity> mActivity;    private AnimListener mListener;    private long time;    private final View startView;    private final View endView;    private final String imageUrl;    private View animView;    private double scale;    private float animWidth;    private float animHeight;    private ViewGroup animMaskLayout;    private AnimModule animModule = AnimModule.SMALL;    private AnimManager() {        this(new Builder());    }
AnimManager(Builder builder) { this.mActivity = builder.activity; this.startView = builder.startView; this.endView = builder.endView; this.time = builder.time; this.mListener = builder.listener; this.animView = builder.animView; this.imageUrl = builder.imageUrl; this.scale = builder.scale; this.animWidth = builder.animWidth; this.animHeight = builder.animHeight; this.animModule = builder.animModule; }

/** * 开始动画 */ public void startAnim() { if (startView == null || endView == null) { throw new NullPointerException("startView or endView must not null"); } int[] startLocation = new int[2]; int[] endLocation = new int[2]; startView.getLocationInWindow(startLocation); endView.getLocationInWindow(endLocation); if (animView != null) { setAnim(startLocation, endLocation); } else if (!TextUtils.isEmpty(imageUrl)) { createImageAndAnim(startLocation, endLocation); } }
private void createImageAndAnim(final int[] startLocation, final int[] endLocation) { if(animModule == AnimModule.SMALL){ final ImageView animImageView = new ImageView(getActivity()); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ConvertUtils.dp2px(animWidth), ConvertUtils.dp2px(animHeight)); animImageView.setLayoutParams(layoutParams); Glide.with(getActivity()).load(imageUrl) .asBitmap() .listener(new RequestListener<String, Bitmap>() { @Override public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) { return false; }
@Override public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) { setAnim(animImageView, startLocation, endLocation); return false; } }) .into(animImageView); }else { if (startView != null) { final CircleImageView circleImageView = new CircleImageView(getActivity());// ViewGroup.LayoutParams starViewtLayoutParams = startView.getLayoutParams(); int min = Math.min(startView.getMeasuredWidth(), startView.getMeasuredHeight()); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(min, min); circleImageView.setBorderColor(Color.WHITE); circleImageView.setBorderWidth(3); circleImageView.setLayoutParams(layoutParams); Glide.with(getActivity()).load(imageUrl) .asBitmap() .listener(new RequestListener<String, Bitmap>() { @Override public boolean onException(Exception e, String model, Target<Bitmap> target, boolean isFirstResource) { return false; }
@Override public boolean onResourceReady(Bitmap resource, String model, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) { setAnim(circleImageView, startLocation, endLocation); return false; } }) .into(circleImageView); } } }

private void setAnim(int[] startLocation, int[] endLocation) { setAnim(animView, startLocation, endLocation); }
private void setAnim(final View v, int[] startLocation, int[] endLocation) { animMaskLayout = createAnimLayout(getActivity()); // 把动画小球添加到动画层 animMaskLayout.addView(v); final View view = addViewToAnimLayout(v, startLocation);
//终点位置 int endX = endLocation[0] - startLocation[0] + endView.getMeasuredWidth()/2; // 动画位移的y坐标 int endY = endLocation[1] - startLocation[1] + 10; TranslateAnimation translateAnimationX = new TranslateAnimation(0, endX, 0, 0); translateAnimationX.setInterpolator(new AccelerateInterpolator()); // 动画重复执行的次数 translateAnimationX.setRepeatCount(0); translateAnimationX.setFillAfter(true);
TranslateAnimation translateAnimationY = new TranslateAnimation(0, 0, 0, endY); translateAnimationY.setInterpolator(new AccelerateInterpolator()); translateAnimationY.setRepeatCount(0); translateAnimationX.setFillAfter(true); AnimationSet set = new AnimationSet(false); set.setFillAfter(false); if(animModule == AnimModule.BIG_CIRCLE){ ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.1f, 1.0f, 0.1f); scaleAnimation.setInterpolator(new AccelerateInterpolator()); scaleAnimation.setRepeatCount(0); scaleAnimation.setFillAfter(true); set.addAnimation(scaleAnimation); } set.addAnimation(translateAnimationY); set.addAnimation(translateAnimationX);
if (scale == 1) { // 计算屏幕最远两个点的直线距离 double diagonalDef = Math.sqrt(Math.pow(ScreenUtils.getScreenWidth(), 2) + Math.pow(ScreenUtils.getScreenHeight(), 2)); // 计算实际两点的距离 double diagonal = Math.abs(Math.sqrt(Math.pow(startLocation[0] - endLocation[0], 2) + Math.pow(startLocation[1] - endLocation[1], 2))); // 计算一个值,不同距离动画执行的时间不同 scale = diagonal / diagonalDef; } // 动画的执行时间,计算出的时间小于300ms默认为300ms set.setDuration((time * scale) < 1000 ? 1000 : (long) (time * scale)); view.startAnimation(set); // 动画监听事件 set.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { v.setVisibility(View.VISIBLE); if (mListener != null) { mListener.setAnimBegin(AnimManager.this); } }
@Override public void onAnimationRepeat(Animation animation) { }
// 动画的结束调用的方法 @Override public void onAnimationEnd(Animation animation) { v.setVisibility(View.GONE); animMaskLayout.removeAllViews(); if (mListener != null) { mListener.setAnimEnd(AnimManager.this); } } }); }
public void stopAnim() { if (animMaskLayout != null && animMaskLayout.getChildCount() > 0) { animMaskLayout.removeAllViews(); } }
private ViewGroup createAnimLayout(Activity mainActivity) { ViewGroup rootView = (ViewGroup) mainActivity.getWindow().getDecorView(); LinearLayout animLayout = new LinearLayout(mainActivity); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); animLayout.setLayoutParams(lp); animLayout.setId(R.id.anim_icon); animLayout.setBackgroundResource(android.R.color.transparent); rootView.addView(animLayout); return animLayout; }
private View addViewToAnimLayout(final View view, int[] location) { int x = location[0]; int y = location[1]; LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); lp.leftMargin = x; lp.topMargin = y; view.setLayoutParams(lp); return view; }
/** * 自定义时间 * * @param time * @return */ public long setTime(long time) { this.time = time; return time; }
private Activity getActivity() { return mActivity.get(); }
public void setOnAnimListener(AnimListener listener) { mListener = listener; }
//回调监听 public interface AnimListener {
void setAnimBegin(AnimManager a);
void setAnimEnd(AnimManager a);
}
public static final class Builder { WeakReference<Activity> activity; View startView; View endView; View animView; String imageUrl; long time; double scale; float animWidth; float animHeight; AnimListener listener; private AnimModule animModule = AnimModule.SMALL; public Builder() { this.time = 1000; this.scale = 1; this.animHeight = 25; this.animWidth = 25; } public Builder animModule(AnimModule animModule) { this.animModule = animModule; return this; } public Builder with(Activity activity) { this.activity = new WeakReference<>(activity); return this; }
public Builder startView(View startView) { if (startView == null) { throw new NullPointerException("startView is null"); } this.startView = startView; return this; }
public Builder endView(View endView) { if (endView == null) { throw new NullPointerException("endView is null"); } this.endView = endView; return this; }
public Builder animView(View animView) { if (animView == null) { throw new NullPointerException("animView is null"); } this.animView = animView; return this; }
public Builder listener(AnimListener listener) { if (listener == null) { throw new NullPointerException("listener is null"); } this.listener = listener; return this; }
public Builder imageUrl(String imageUrl) { this.imageUrl = imageUrl; return this; }
public Builder time(long time) { if (time <= 0) { throw new IllegalArgumentException("time must be greater than zero"); } this.time = time; return this; }
public Builder scale(double scale) { this.scale = scale; return this; }
public Builder animWidth(float width) { if (width <= 0) { throw new IllegalArgumentException("width must be greater than zero"); } this.animWidth = width; return this; }
public Builder animHeight(float height) { if (height <= 0) { throw new IllegalArgumentException("height must be greater than zero"); } this.animHeight = height; return this; }
public AnimManager build() { return new AnimManager(this); } }
public enum AnimModule{ SMALL,//小的(默认) BIG_CIRCLE//大的圆形 }}

使用逻辑:
animManager = new AnimManager.Builder()                    .with(this)                    .animModule(AnimManager.AnimModule.BIG_CIRCLE)//图片的动画模式,小的或者大的(仿每日优鲜)                    .startView(startView)//开始位置的控件                    .endView(imageViewShopCar)//结束位置的控件                    .listener(new AnimManager.AnimListener() {                        @Override                        public void setAnimBegin(AnimManager a) {
}
@Override public void setAnimEnd(AnimManager a) { //购物车回弹动画(这里是加入购物车动画执行结束时的回调我在这里加入了购物车回弹效果,不需要的话可以去掉) TranslateAnimation anim = new TranslateAnimation(0, 0, 20, 0); anim.setInterpolator(new BounceInterpolator()); anim.setDuration(700); imageViewShopCar.startAnimation(anim); textViewNum.setText(num+""); } }) .imageUrl(animImgUrl) .build(); animManager.startAnim();


源码地址:

https://github.com/myml666/ShopCarAnim


到这里就结束啦。
浏览 28
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报