问题描述
如何使用Itemdecoration为Android Recyclerview做双层粘性标头?我有3种类型的视图:2个标题和1个项目(子项)视图。我需要类似link
的功能dataBinding.recyclerView.addItemdecoration(HeaderItemdecoration(dataBinding.recyclerView) { itemPosition ->
if (itemPosition >= 0 && itemPosition < dataBinding.recyclerView.adapter!!.itemCount) {
val viewType = dataBinding.recyclerView.adapter!!.getItemViewType(itemPosition)
viewType == VIEW_TYPE_HEADER_1
} else false
})
这是我的HeaderItemdecoration类
class HeaderItemdecoration(parent: RecyclerView,private val shouldFadeOutHeader: Boolean = false,private val isHeader: (itemPosition: Int) -> Boolean) : RecyclerView.Itemdecoration() {
private var currentHeader: Pair<Int,RecyclerView.ViewHolder>? = null
init {
parent.adapter?.registeradapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
// clear saved header as it can be outdated Now
currentHeader = null
}
})
parent.doOnEachNextLayout {
// clear saved layout as it may need layout update
currentHeader = null
}
// handle click on sticky header
parent.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
override fun onInterceptTouchEvent(
recyclerView: RecyclerView,motionEvent: MotionEvent
): Boolean {
return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
motionEvent.y <= currentHeader?.second?.itemView?.bottom ?: 0
} else false
}
})
}
override fun onDrawOver(c: Canvas,parent: RecyclerView,state: RecyclerView.State) {
super.onDrawOver(c,parent,state)
//val topChild = parent.getChildAt(0) ?: return
val topChild = parent.findChildViewUnder(
parent.paddingLeft.toFloat(),parent.paddingTop.toFloat() /*+ (currentHeader?.second?.itemView?.height ?: 0 )*/
) ?: return
val topChildPosition = parent.getChildAdapterPosition(topChild)
if (topChildPosition == RecyclerView.NO_POSITION) {
return
}
val headerView = getHeaderViewForItem(topChildPosition,parent) ?: return
val contactPoint = headerView.bottom + parent.paddingTop
val childInContact = getChildInContact(parent,contactPoint) ?: return
if (isHeader(parent.getChildAdapterPosition(childInContact))) {
moveHeader(c,headerView,childInContact,parent.paddingTop)
return
}
drawHeader(c,parent.paddingTop)
}
private fun getHeaderViewForItem(itemPosition: Int,parent: RecyclerView): View? {
if (parent.adapter == null) {
return null
}
val headerPosition = getHeaderPositionForItem(itemPosition)
if (headerPosition == RecyclerView.NO_POSITION) return null
val headerType = parent.adapter?.getItemViewType(headerPosition) ?: return null
// if match reuse viewHolder
if (currentHeader?.first == headerPosition && currentHeader?.second?.itemViewType == headerType) {
return currentHeader?.second?.itemView
}
val headerHolder = parent.adapter?.createViewHolder(parent,headerType)
if (headerHolder != null) {
parent.adapter?.onBindViewHolder(headerHolder,headerPosition)
fixLayoutSize(parent,headerHolder.itemView)
// save for next draw
currentHeader = headerPosition to headerHolder
}
return headerHolder?.itemView
}
private fun drawHeader(c: Canvas,header: View,paddingTop: Int) {
c.save()
c.translate(0f,paddingTop.toFloat())
header.draw(c)
c.restore()
}
private fun moveHeader(c: Canvas,currentHeader: View,nextHeader: View,paddingTop: Int) {
c.save()
if (!shouldFadeOutHeader) {
c.clipRect(0,paddingTop,c.width,paddingTop + currentHeader.height)
} else {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
c.saveLayerAlpha(
RectF(0f,0f,c.width.toFloat(),c.height.toFloat()),(((nextHeader.top - paddingTop) / nextHeader.height.toFloat()) * 255).toInt()
)
} else {
c.saveLayerAlpha(
0f,c.height.toFloat(),(((nextHeader.top - paddingTop) / nextHeader.height.toFloat()) * 255).toInt(),Canvas.ALL_SAVE_FLAG
)
}
}
c.translate(0f,(nextHeader.top - currentHeader.height).toFloat() /*+ paddingToP*/)
currentHeader.draw(c)
if (shouldFadeOutHeader) {
c.restore()
}
c.restore()
}
private fun getChildInContact(parent: RecyclerView,contactPoint: Int): View? {
var childInContact: View? = null
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
val mBounds = Rect()
parent.getDecoratedBoundsWithMargins(child,mBounds)
if (mBounds.bottom > contactPoint) {
if (mBounds.top <= contactPoint) {
// This child overlaps the contactPoint
childInContact = child
break
}
}
}
return childInContact
}
/**
* Properly measures and layouts the top sticky header.
*
* @param parent ViewGroup: RecyclerView in this case.
*/
private fun fixLayoutSize(parent: ViewGroup,view: View) {
// Specs for parent (RecyclerView)
val widthSpec = View.MeasureSpec.makeMeasureSpec(parent.width,View.MeasureSpec.EXACTLY)
val heightSpec =
View.MeasureSpec.makeMeasureSpec(parent.height,View.MeasureSpec.UNSPECIFIED)
// Specs for children (headers)
val childWidthSpec = ViewGroup.getChildMeasureSpec(
widthSpec,parent.paddingLeft + parent.paddingRight,view.layoutParams.width
)
val childHeightSpec = ViewGroup.getChildMeasureSpec(
heightSpec,parent.paddingTop + parent.paddingBottom,view.layoutParams.height
)
view.measure(childWidthSpec,childHeightSpec)
view.layout(0,view.measuredWidth,view.measuredHeight)
}
private fun getHeaderPositionForItem(itemPosition: Int): Int {
var headerPosition = RecyclerView.NO_POSITION
var currentPosition = itemPosition
do {
if (isHeader(currentPosition)) {
headerPosition = currentPosition
break
}
currentPosition -= 1
} while (currentPosition >= 0)
return headerPosition
}
}
inline fun View.doOnEachNextLayout(crossinline action: (view: View) -> Unit) {
addOnLayoutchangelistener { view,_,_ ->
action(
view
)
}
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)