逼格特性:
ItemDecoration
实现touch
事件, 以及Drawable
的状态效果子View
她
, 什么位置,需要什么悬停xml
即可.自绘分割线通常需要重写以下方法:
override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
checkOverDecoration(parent)
overViewHolder?.let {
if (!overDecorationRect.isEmpty) {
addHoverView(it.itemView)
if (it.itemView.parent != null) {
hoverCallback?.drawOverDecoration?.invoke(canvas, paint, it, overDecorationRect)
}
}
}
}
核心方法
思路: 没次回调draw
时候, 遍历RecyclerView
界面上, 所有的child
, 并且拿到child
对应的adapterPosition
,
然后通过adapterPosition
判断是否需要分割线
如果不需要:
继续往前查找, 直到找到需要分割线位置的child
, 拿到adapterPosition
, 或者真的没有child
需要分割线, 则返回 -1
有了adapterPosition
就可以通过adapter
创建对应的view
, 这个时候, 调用View.Draw
方法, 就可以绘制出分割线.
如果不需要 touch
事件, 到这里就实现了, 自定义View
的分割线了.
往下查找, 判断紧挨着的下一个child
, 是否需要分割线
, 用来实现上推效果
, 可以通过 itemView.top
, 判断是否需要 上推
.
/**
* 核心方法, 用来实时监测界面上 需要浮动的 分割线.
* */
internal fun checkOverDecoration(parent: RecyclerView) {
childViewHolder(parent, 0)?.let { viewHolder ->
var firstChildAdapterPosition = viewHolder.adapterPosition
if (firstChildAdapterPosition != RecyclerView.NO_POSITION) {
parent.adapter?.let { adapter ->
hoverCallback?.let { callback ->
var firstChildHaveOver = callback.haveOverDecoration.invoke(firstChildAdapterPosition)
if (!firstChildHaveOver) {
//第一个child没有分割线, 查找之前最近有分割线的position
val findOverPrePosition = findOverPrePosition(firstChildAdapterPosition)
if (findOverPrePosition != RecyclerView.NO_POSITION) {
//找到了最近的分割线
firstChildHaveOver = true
firstChildAdapterPosition = findOverPrePosition
}
}
if (firstChildHaveOver) {
val overStartPosition = findOverStartPosition(adapter, firstChildAdapterPosition)
if (overStartPosition == RecyclerView.NO_POSITION) {
clearOverDecoration()
return
}
//创建第一个位置的child 需要分割线
val firstViewHolder =
callback.createDecorationOverView.invoke(
parent,
adapter,
overStartPosition
)
val overView = firstViewHolder.itemView
tempRect.set(overView.left, overView.top, overView.right, overView.bottom)
val nextViewHolder = childViewHolder(parent, findGridNextChildIndex())
if (nextViewHolder != null) {
//紧挨着的下一个child也有分割线, 监测是否需要上推
if (callback.haveOverDecoration.invoke(nextViewHolder.adapterPosition) &&
!callback.isOverDecorationSame.invoke(
adapter,
firstChildAdapterPosition,
nextViewHolder.adapterPosition
)
) {
//不同的分割线, 实现上推效果
if (nextViewHolder.itemView.top < overDecorationRect.height()) {
tempRect.offsetTo(
0,
nextViewHolder.itemView.top - overDecorationRect.height()
)
}
}
}
if (overStartPosition == firstChildAdapterPosition && viewHolder.itemView.top == 0) {
//第一个child, 正好是 分割线的开始位置
clearOverDecoration()
} else {
if (overAdapterPosition != overStartPosition) {
clearOverDecoration()
overViewHolder = firstViewHolder
overDecorationRect.set(tempRect)
overAdapterPosition = overStartPosition
} else if (overDecorationRect != tempRect) {
overDecorationRect.set(tempRect)
}
}
} else {
//当前位置不需要分割线
clearOverDecoration()
}
}
}
}
}
}
为了支持Touch/Drawable状态的妥协方法
由于分割线是draw
出来的, 所以 touch
事件是传递不到分割线上的. 即时你通过Rect
和touch x,y
来判断, 也只能实现一部分效果.
Drawable
就不好控制了, 里面的子View
, 就更不好控制了.
所以…
我这里用了一个骚操作:
将分割线的view
, 同时attach
到window
上.
/**
* 添加悬浮view 到 Activity, 目的是为了 系统接管 悬浮View的touch事件以及drawable的state
* */
private fun addHoverView(view: View) {
if (view.parent == null) {
windowContent?.addView(
view, 0,
FrameLayout.LayoutParams(overDecorationRect.width(), overDecorationRect.height())
)
}
}
再将touch
事件, 转发到View
到, 就可以完美实现Touch
的控制, 和Drawable
状态的控制.
private val itemTouchListener = object : RecyclerView.SimpleOnItemTouchListener() {
override fun onInterceptTouchEvent(recyclerView: RecyclerView, event: MotionEvent): Boolean {
val action = event.actionMasked
if (action == MotionEvent.ACTION_DOWN) {
isDownInHoverItem = overDecorationRect.contains(event.x.toInt(), event.y.toInt())
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
isDownInHoverItem = false
}
if (isDownInHoverItem) {
onTouchEvent(recyclerView, event)
}
return isDownInHoverItem
}
override fun onTouchEvent(recyclerView: RecyclerView, event: MotionEvent) {
if (isDownInHoverItem) {
overViewHolder?.apply {
//一定要调用dispatchTouchEvent, 否则ViewGroup里面的子View, 不会响应touchEvent
itemView.dispatchTouchEvent(event)
if (itemView is ViewGroup) {
if ((itemView as ViewGroup).onInterceptTouchEvent(event)) {
itemView.onTouchEvent(event)
}
} else {
itemView.onTouchEvent(event)
}
}
}
}
}
极简使用方式
HoverItemDecoration().attachToRecyclerView(this) {
decorationOverLayoutType = {
R.layout.item_text
}
haveOverDecoration = {
overPositionList.contains(it)
}
}
其他部分, 请直接参考源码:
砖厂地址: https://github.com/angcyo/HoverItemDecoration
群内有各(pian)种(ni)各(jin)样(qun)
的大佬,等你来撩.
点此快速加群
请使用QQ扫码加群, 小伙伴们都在等着你哦!
关注我的公众号, 每天都能一起玩耍哦!