BaseRecyclerViewAdapterHelper源码解读(三) 添加动画

一行代码轻松切换5种默认动画

此篇文章为BaseRecyclerViewAdapterHelper源码解读第三篇,开源库地址,如果没有看过之前2篇文章的同学可以先去看看,大神可直接跳过.

BaseRecyclerViewAdapterHelper源码解读(一) 封装简单的adapter和万能的BaseViewHolder

BaseRecyclerViewAdapterHelper源码解读(二) 添加header和footer

今天给大家带来BaseRecyclerViewAdapterHelper是如何添加动画的.由于本人才学尚浅,如有有不对的地方,欢迎指正,谢谢.

一,定义5种默认动画类型

这5种动画是默认的,外界可以直接使用者5种动画,当然也支持自定义.

BaseAnimation 动画的父类

public interface BaseAnimation {
    /**
     * 返回一个Animator数组,方便扩展,可以在view上加多个动画
     * @param view
     * @return
     */
    Animator[] getAnimators(View view);
}

下面再看看实现类

AlphaInAnimation

/**
 * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
 * 渐显
 */
public class AlphaInAnimation implements BaseAnimation {
    /**
     * 默认透明度从0
     */
    private static final float DEFAULT_ALPHA_FROM = 0f;
    private final float mFrom;

    public AlphaInAnimation() {
        this(DEFAULT_ALPHA_FROM);
    }

    public AlphaInAnimation(float from) {
        mFrom = from;
    }

    @Override
    public Animator[] getAnimators(View view) {
        //返回一个Animator数组    将动画附加到view上,透明度从mFrom-1f
        return new Animator[]{ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)};
    }
}

其实就是设置一个属性动画在view上,然后将Animator数组返回,外界就可以直接调用
anim.setDuration(mDuration).start();设置动画时长并开启加载动画了.

ScaleInAnimation

/**
 * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
 * 缩放
 */
public class ScaleInAnimation implements BaseAnimation {
    private static final float DEFAULT_SCALE_FROM = .5f;
    private final float mFrom;

    public ScaleInAnimation() {
        this(DEFAULT_SCALE_FROM);
    }

    public ScaleInAnimation(float from) {
        mFrom = from;
    }

    @Override
    public Animator[] getAnimators(View view) {
        //设置scaleX和scaleY都从mFrom-1f
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f);
        return new ObjectAnimator[]{scaleX, scaleY};
    }
}

SlideInBottomAnimation

/**
 * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
 * 从下往上
 */
public class SlideInBottomAnimation implements BaseAnimation {
    @Override
    public Animator[] getAnimators(View view) {
        //这里设置的是view的纵坐标,从view.getMeasuredHeight()-0,相当于从下往上移动自身的高度
        return new Animator[]{
                ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
        };
    }
}

SlideInLeftAnimation

/**
 * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
 * 从左往右
 */
public class SlideInLeftAnimation implements BaseAnimation {
    @Override
    public Animator[] getAnimators(View view) {
        //view 的translationX 从 -顶层root view的宽度~0
        return new Animator[]{
                ObjectAnimator.ofFloat(view, "translationX", -view.getRootView().getWidth(), 0)
        };
    }
}

SlideInRightAnimation

/**
 * https://github.com/CymChad/BaseRecyclerViewAdapterHelper
 * 从右往左
 */
public class SlideInRightAnimation implements BaseAnimation {
    @Override
    public Animator[] getAnimators(View view) {
        //view 的translationX 从 顶层root view的宽度~0
        return new Animator[]{
                ObjectAnimator.ofFloat(view, "translationX", view.getRootView().getWidth(), 0)
        };
    }
}

SlideInTopAnimation

从上往下,这是我自己加的,感觉这种动画效果很不错嘛

/**
 * author feiyang
 * create at 2017/10/19 10:37
 * description:从上往下
 */
public class SlideInTopAnimation implements BaseAnimation {

    @Override
    public Animator[] getAnimators(View view) {
        return new Animator[]{
                ObjectAnimator.ofFloat(view, "translationY", -view.getMeasuredHeight(), 0)};
    }
}

