Android UI:RecyclerView 懸浮Item實現
阿新 • • 發佈:2022-03-26
演示效果
思路
RecyclerView外包裹一個FrameLayout,通過adapter建立懸浮的Item,將其覆蓋在RecyclerView上面,
再通過監聽RecyclerView的滾動,動態改變懸浮item的translationY,實現懸浮。
使用
floatRvItemContainer.showItemFloat = {
it % 8 == 0
}
floatRvItemContainer.observe()
實現
package com.nevermore.floatbar
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import androidx.core.view.doOnNextLayout
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
/**
* @author xuchuanting
* Create on 2021/4/14 17:17
*/
class FloatRvItemContainer : FrameLayout, FloatItemContract {
constructor(context: Context) : this(context, null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(
context,
attributeSet,
defStyleAttr
)
private var recyclerView: RecyclerView? = null
private var floatItemView: View? = null
private var viewHolder: RecyclerView.ViewHolder? = null
// fun showItemFloat(position: Int): Boolean
var showItemFloat: ((position: Int) -> Boolean)? = null
var adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? = null
override fun onFinishInflate() {
super.onFinishInflate()
val view = getChildAt(0)
if (view is RecyclerView) {
recyclerView = view
}
}
var floatItemHeight = 0
private var currentFloatItemPoi = -1
fun observe() {
recyclerView?.let {
this.adapter = it.adapter
val firstFloatPosition = findNexFloatItemPosition(0)
ensureFloatItem(firstFloatPosition)
bindFloatItem(firstFloatPosition)
}
recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = recyclerView.layoutManager
if (layoutManager is LinearLayoutManager) {
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (showItemFloat(firstVisibleItemPosition)) {
updateFloatItem(firstVisibleItemPosition)
}
val nextItemPosition = firstVisibleItemPosition + 1
val itemCount = adapter?.itemCount ?: 0
if (nextItemPosition < itemCount) {
if (showItemFloat(nextItemPosition)) {
val preFloatItemPosition: Int =
findPreFloatItemPosition(firstVisibleItemPosition)
updateFloatItem(preFloatItemPosition)
val nextItemView = layoutManager.findViewByPosition(nextItemPosition)
nextItemView?.apply {
val top = nextItemView.top
if (top < floatItemHeight) {
onRequestFloatItemTranslationY((top - floatItemHeight).toFloat())
}
}
} else {
onRequestFloatItemTranslationY(0f)
}
}
}
}
})
}
private fun updateFloatItem(position: Int) {
if (currentFloatItemPoi != position) {
ensureFloatItem(position)
bindFloatItem(position)
currentFloatItemPoi = position
}
}
/**
* position 及之前該懸浮的item位置
*/
private fun findPreFloatItemPosition(position: Int): Int {
for (index in position downTo 0) {
if (showItemFloat(index)) {
return index
}
}
return 0
}
private fun findNexFloatItemPosition(position: Int): Int {
val itemCount = adapter?.itemCount ?: 0
for (index in position until itemCount) {
if (showItemFloat(index)) {
return index
}
}
return 0
}
private fun bindFloatItem(position: Int) {
adapter?.apply {
viewHolder?.let {
bindViewHolder(it, position)
}
}
}
private fun ensureFloatItem(position: Int) {
if (floatItemView == null) {
adapter?.let {
viewHolder =
it.createViewHolder(this@FloatRvItemContainer, it.getItemViewType(position))
floatItemView = viewHolder?.itemView?.apply {
this@apply.doOnNextLayout {
floatItemHeight = it.measuredHeight
}
}
addView(floatItemView)
}
}
}
override fun showItemFloat(position: Int): Boolean {
return showItemFloat?.invoke(position) ?: false
}
override fun onRequestFloatItemTranslationY(translationY: Float) {
floatItemView?.translationY = translationY
}
}