RecyclerView的进阶使用(分割线、拖拽、动画)

前言

使用RecyclerView已经很久了,它相对于ListView和GridView方便很多,使用LayoutManager等类可以给它的使用带来很大的便利。但是后面发现,使用RecyclerView的功能只是那么一小点,也就是都它的基本使用,所以最近对它进一步学习和使用。

添加分割线

RecyclerView默认是没有为我们添加分割线的,这得需要通过自己去实现,虽然看起来麻烦,但是反过来给我们带来更大的灵活性。

思路:

  1. 继承于ItemDecoration类,它是RecyclerView的一个内部类
  2. 定义一个Paint对象,可用于绘制,如果需要的话可提供一些方法供用户设置Paint的属性
  3. 分割线厚度
  4. 重写getItemOffsets方法,此方法可以定义分割线的大小
  5. 重写onDraw方法,此方法在itemView绘制之前调用,所以需要注意绘制后被ItemView覆盖
  6. onDrawOver,此方法在ItemView绘制后调用,可以在此方法上绘制一些覆盖物等的效果

分割线代码:

lass ItemDivider : RecyclerView.ItemDecoration() {

    private val mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var mLayoutManager: RecyclerView.LayoutManager? = null

    var mDividerWidth = 1

    init {
        mPaint.style = Paint.Style.FILL
        mPaint.color = 0xff000000.toInt()
    }

    /**
     * 指定分割线的大小
     */
    override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
        super.getItemOffsets(outRect, view, parent, state)

        if (mLayoutManager == null) {
            mLayoutManager = parent?.layoutManager
        }
        
	//根据LayoutManager的类型对应设置分割线
        if (mLayoutManager is LinearLayoutManager) {

            val orientation = (mLayoutManager as LinearLayoutManager).orientation

            if (orientation == LinearLayoutManager.VERTICAL) {
                outRect?.bottom = mDividerWidth
            } else {
                outRect?.right = mDividerWidth
            }

            if (mLayoutManager is GridLayoutManager) {

                val layoutParams: GridLayoutManager.LayoutParams? = view?.layoutParams as GridLayoutManager.LayoutParams

                if (orientation == LinearLayoutManager.VERTICAL && layoutParams?.spanIndex!! > 0) {
                    outRect?.left = mDividerWidth
                } else if (orientation == LinearLayoutManager.HORIZONTAL && layoutParams?.spanIndex!! > 0) {
                    outRect?.top = mDividerWidth
                }

            }
        }

    }

    /**
     * 在ItemView绘制之前调用
     */
    override fun onDraw(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        super.onDraw(c, parent, state)

        val childCount = parent?.childCount

        for (i in 0 until childCount!!) {
            val child = parent.getChildAt(i)
            /*竖直分割线*/
            val params = child.layoutParams as RecyclerView.LayoutParams
            val left1 = child.right + params.rightMargin
            val right1 = left1 + mDividerWidth
            val top1 = child.top - params.topMargin
            val bottom1 = child.bottom + params.bottomMargin
            c?.drawRect(left1.toFloat(), top1.toFloat(), right1.toFloat(), bottom1.toFloat(), mPaint)

            /*水平分割线*/
            val left2 = child.left - params.leftMargin
            val right2 = child.right + params.rightMargin
            val top2 = child.bottom + params.bottomMargin
            val bottom2 = top2 + mDividerWidth
            c?.drawRect(left2.toFloat(), top2.toFloat(), right2.toFloat(), bottom2.toFloat(), mPaint)
        }
    }

    /**
     * 在ItemView绘制后调用
     */
    override fun onDrawOver(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        super.onDrawOver(c, parent, state)
    }

}

使用:

mRvShowDivider.addItemDecoration(ItemDivider())

RecyclerView的进阶使用(分割线、拖拽、动画)_第1张图片

添加拖拽功能

RecyclerView提供了ItemTouchHelper类,给对ItemView的操作带来挺大的方便,它有一个内部类Callback,当对ItemView的拖拽时会进行相对应的回调。

# Callback几个重要的方法
//拖动ItemView时的回调
//viewHolder参数表示被用户拖拽的ItemView对应的ViewHolder
//target参数表示用户拖拽停止的ItemView对应的ViewHolder
public abstract boolean onMove(RecyclerView recyclerView,
                ViewHolder viewHolder, ViewHolder target);

//用户将某个ItemView滑出屏幕时回调
//direction表示被滑出的ItemView对应的Position
public abstract void onSwiped(ViewHolder viewHolder, int direction);

//返回(空闲,滑动 , 拖拽)标识的结合,也就是各种操作所支持的方向范围
public abstract int getMovementFlags(RecyclerView recyclerView,
                ViewHolder viewHolder);

//返回值表示是否支持用户滑动
public boolean isItemViewSwipeEnabled() {
            return true;
}

//返回值表示是否支持用户长按后拖拽
public boolean isLongPressDragEnabled() {
            return true;
}

实现:

继承于ItemTouchHelper.Callback,实现关键的方法

override fun getMovementFlags(recyclerView: RecyclerView?, viewHolder: RecyclerView.ViewHolder?): Int {

        var dragFlags = 0
        var swipeFlags = 0

        val manager = recyclerView?.layoutManager

        if (manager is GridLayoutManager || manager is StaggeredGridLayoutManager) {
        //支持上下左右的拖拽
            dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or
                    ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
        } else {
        //如果是线性的则只支持上下防线
            dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        }
        //左右滑动删除
        swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END 
		//如果标识为0的话则表示不支持对应的操作
        return makeMovementFlags(dragFlags, swipeFlags)
    }
