直播网站程序源码,FlowLayoutManager 流式布局
直播网站程序源码,FlowLayoutManager 流式布局
1 | import android.graphics.Rect<br>import android.util.Log<br>import android.util.SparseArray<br>import android.view.View<br>import android.view.ViewGroup<br>import androidx.recyclerview.widget.RecyclerView<br> class FlowLayoutManager : RecyclerView.LayoutManager() {<br> companion object {<br> const val TAG = "FlowLayoutManager2" <br> }<br> var widthFlow = 0<br> var heightFlow = 0<br> private var left = 0<br> private var top = 0<br> private var right = 0<br> private var useMaxWidth = 0<br> private var verticalScrollOffset = 0<br> var totalHeight = 0<br> private set<br> private var row = Row()<br> private val lineRows: MutableList<Row> = mutableListOf()<br> private val allItemFrames = SparseArray<Rect>()<br> override fun isAutoMeasureEnabled(): Boolean {<br> return true<br> }<br> override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {<br> return RecyclerView.LayoutParams(<br> ViewGroup.LayoutParams.WRAP_CONTENT,<br> ViewGroup.LayoutParams.WRAP_CONTENT<br> )<br> }<br> override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) {<br> Log.d(TAG, "onLayoutChildren" )<br> totalHeight = 0<br> var cuLineTop = top<br> //当前行使用的高度<br> var cuLineWidth = 0<br> var itemLeft: Int<br> var itemTop: Int<br> var maxHeightItem = 0<br> row = Row()<br> lineRows.clear()<br> allItemFrames.clear()<br> removeAllViews()<br> if (itemCount == 0) {<br> recycler?.let {<br> detachAndScrapAttachedViews(it)<br> }<br> verticalScrollOffset = 0<br> return<br> }<br> if (childCount == 0 && state?.isPreLayout == true) {<br> return<br> }<br> //onLayoutChildren方法在RecyclerView 初始化时 会执行两遍<br> recycler?.let {<br> detachAndScrapAttachedViews(it)<br> }<br> if (childCount == 0) {<br> widthFlow = width<br> heightFlow = height<br> left = paddingLeft<br> right = paddingRight<br> top = paddingTop<br> useMaxWidth = widthFlow - left - right<br> }<br> for (i in 0 until itemCount) {<br> Log.d(TAG, "index:$i")<br> val childAt = recycler?.getViewForPosition(i) ?: continue<br> if (View.GONE == childAt.visibility) {<br> continue<br> }<br> measureChildWithMargins(childAt, 0, 0)<br> val childWidth = getDecoratedMeasuredWidth(childAt)<br> val childHeight = getDecoratedMeasuredHeight(childAt)<br> if ((cuLineWidth + childWidth) <= useMaxWidth) {<br> itemLeft = left + cuLineWidth<br> itemTop = cuLineTop<br> val frame = allItemFrames.get(i) ?: Rect()<br> frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight)<br> allItemFrames.put(i, frame)<br> cuLineWidth += childWidth<br> maxHeightItem = maxHeightItem.coerceAtLeast(childHeight)<br> row.views.add(Item(childHeight, childAt, frame))<br> row.cuTop = cuLineTop<br> row.maxHeight = maxHeightItem<br> } else {<br> // 换行<br> formatAboveRow()<br> cuLineTop += maxHeightItem<br> totalHeight += maxHeightItem<br> itemTop = cuLineTop<br> itemLeft = left<br> val frame = allItemFrames.get(i) ?: Rect()<br> frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight)<br> allItemFrames.put(i, frame)<br> cuLineWidth = childWidth<br> maxHeightItem = childHeight<br> row.views.add(Item(childHeight, childAt, frame))<br> row.cuTop = cuLineTop<br> row.maxHeight = maxHeightItem<br> }<br> //不要忘了最后一行进行刷新下布局<br> if (i == itemCount - 1) {<br> formatAboveRow()<br> totalHeight += maxHeightItem<br> }<br> }<br> totalHeight = totalHeight.coerceAtLeast(getVerticalSpace())<br> Log.d(TAG, "onLayoutChildren totalHeight:$totalHeight")<br> fillLayout(state)<br> }<br> private fun fillLayout(state: RecyclerView.State?) {<br> if (state?.isPreLayout == true || itemCount == 0) {<br> // 跳过preLayout,preLayout主要用于支持动画<br> return<br> }<br> //对所有的行信息进行遍历<br> for (j in 0 until lineRows.size) {<br> val row = lineRows[j]<br> val views = row.views<br> for (i in 0 until views.size) {<br> val scrap = views[i].view<br> measureChildWithMargins(scrap, 0, 0)<br> addView(scrap)<br> val frame = views[i].rect<br> //将这个item布局出来<br> layoutDecoratedWithMargins(<br> scrap,<br> frame.left ,<br> frame.top - verticalScrollOffset,<br> frame.right ,<br> frame.bottom - verticalScrollOffset<br> )<br> }<br> }<br> }<br> private fun formatAboveRow() {<br> var lineNeedWidth = 0<br> val views = row.views<br> for (i in 0 until views.size) {<br> //计算行高居中<br> val item = views[i]<br> val view = item.view<br> val position = getPosition(view)<br> if (allItemFrames[position].top < row.cuTop + (row.maxHeight - item.useHeight) / 2) {<br> val frame = allItemFrames[position] ?: Rect()<br> frame.set(<br> allItemFrames[position].left,<br> row.cuTop + (row.maxHeight - item.useHeight) / 2,<br> allItemFrames[position].right,<br> row.cuTop + (row.maxHeight - item.useHeight) / 2 + getDecoratedMeasuredHeight(view)<br> )<br> allItemFrames.put(position,frame)<br> item.rect = frame<br> views[i] = item<br> }<br> //计算行宽居中<br> lineNeedWidth += (item.rect.right - item.rect.left)<br> }<br> val off = (useMaxWidth - lineNeedWidth) / 2<br> for (item in views) {<br> item.rect.left += off<br> item.rect.right += off<br> }<br> lineRows.add(row)<br> row = Row()<br> }<br> override fun canScrollVertically(): Boolean {<br> return true<br> }<br> override fun scrollVerticallyBy(<br> dy: Int,<br> recycler: RecyclerView.Recycler?,<br> state: RecyclerView.State?<br> ): Int {<br> return super.scrollVerticallyBy(dy, recycler, state)<br> }<br> private fun getVerticalSpace(): Int {<br> return height - paddingBottom - paddingTop<br> }<br> //行信息的定义<br> class Row(val views: MutableList<Item> = mutableListOf()) {<br> //每一行的头部坐标<br> var cuTop: Int = 0<br> //每一行需要占据的最大高度<br> var maxHeight: Int = 0<br> }<br> //每个item的定义<br> class Item(val useHeight: Int, val view: View, var rect: Rect)<br>} |
以上就是直播网站程序源码,FlowLayoutManager 流式布局, 更多内容欢迎关注之后的文章
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2021-12-20 直播源码网站,菜单栏的横向滑动
2021-12-20 短视频直播系统,简单实现个人主页背景图片和文字颜色的更改
2021-12-20 android短视频开发,实现动态点赞出现的点赞动画