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
评论
“海优最初弄出来可并不是为了给大佬弟子开后门的, 学术界现在几乎不存在任何套利空间,不要相信有什么办法可以投机取巧”
点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达知乎问题:为什么部分同学倾向土博+国外博后而不是直接国外读博?感觉身边土博越来越多,基本都是土博➕海博后,申国外phd的明显减少(相比较疫情之前几年)知乎Tianteman: 这个问题下,可以看得出一部分国内硕博信息检索能力差的离
小白学视觉
0
用 Shader 实现旗帜飘扬动画效果
我觉得对于刚入门 3D 编程的朋友来说,如果能够完成代码创建模型数据->创建材质->编写Shader动画这一系列,想必会有满满的成就感。今天就用 Cocos Creator 的 utils.MeshUtils.createMesh 接口,带大家感受一下这个流程。这个流程不仅可以用于新手学
COCOS
2
日本影山优佳最新杂志照,展现充满透明感的美丽
今天的图文分享的是影山优佳的杂志写真。元日向坂46的影山优佳,登上了写真杂志《周刊FLASH》5/7和5/14合并号的封面。影山优佳是日本艺人、女演员、前偶像。身高155厘米。2001年5月8日出生于东京都。2023年7月从组合日向坂46毕业,之后作为演员活跃的影山优佳,在《周刊FLAS
python教程
0
全新 SOTA backbone | 2024年了,再见ViT系列Backbone,实数难得,不知道效果如何?
点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达在构建用于精确匹配的深度固定长度表示时,确定指纹上的密集特征点,特别是在像素 Level 上,具有重大意义。为了探索指纹匹配的可解释性,作者提出了一种多阶段可解释的指纹匹配网络,名为通过视觉 Transformer 进行指纹匹配的
小白学视觉
10
以环境之“优”谋发展之“势”
好的营商环境是生产力、竞争力,更是吸引力。近年来,我县始终坚持“项目为王、环境是金”工作导向,践行101%服务理念,大力优化营商环境,厚植高质量发展沃土,为县域经济发展提供强大支撑。 紧盯重大项目,推动企业投产增效 江苏伟复能源有限公司主要生产铅酸动力蓄电池,产品直供
盱眙老妹
0
文献分享 | 体外冲击波+本体感觉训练治疗「运动创伤性踝关节炎」患者的效果分析
■ 近年来,国内患有运动创伤性踝关节炎患者的数量在逐年增加,患者主要为青壮年。在日常生活与工作中,不健康运动方式或者运动过度,都会对踝关节健康造成危害。■ 运动创伤性踝关节炎在临床上还被称为足球踝,该病症大多数是运动员踝关节出现急性损伤后,过早负重,韧带因为尚未修复
乐普医疗AI
0
系统调优助手,PyTorch Profiler TensorBoard 插件教程
0x1. 前言使用PyTorch Profiler进行性能分析已经一段时间了,毕竟是PyTorch提供的原生profile工具,个人感觉做系统性能分析时感觉比Nsys更方便一些,并且画的图也比较直观。这里翻译一下PyTorch Profiler TensorBoard Plugin的教程并分享一些使
GiantPandaCV
6
3秒搞定低卡健康果茶,满口清爽鲜,嘴馋怕胖就喝它!
最近真的是爱死了这个百香果➕青金桔冻干组合,完美解决了我不爱喝白水的问题。 只需要简单一泡,就能得到一杯酸甜清香的饮品,喝完真的一整个清爽又解腻。不爱喝白水的宝子真的可以试试,准保让你爱上喝水! 点击链...
莫家明
0