二,再看看BaseQuickAdapter

    //Animation
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int ALPHAIN = 0x00000001;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SCALEIN = 0x00000002;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_BOTTOM = 0x00000003;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_LEFT = 0x00000004;
    /**
     * Use with {@link #openLoadAnimation}
     */
    public static final int SLIDEIN_RIGHT = 0x00000005;

    @IntDef({ALPHAIN, SCALEIN, SLIDEIN_BOTTOM, SLIDEIN_LEFT, SLIDEIN_RIGHT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface AnimationType {
    }

    /**
     * 动画只执行1次?
     */
    private boolean mFirstOnlyEnable = true;
    /**
     * 开启了动画?
     */
    private boolean mOpenAnimationEnable = false;
    private Interpolator mInterpolator = new LinearInterpolator();
    /**
     * 动画播放时长
     */
    private int mDuration = 300;
    /**
     * 上一个在播放动画的item的位置
     */
    private int mLastPosition = -1;

    /**
     * 自定义的动画
     */
    private BaseAnimation mCustomAnimation;
    /**
     * 当前选择使用哪种动画
     */
    private BaseAnimation mSelectAnimation = new AlphaInAnimation();

上面就是定义了5种默认的动画
- ALPHAIN 渐显
- SCALEIN 缩放
- SLIDEIN_BOTTOM 从下到上
- SLIDEIN_LEFT 从左到右
- SLIDEIN_RIGHT 从右到左

并且下面还定义了AnimationType,这里是注解类型有点类似于枚举,就是当使用@AnimationType修饰的参数时只能使用上面的5种动画常量中的1种.关于枚举,其实很有必要学习一下,在很多时候可以规范我们的编码,可以看看Airsaid大神写的Android 中注解的使用.

后面是一些其他的属性,已加入详细注释.

设置从哪个item开始执行动画

    /**
     * up fetch end
     * 设置count个不执行动画
     */
    public void setNotDoAnimationCount(int count) {
        mLastPosition = count;
    }

可以看到,将mLastPosition设置为count,mLastPosition之前的item是不会被加载动画了.

当视图附加到窗口时,开始执行动画

    /**
     * Called when a view created by this adapter has been attached to a window.
     * simple to solve item will layout using all
     *
     * {@link #setFullSpan(RecyclerView.ViewHolder)}
     *
     * @param holder
     */
    @Override
    public void onViewAttachedToWindow(K holder) {
        super.onViewAttachedToWindow(holder);
        int type = holder.getItemViewType();
        if (type == EMPTY_VIEW || type == HEADER_VIEW || type == FOOTER_VIEW || type ==
                LOADING_VIEW) {
            //设置为跨区域  比如是StaggeredGridLayoutManager时,header或者footer等应该如何展示
            setFullSpan(holder);
        } else {
            //添加动画到holder的itemView上,并执行动画
            addAnimation(holder);
        }
    }

    /**
     * add animation when you want to show time
     * 添加动画到item上并执行动画
     * @param holder
     */
    private void addAnimation(RecyclerView.ViewHolder holder) {
        // 判断是否开启了动画
        if (mOpenAnimationEnable) {
            //  !isFirstOnly            这一次item的位置>上一次加载动画item的位置
            if (!mFirstOnlyEnable || holder.getLayoutPosition() > mLastPosition) {
                BaseAnimation animation = null;
                //判断是否是自定义了动画
                if (mCustomAnimation != null) {
                    animation = mCustomAnimation;
                } else {
                    //没有自定义,则使用默认的动画
                    animation = mSelectAnimation;
                }
                // 遍历定义到holder.itemView上的动画  BaseAnimation使用的getAnimators()是获取动画数组,方便扩展
                // 如果用户需要自定义动画的话,则可以在一个item上同时加入多个动画,然后下面让这些动画依次执行
                for (Animator anim : animation.getAnimators(holder.itemView)) {
                    //开启动画
                    startAnim(anim, holder.getLayoutPosition());
                }
                //记录这一次执行动画的item
                mLastPosition = holder.getLayoutPosition();
            }
        }
    }

    /**
     * set anim to start when loading
     * 开启动画并设置插值器
     * @param anim
     * @param index
     */
    protected void startAnim(Animator anim, int index) {
        anim.setDuration(mDuration).start();
        anim.setInterpolator(mInterpolator);
    }
  • 当view被添加到window时,给view附加动画到上面去,然后开始执行动画.
  • 在addAnimation()时首先是判断了是否开启动画,再判断是否是只需要显示一次,再根据是否是自定义的动画然后生成不同的animation,遍历Animator数组开始依次执行动画.

设置动画类型

 /**
     * Set the view animation type.
     * 设置动画类型
     * @param animationType One of {@link #ALPHAIN}, {@link #SCALEIN}, {@link #SLIDEIN_BOTTOM},
     *                      {@link #SLIDEIN_LEFT}, {@link #SLIDEIN_RIGHT}.
     */
    public void openLoadAnimation(@AnimationType int animationType) {
        //标志着需要加载动画
        this.mOpenAnimationEnable = true;
        //用户没有自定义动画  是使用的默认动画
        mCustomAnimation = null;
        //根据用户传入的类型初始化应该使用哪种动画
        switch (animationType) {
            case ALPHAIN:
                mSelectAnimation = new AlphaInAnimation();
                break;
            case SCALEIN:
                mSelectAnimation = new ScaleInAnimation();
                break;
            case SLIDEIN_BOTTOM:
                mSelectAnimation = new SlideInBottomAnimation();
                break;
            case SLIDEIN_LEFT:
                mSelectAnimation = new SlideInLeftAnimation();
                break;
            case SLIDEIN_RIGHT:
                mSelectAnimation = new SlideInRightAnimation();
                break;
            default:
                break;
        }
    }

    /**
     * Set Custom ObjectAnimator
     * 自定义动画
     * @param animation ObjectAnimator
     */
    public void openLoadAnimation(BaseAnimation animation) {
        //标志着需要加载动画
        this.mOpenAnimationEnable = true;
        //初始化自定义动画
        this.mCustomAnimation = animation;
    }

    /**
     * To open the animation when loading
     * 开启动画,这种情况下会默认开启:渐显动画
     */
    public void openLoadAnimation() {
        this.mOpenAnimationEnable = true;
    }

    /**
     * {@link #addAnimation(RecyclerView.ViewHolder)}
     * 设置动画是否只加载一次
     * @param firstOnly true just show anim when first loading false show anim when load the data
     *                  every time  true:第一次显示时才加载动画   false:每次都加载动画
     */
    public void isFirstOnly(boolean firstOnly) {
        this.mFirstOnlyEnable = firstOnly;
    }

这里主要是做一些配置信息的初始化
- 动画类型
- 自定义动画配置
- 是否只需要加载一次

总结

  • 其实今天的重点是onViewAttachedToWindow(),当view被添加到window时,通过holder找到view,给view依次执行动画.
  • 其他的都是一些修饰,比如在开源库中自带了几种默认的动画效果可以提供给开发者选择,显得更加人性化.
  • 其次,该开源库这样设计之后不仅调用简单,只需一行代码.而且还支持自定义动画.
  • 再其次,该开源库真的超级强大,大家有空多看看源码,收获颇多.
  • 最后,感谢开发者的无私奉献,该开源库地址:https://github.com/CymChad/BaseRecyclerViewAdapterHelper

你可能感兴趣的:(Android)