Aide学

RecyclerView进入编辑模式的动画效果

2018-05-08  本文已影响0人  留给时光吧

先看效果



基本流程是长按某个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;
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读