RecyclerView(二)--分割线和动画

1,208 阅读3分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

一、 分割线

RecyclerView分割线有两种:

  • 默认分割线
  • 自定义分割线

1. 默认分割线

DividerItemDecoration是RecyclerView默认分割线,只需要new一个DividerItemDecoration,调用RecyclerView的addItemDecoration()即可 具体是实现:

DividerItemDecoration dividerItemDecoration=new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(dividerItemDecoration);

2. 自定义分割线

自定义分割线有很多种实现方式,我们介绍2种

  • 调用setDrawable 这种和上面很像,不同的是,我们需要在drawable定义一个属性或者存放一张图片,在调用DividerItemDecoration的setDrawable方法将它取出来
DividerItemDecoration dividerItemDecoration=new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
dividerItemDecoration.setDrawable(getResources().getDrawable(R.drawable.shape_divider));
recyclerView.addItemDecoration(dividerItemDecoration);
  • 继承RecyclerView.ItemDecoration 继承成RecyclerView.ItemDecoration

在RecyclerView.ItemDecoration里面有3个方法

(1) getItemOffsets() 可以调用outRect.set()设置每个Item的padding

(2) onDraw() 可以在绘制item之前绘制我们需要的内容,主要调用Canvas.drawXX()方法来绘制

(3) onDrawOver() 是在绘制item之后调用,是绘制在item上层

二、 动画

实现RecyclerView的动画效果,我们可以用官方推荐的RecyclerView.ItemAnimator,它主要是用于Item的添加、移除、更新

其实系统为我们提供了默认的动画DefaultItemAnimator,所以即使我们不定义,也会有默认的动画效果

我们要自定义RecyclerView.ItemAnimator,可以先了解DefaultItemAnimator,DefaultItemAnimator继承的是抽象类SimpleItemAnimator,所以我们先定义一个DemoItemAnimator继承SimpleItemAnimator,实现其中的方法

  • animateRemove() Item移除回调
  • animateAdd() Item添加回调
  • animateMove() 添加/移动更新时,其他Item的动画
  • animateChange() Item更新调用
  • runPendingAnimations() 真正执行动画的地方
  • endAnimation() 停止某一个Item动画
  • endAnimations() 停止所有动画
  • isRunning()

再来看看DefaultItemAnimator中的实现

  • animateRemove() 里面只有两行代码,第一句是删除要被移除的Item所有动画的相关代码。第二句是往一个List中添加了当前的ViewHolder
@Override
public boolean animateRemove(final RecyclerView.ViewHolder holder) {
    resetAnimation(holder);
    mPendingRemovals.add(holder);
    return true;
}
  • animateAdd() 初始化Item的动画状态,和animateRemove()动画差不多
@Override
public boolean animateAdd(final RecyclerView.ViewHolder holder) {
    resetAnimation(holder);
    holder.itemView.setAlpha(0);
    mPendingAdditions.add(holder);
    return true;
}
  • animateMove() 把目标位置和未执行操作的当前位置差值计算出来,把Item位移到未操作前的位置

方法里面有5个参数,分别是要移动的ViewHolder、起始x,y值、目标x,y值

@Override
public boolean animateMove(final RecyclerView.ViewHolder holder, int fromX, int fromY,
        int toX, int toY) {
    . . .
    fromX += (int) holder.itemView.getTranslationX();
    fromY += (int) holder.itemView.getTranslationY();
    resetAnimation(holder);
    int deltaX = toX - fromX;
    int deltaY = toY - fromY;
    . . .
    mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
    return true;
}
  • animateChange() 里面有一个判断:如果是同一个ViewHolder则直接调用animatoMove(),否则在内部多记录了一个alpha的值
@Override
public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder,int fromX, int fromY, int toX, int toY) {
    if (oldHolder == newHolder) {
        . . .
        return animateMove(oldHolder, fromX, fromY, toX, toY);
    }
    . . .
    if (newHolder != null) {
        // carry over translation values
        resetAnimation(newHolder);
        newHolder.itemView.setTranslationX(-deltaX);
        newHolder.itemView.setTranslationY(-deltaY);
        newHolder.itemView.setAlpha(0);
    }
    mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
    return true;
}
  • runPendingAnimations() 首先判断List(Pending)中是否存在待处理动画,如果不存在的话就退出,如果存在就开始依次执行动画
@Override
public void runPendingAnimations() {
    . . .
    if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
        // nothing to animate
        return;
    }
    // First, remove stuff
    for (RecyclerView.ViewHolder holder : mPendingRemovals) {
        animateRemoveImpl(holder);
    }
    . . .
    // Next, move stuff
    if (movesPending) {
        . . .
        
    }
    // Next, change stuff, to run in parallel with move animations
    if (changesPending) {
        . . .
    }
    // Next, add stuff
    if (additionsPending) {
        . . .
    }
}
  • endAnimation() 取消指定item的动画,然后把List中(待处理/等待运行动画相关数据和正在运行动画的ViewHolder)里面的ViewHolder的都移除掉
  • endAnimations() 循环把List里面的ViewHolder都移除掉,然后把调cancelAll()方法把所有正在执行的属性动画停止
  • isRunning() 通过判断List是否为空,就会知道是否有动画需要执行或者正在执行

下一篇介绍实现拖拽和侧滑的ItemTounchHelper