欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-04 19:53:36
...

一行代码轻松切换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