公司产品和UI重新设计了APP首页,顺便加上了一些奇怪的动画。设计了开启横向RecyclerView,共搞了两个版本。此文用于展示并发布这个动画的开源代码。
先贴上代码开源地址:https://gitee.com/rookieci/shop-anim
开源的代码是从项目中剥离出来的,不包含项目业务逻辑,可以直接编译运行。
做了两个gif的展示图,然后csdn表示文章只能传5M以内的图片。泪奔。。。。
两张展示图地址:展示图1 展示图2
动画说明
动画一,主要上下平移RecyclerView,同时定义每一个item的位置、速度和加速度。这样就能实现item去追赶RecyclerView。
VerticalDrawerLayout中监听触摸确定位置,用于平移RecyclerView
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//if (!BuildConfig.DEBUG) return super.dispatchTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (buttonView != null) {
startX = ev.getX();
startY = ev.getY();
oldY = startY;
final int left = buttonView.getLeft();
final int right = buttonView.getRight();
final int top = buttonView.getTop();
final int bottom = buttonView.getBottom();
if (startX > left && startX < right && startY > top && startY < bottom) {
onTouchButton = true;
}
}
break;
case MotionEvent.ACTION_MOVE:
if (onTouchButton) {
//float x = ev.getX();
float y = ev.getY();
float len = y - oldY;
oldY = y;
int paddingTop = getPaddingTop();
setPadding(getPaddingLeft(), (int) (paddingTop + len), getPaddingRight(), getPaddingBottom());
}
break;
case MotionEvent.ACTION_UP:
if (onTouchButton) {
//float x = ev.getX();
//float y = ev.getY();
oldY = ev.getY();
final boolean down = oldY > startY;//向下滑动
final int paddingTop = getPaddingTop();
if (currOpen) {//展开状态
final int openHeight = getOpenHeight();//最大展开
if (down) {//恢复原位
startAnim(paddingTop, openHeight);
} else {//向上
int contentHeight = getContentHeight();
int i = (openHeight - paddingTop) << 1;
if (i > contentHeight) {//开始关闭
startAnim(paddingTop, -contentHeight);
currOpen = false;
if (onVerticalDrawerListener != null) {
onVerticalDrawerListener.onChangeOpen(currOpen);
}
} else startAnim(paddingTop, openHeight);
}
} else {//关闭状态
int contentHeight = getContentHeight();
if (down) {//向下
int i = (paddingTop + contentHeight) << 1;
if (i > contentHeight) {//展开
startAnim(paddingTop, getOpenHeight());
currOpen = true;
if (onVerticalDrawerListener != null) {
onVerticalDrawerListener.onChangeOpen(currOpen);
}
} else startAnim(paddingTop, -contentHeight);
} else startAnim(paddingTop, -contentHeight);//恢复原位
}
}
onTouchButton = false;
break;
}
if (onTouchButton) {
getParent().requestDisallowInterceptTouchEvent(true);
return true;
} else return super.dispatchTouchEvent(ev);
}
private void startAnim(float startY, float endY) {
if (startY == endY) return;
ValueAnimator animator = ValueAnimator.ofFloat(startY, endY);
animator.setDuration(300);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(animation -> {
Object animatedValue = animation.getAnimatedValue();
if (animatedValue instanceof Float) {
Float f = (Float) animatedValue;
setPadding(getPaddingLeft(), f.intValue(), getPaddingRight(), getPaddingBottom());
}
});
animator.start();
}
olda.LayoutDrawerViewHolder.java
item的位置是相对于RecyclerView。这部分代码用于计算item相对位置,并执行动画
private void startChaseAnim(int paddingTop) {
ViewHolderAnim holderAnimTmp;
if (animList == null) {
final int animCurrY = -drawerLayout.getContentHeight();
holderAnimTmp = new ViewHolderAnim(animCurrY, paddingTop);
animList = new ArrayList<>(5);
animList.add(holderAnimTmp);//先创建一个
} else {
holderAnimTmp = animList.get(0);
for (int i = 0; i < animList.size(); i++) {
ViewHolderAnim holderAnim = animList.get(i);
holderAnim.setAnimEndY(paddingTop);
}
}
final int firstPosition = layoutManager.findFirstVisibleItemPosition();
final int lastPosition = layoutManager.findLastVisibleItemPosition();
List<View> viewList = new ArrayList<>();
for (int i = firstPosition; i <= lastPosition; i++) {
View view = layoutManager.findViewByPosition(i);
if (view == null) continue;
viewList.add(view);
}
for (int i = 0; i < viewList.size(); i++) {
View view = viewList.get(i);
ViewHolderAnim holderAnim;
if (i < animList.size()) {
holderAnim = animList.get(i);
} else {
holderAnim = holderAnimTmp.cloneAnim();
animList.add(holderAnim);
}
int top = holderAnim.animCurrY - holderAnim.animEndY;
setViewTop(view, top);
}
startViewAnim();
}
==ViewHolderAnim ==计算并模拟动画
private static final float Rub = 0.45f;//摩擦
public int animCurrY;//动画当前位置或开始位置 -ContentHeight
public int animEndY;//动画要追逐的位置 当前的paddingTop
public float animV;//速度
public float animG;//动态加速度
public float animGB;//这个决定最大的加速度
public void move() {
int len = animCurrY - animEndY;//距离
animG = -len / animGB;
if (animG > maxG) animG = maxG;
else if (animG < -2.0f) animG = -2.0f;
animV += animG;
animCurrY = (int) (animCurrY + animV);
if (len > 0) {
if (animCurrY - animEndY < 0) animV = Rub * animV;//减速
} else if (animCurrY - animEndY > 0) animV = Rub * animV;//减速
}
动画二,主要就是对RecyclerView平移和对ViewPage缩放。这儿就不说了