直播網站程式原始碼,FlowLayoutManager 流式佈局

zhibo系統開發發表於2022-12-20

直播網站程式原始碼,FlowLayoutManager 流式佈局

import android.graphics.Rect
import android.util.Log
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
class FlowLayoutManager : RecyclerView.LayoutManager() {
    companion object {
        const val TAG = "FlowLayoutManager2"
    }
    var widthFlow = 0
    var heightFlow = 0
    private var left = 0
    private var top = 0
    private var right = 0
    private var useMaxWidth = 0
    private var verticalScrollOffset = 0
    var totalHeight = 0
        private set
    private var row = Row()
    private val lineRows: MutableList<Row> = mutableListOf()
    private val allItemFrames = SparseArray<Rect>()
    override fun isAutoMeasureEnabled(): Boolean {
        return true
    }
    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        return RecyclerView.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )
    }
    override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) {
        Log.d(TAG, "onLayoutChildren")
        totalHeight = 0
        var cuLineTop = top
        //當前行使用的高度
        var cuLineWidth = 0
        var itemLeft: Int
        var itemTop: Int
        var maxHeightItem = 0
        row = Row()
        lineRows.clear()
        allItemFrames.clear()
        removeAllViews()
        if (itemCount == 0) {
            recycler?.let {
                detachAndScrapAttachedViews(it)
            }
            verticalScrollOffset = 0
            return
        }
        if (childCount == 0 && state?.isPreLayout == true) {
            return
        }
        //onLayoutChildren方法在RecyclerView 初始化時 會執行兩遍
        recycler?.let {
            detachAndScrapAttachedViews(it)
        }
        if (childCount == 0) {
            widthFlow = width
            heightFlow = height
            left = paddingLeft
            right = paddingRight
            top = paddingTop
            useMaxWidth = widthFlow - left - right
        }
        for (i in 0 until itemCount) {
            Log.d(TAG, "index:$i")
            val childAt = recycler?.getViewForPosition(i) ?: continue
            if (View.GONE == childAt.visibility) {
                continue
            }
            measureChildWithMargins(childAt, 0, 0)
            val childWidth = getDecoratedMeasuredWidth(childAt)
            val childHeight = getDecoratedMeasuredHeight(childAt)
            if ((cuLineWidth + childWidth) <= useMaxWidth) {
                itemLeft = left + cuLineWidth
                itemTop = cuLineTop
                val frame = allItemFrames.get(i) ?: Rect()
                frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight)
                allItemFrames.put(i, frame)
                cuLineWidth += childWidth
                maxHeightItem = maxHeightItem.coerceAtLeast(childHeight)
                row.views.add(Item(childHeight, childAt, frame))
                row.cuTop = cuLineTop
                row.maxHeight = maxHeightItem
            } else {
                // 換行
                formatAboveRow()
                cuLineTop += maxHeightItem
                totalHeight += maxHeightItem
                itemTop = cuLineTop
                itemLeft = left
                val frame = allItemFrames.get(i) ?: Rect()
                frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight)
                allItemFrames.put(i, frame)
                cuLineWidth = childWidth
                maxHeightItem = childHeight
                row.views.add(Item(childHeight, childAt, frame))
                row.cuTop = cuLineTop
                row.maxHeight = maxHeightItem
            }
            //不要忘了最後一行進行重新整理下佈局
            if (i == itemCount - 1) {
                formatAboveRow()
                totalHeight += maxHeightItem
            }
        }
        totalHeight = totalHeight.coerceAtLeast(getVerticalSpace())
        Log.d(TAG, "onLayoutChildren totalHeight:$totalHeight")
        fillLayout(state)
    }
    private fun fillLayout(state: RecyclerView.State?) {
        if (state?.isPreLayout == true || itemCount == 0) {
            // 跳過preLayout,preLayout主要用於支援動畫
            return
        }
        //對所有的行資訊進行遍歷
        for (j in 0 until lineRows.size) {
            val row = lineRows[j]
            val views = row.views
            for (i in 0 until views.size) {
                val scrap = views[i].view
                measureChildWithMargins(scrap, 0, 0)
                addView(scrap)
                val frame = views[i].rect
                //將這個item佈局出來
                layoutDecoratedWithMargins(
                    scrap,
                    frame.left ,
                    frame.top - verticalScrollOffset,
                    frame.right ,
                    frame.bottom - verticalScrollOffset
                )
            }
        }
    }
    private fun formatAboveRow() {
        var lineNeedWidth = 0
        val views = row.views
        for (i in 0 until views.size) {
            //計算行高居中
            val item = views[i]
            val view = item.view
            val position = getPosition(view)
            if (allItemFrames[position].top < row.cuTop + (row.maxHeight - item.useHeight) / 2) {
                val frame = allItemFrames[position] ?: Rect()
                frame.set(
                    allItemFrames[position].left,
                    row.cuTop + (row.maxHeight - item.useHeight) / 2,
                    allItemFrames[position].right,
                    row.cuTop + (row.maxHeight - item.useHeight) / 2 + getDecoratedMeasuredHeight(view)
                )
                allItemFrames.put(position,frame)
                item.rect = frame
                views[i] = item
            }
            //計算行寬居中
            lineNeedWidth += (item.rect.right - item.rect.left)
        }
        val off = (useMaxWidth - lineNeedWidth) / 2
        for (item in views) {
            item.rect.left += off
            item.rect.right += off
        }
        lineRows.add(row)
        row = Row()
    }
    override fun canScrollVertically(): Boolean {
        return true
    }
    override fun scrollVerticallyBy(
        dy: Int,
        recycler: RecyclerView.Recycler?,
        state: RecyclerView.State?
    ): Int {
        return super.scrollVerticallyBy(dy, recycler, state)
    }
    private fun getVerticalSpace(): Int {
        return height - paddingBottom - paddingTop
    }
    //行資訊的定義
    class Row(val views: MutableList<Item> = mutableListOf()) {
        //每一行的頭部座標
        var cuTop: Int = 0
        //每一行需要佔據的最大高度
        var maxHeight: Int = 0
    }
    //每個item的定義
    class Item(val useHeight: Int, val view: View, var rect: Rect)
}


以上就是直播網站程式原始碼,FlowLayoutManager 流式佈局, 更多內容歡迎關注之後的文章


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69978258/viewspace-2928686/,如需轉載,請註明出處,否則將追究法律責任。

相關文章