RecyclerView进入编辑模式的动画效果
先看效果
基本流程是长按某个item时整个RecyclerView进入编辑模式,同时所有已出现item开始动画,未出现的item虽然不用播放动画但也要同步修改状态。
首先既然要让每个item都独立播放动画,我们先设置一个接口:
public interface ISlide {
void open();
void close();
}
然后让所有ViewHolder都实现这个接口。
我这里定义长按进入编辑模式,点返回键退出。由于需要所有item同步进入,所以我们要单独有一个类来管理每个item:
public class SlideHelper {
private ArrayList<ISlide> mISlides = new ArrayList<>();
public void slideOpen() {
for (ISlide iSlide : mISlides){
iSlide.open();
}
}
public void slideClose() {
for (ISlide iSlide : mISlides){
iSlide.close();
}
}
public void add(ISlide iSlide) {
mISlides.add(iSlide);
}
}
接下来在adapter的onCreateViewHolder中添加每一个item
@Override
public CityManagerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
...
mSlideHelper.add(viewHolder);
...
}
当模式切换时执行相应的动作:
public void setMode(int mode){
currentMode = mode;
if(mode == EDIT_MODE){
mSlideHelper.slideOpen();
return;
}
mSlideHelper.slideClose();
}
到这里我们就需要去实现具体动画逻辑,由于每个Item都执行一样的动画,所以我们创建一个帮助类管理动画,在viewholder中:
@Override
public void open() {
if (mOpenUpdateListener == null) {
mOpenUpdateListener = new OpenUpdateListener();
}
mSlideAnimationHelper.openAnimation(DURATION_OPEN, mOpenUpdateListener);
}
@Override
public void close() {
if (mCloseUpdateListener == null) {
mCloseUpdateListener = new CloseUpdateListener();
}
mSlideAnimationHelper.closeAnimation(DURATION_CLOSE, mCloseUpdateListener);
}
下面看帮助类的实现:
public class SlideAnimationHelper {
public static final int STATE_CLOSE = CityManageActivity.NORMAL_MODE;
public static final int STATE_OPEN = CityManageActivity.EDIT_MODE;
private static int mCurrentState = STATE_CLOSE;
private ValueAnimator mValueAnimator;
public void openAnimation(long duration, ValueAnimator.AnimatorUpdateListener animatorUpdateListener) {
mCurrentState = STATE_OPEN;
setValueAnimator(duration, animatorUpdateListener, null);
}
public void closeAnimation(long duration, ValueAnimator.AnimatorUpdateListener animatorUpdateListener) {
mCurrentState = STATE_CLOSE;
setValueAnimator(duration, animatorUpdateListener, null);
}
private void setValueAnimator(long duration, ValueAnimator.AnimatorUpdateListener animatorUpdateListener,
Animator.AnimatorListener listener) {
mValueAnimator = getAnimation();
mValueAnimator.setDuration(duration);
if (animatorUpdateListener != null) {
mValueAnimator.addUpdateListener(animatorUpdateListener);
}
if (listener != null) {
mValueAnimator.addListener(listener);
}
start();
}
public ValueAnimator getAnimation() {
if (mValueAnimator == null) {
mValueAnimator = new ValueAnimator();
mValueAnimator.setFloatValues(0.0f, 1.0f);
}
return mValueAnimator;
}
private void start() {
if (mValueAnimator != null && !mValueAnimator.isRunning()) {
mValueAnimator.start();
}
}
public int getState() {
return mCurrentState;
}
}
这里我们采用了属性动画,过程也比较简单,就是设置一下然后执行动画。
这里有一个关键点是,类中的mCurrentState 变量要用static类型的,这是为了让那些还没加载出来的item也能正确修改状态。因为我们是在adapter的onCreateViewHolder中添加的item,一些未加载出来的item其实是不会被添加到SlideHelper 中管理的,若不是static类型变量,那些未加载的item将会得不到正确的状态。
到这里动画设置完成了,但真正执行动画的地方在viewholder中我们设置的那个AnimatorUpdateListener里:
private class OpenUpdateListener implements ValueAnimator.AnimatorUpdateListener {
@Override public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
int endX = (int) (OFFSET * fraction);
cbItem.setChecked(false);
doAnimationSet(endX, fraction);
}
}
private class CloseUpdateListener implements ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float fraction = animation.getAnimatedFraction();
fraction = 1.0f - fraction;
int endX = (int) (OFFSET * fraction);
doAnimationSet(endX, fraction);
}
}
public void doAnimationSet(int offset, float fraction){
lLeft.scrollTo(-offset, 0);
lRight.scrollTo(offset,0);
cbItem.setVisibility(View.VISIBLE);
ivSort.setVisibility(View.VISIBLE);
cbItem.setScaleX(fraction);
cbItem.setScaleY(fraction);
cbItem.setAlpha(fraction * 255);
ivSort.setScaleX(fraction);
ivSort.setScaleY(fraction);
ivSort.setAlpha(fraction * 255);
}
很简单的动画动作,若有需求可以任意设置各种绚丽动画。
到这里基本就完成了,但是有一点需要优化一下,若是按照我们这样来写,状态切换后,那些未被加载出来item在加载出来时并不会改变状态,问题还在我们SlideHelper 的逻辑,我我们是在onCreateViewHolder中添加的每个ISlide对象,那些未添加的是不会在执行mSlideHelper.slideOpen();时有任何改变的。如下:
其实解决方法也很简单,我们知道每个item被加载时都会调用onBindViewHolder方法,我们在这里同步状态就行
@Override
public void onBindViewHolder(final CityManagerViewHolder holder, int position) {
holder.onBindSlide();
....
}
onBindSlide实现如下(只需简单修改状态不必执行动画):
public void onBindSlide() {
switch (mSlideAnimationHelper.getState()) {
case SlideAnimationHelper.STATE_CLOSE:
lLeft.scrollTo(0, 0);
lRight.scrollTo(0, 0);
cbItem.setVisibility(View.GONE);
ivSort.setVisibility(View.GONE);
break;
case SlideAnimationHelper.STATE_OPEN:
cbItem.setChecked(false,false);
lLeft.scrollTo(-OFFSET, 0);
lRight.scrollTo(OFFSET, 0);
cbItem.setVisibility(View.VISIBLE);
ivSort.setVisibility(View.VISIBLE);
break;
}
}