1. 程式人生 > 其它 >Android UI:RecyclerView 懸浮Item實現

Android UI:RecyclerView 懸浮Item實現

演示效果

思路

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 } }