//长按拖拽回调,注意要相同的viewType才可进行交换
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        if (viewHolder.itemViewType == target.itemViewType) {
            var fromPosition = viewHolder.adapterPosition
            var toPosition = target.adapterPosition
 			//对应Adapter的回调,可供Adapter进行数据的交换
            if (fromPosition < toPosition) {
                for (i in fromPosition until toPosition) {
                    mAdapter.onItemMove(i, i + 1)
                }
            } else {
                for (i in fromPosition downTo toPosition + 1) {
                    mAdapter.onItemMove(i, i - 1)
                }
            }
			//刷新数据交换后的列表视图
            mAdapter.notifyItemMoved(fromPosition, toPosition)
            return true
        }
        return false
    }
//滑出屏幕(滑动删除)后的回调
override fun onSwiped(viewHolder: RecyclerView.ViewHolder?, direction: Int) {
		//回调Adapter的删除回调,可供Adapter删除数据
        mAdapter.onItemRemove(viewHolder?.adapterPosition!!)
        mAdapter.notifyItemRemoved(viewHolder.adapterPosition)
    }

使用:

val itemTouchHelper = ItemTouchHelper(ItemTouchCallbakImpl(adapter))
itemTouchHelper.attachToRecyclerView(mRvShowDivider)

ItemView动画

思路:
使用的方法较粗暴,通过查看提供的DefaultItemAnimator类,复制该类的代码,然后改写对应的方法:

  • itemView移除动画效果:改写animateRemoveImpl方法里面动画相关的代码
  • itemView添加动画效果:改写animateAdd和animateAddImpl里面的动画相关代码
  • itemView改变动画效果:改写animateChange和animateChangeImpl里面的动画相关代码
  • 最后要在Adapter中添加对应的操作方法

修改的代码

#关键代码
//移除itemView
 private fun animateRemoveImpl(holder: RecyclerView.ViewHolder) {
        val view = holder.itemView
        val animation = view.animate()
        mRemoveAnimations.add(holder)
//        animation.setDuration(getRemoveDuration()).alpha(0f).
  animation
  .setDuration(removeDuration)
  //从左向右滑出
  .translationX(view.width.toFloat()).
                setListener(
                        object : AnimatorListenerAdapter() {
                            override fun onAnimationStart(animator: Animator) {
                                dispatchRemoveStarting(holder)
                            }

                            override fun onAnimationEnd(animator: Animator) {
                                animation.setListener(null)
//                        view.alpha = 1f
                                ViewCompat.setTranslationX(view, 0f)//还原
                                dispatchRemoveFinished(holder)
                                mRemoveAnimations.remove(holder)
                                dispatchFinishedWhenDone()
                            }
                        }).start()
    }

//添加itemView
 override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean {
        resetAnimation(holder)
//        holder.itemView.alpha = 0f
		//初始化从上方
        ViewCompat.setTranslationY(holder.itemView, (-holder.itemView.height).toFloat())
        mPendingAdditions.add(holder)
        return true
    }

    fun animateAddImpl(holder: RecyclerView.ViewHolder) {
        val view = holder.itemView
        val animation = view.animate()
        mAddAnimations.add(holder)
//        animation.alpha(1f).setDuration(getAddDuration())
		//改为同上到下进入
        animation.translationY(0f).setDuration(addDuration)
                .setListener(object : AnimatorListenerAdapter() {
                   ......
                }).start()
    }

//Item数据改变
 fun animateChangeImpl(changeInfo: ChangeInfo) {
       ......
//            oldViewAnim.alpha(0f)
			//改为从右向左
            oldViewAnim.translationX(-view.width.toFloat())
                    .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationStart(animator: Animator) {
                    dispatchChangeStarting(changeInfo.oldHolder, true)
                }

                override fun onAnimationEnd(animator: Animator) {
                    oldViewAnim.setListener(null)
                    view.alpha = 1f
                    view.translationX = 0f
                    view.translationY = 0f
                    dispatchChangeFinished(changeInfo.oldHolder, true)
                    mChangeAnimations.remove(changeInfo.oldHolder!!)
                    dispatchFinishedWhenDone()
                }
            }).start()
        }
      ......
    }

Adapter添加的方法:

  /**
     * 添加条目
     */
    fun addItem(position: Int, data: String) {
        mDatas.add(position, data)
        notifyItemInserted(position)
    }

    /**
     * 删除指定条目
     */
    fun removeItem(position: Int) {
        mDatas.removeAt(position)
        notifyItemRemoved(position)
    }

    /**
     * 更换指定条目
     */
    fun changeItem(position: Int, data: String) {
        mDatas.set(position, data)
        notifyItemChanged(position)
    }

RecyclerView的进阶使用(分割线、拖拽、动画)_第2张图片 RecyclerView的进阶使用(分割线、拖拽、动画)_第3张图片 RecyclerView的进阶使用(分割线、拖拽、动画)_第4张图片

以上是RecyclerView的几种相对进阶的使用,它还有多种更炫的使用。
Demo地址:AnimationCustomView

你可能感兴趣的:(Android)