使用RecyclerView已经很久了,它相对于ListView和GridView方便很多,使用LayoutManager等类可以给它的使用带来很大的便利。但是后面发现,使用RecyclerView的功能只是那么一小点,也就是都它的基本使用,所以最近对它进一步学习和使用。
RecyclerView默认是没有为我们添加分割线的,这得需要通过自己去实现,虽然看起来麻烦,但是反过来给我们带来更大的灵活性。
思路:
分割线代码:
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提供了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)
思路:
使用的方法较粗暴,通过查看提供的DefaultItemAnimator类,复制该类的代码,然后改写对应的方法:
修改的代码
#关键代码
//移除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的几种相对进阶的使用,它还有多种更炫的使用。
Demo地址:AnimationCustomView