推荐页核心 UI 实现逻辑说明

推荐页核心UI实现逻辑说明

kotlin 复制代码
xxxScreen.kt:


@Composable
fun xxxxScreen(){
//  padding 调用封装的方法 getItemPadding
 xxxxData.TYPE_TITLE_MORE -> {
                    NavigationHeader(
                        item.title ?: "",
                        modifier = Modifier.padding(item.getItemPadding())
                    ) {
                       
                    }
                }

                xxxxData.TYPE_SONG_FOLDER_SUPPLIER_STATION -> {
                    CovertArt(
                        title = item.card?.title ?: "",
                        model = item.card?.cover ?: "",
                        modifier = Modifier.padding(item.getItemPadding())
                    ) {
                      
                    }
                }

}

@Composable
fun xxxxCoent(){

// 跨度调用的是 getxxxxGridItemSpan方法
 SimpleDataScreen(
        loadState = loadState,
        dataIsEmpty = list.isEmpty(),
        onRetry = { currentFetchData() }
    ) {
        LazyVerticalGrid(
            columns = GridCells.Fixed(COLUM_COUNT_TEN),
            horizontalArrangement = Arrangement.spacedBy(appDimens.spacing.spacingXSmall),
            verticalArrangement = Arrangement.spacedBy(0.dp),
            contentPadding = PaddingValues(
                start = DimensionKeyTokens.ContentLayoutSideLayoutLeftPadding.value,
                top = appDimens.spacing.spacing7xLarge,
                end = DimensionKeyTokens.ContentLayoutSideLayoutRightPadding.value,
                bottom = appDimens.spacing.spacing5xLarge
            )
        ) {
            itemsIndexed(
                items = list,
                span = { index, item ->
                    getxxxxxGridItemSpan(bannerWidgetStyle, item)
                }
            ) { index, item ->
                content(index, item)
            }
        }
    }
	
	
}



 xxxxxData.TYPE_TITLE_MORE -> {
                    NavigationHeader(
                        item.title ?: "",
                        modifier = Modifier.padding(item.getItemPadding())
                    ) {
                        EventTrack.General.trackMore()
                        routerToDestination(
                            uriStr = item.routerUrl ?: "",
                            parcelable = item.localAlbumParcelizeData
                        )
                    }
                }

                xxxxData.TYPE_SONG_FOLDER_SUPPLIER_STATION -> {
                    CovertArt(
                        title = item.card?.title ?: "",
                        model = item.card?.cover ?: "",
                        modifier = Modifier.padding(item.getItemPadding())
                    ) {
                        "CovertArt".iLog(TAG)
                        EventTrack.SongFoldClick.track(
                            name = item.card?.title,
                            id = item.card?.id
                        )
                        routerToDestination(
                            uriStr = item.routerUrl ?: "",
                            parcelable = item.localAlbumParcelizeData
                        )
                    }
                }
				
				
	


@Composable
fun xxxxxData.getItemPadding(): PaddingValues {
    return when (this.type) {
        xxxxData.TYPE_CARD -> PaddingValues(bottom = appDimens.spacing.spacing5xLarge)
        xxxxxData.TYPE_TITLE_MORE -> PaddingValues(bottom = appDimens.spacing.spacingMedium)
        xxxxData.TYPE_SONG_FOLDER_SUPPLIER_STATION -> PaddingValues(bottom = appDimens.spacing.spacing5xLarge)
        xxxxData.TYPE_SONG_LIST_TITLE -> PaddingValues(bottom = appDimens.spacing.spacingMedium)
        xxxxData.TYPE_SONG_ITEM -> PaddingValues(bottom = 0.dp)
        else -> PaddingValues(0.dp)
    }
}

private fun getxxxxxGridItemSpan(bannerWidgetStyle: Int?, item: xxxxxData): GridItemSpan {
    return when (item.type) {
        xxxxData.TYPE_SONG_FOLDER_SUPPLIER_STATION -> {
            GridItemSpan(COLUM_COUNT_TWO)
        }

        xxxxData.TYPE_SONG_ITEM -> {
            GridItemSpan(COLUM_COUNT_FIVE)
        }

        xxxxData.TYPE_BANNER -> {
            if (bannerWidgetStyle == 2) {
                GridItemSpan(COLUM_COUNT_FIVE)
            } else {
                GridItemSpan(COLUM_COUNT_TEN)
            }
        }

        else -> {
            GridItemSpan(COLUM_COUNT_TEN)
        }
    }
}

1. 核心布局架构:分层治理

代码划分为三个互不干扰的逻辑层:

  • 容器层 (xxxxContent):负责整体的加载流(Loading/Success/Error/Empty)和网格的基础框架
  • 规则层 (getxxxxxGridItemSpan):负责空间分配,决定Item占列数,不关心具体UI样式
  • 表现层 (xxxxScreen):负责具体样式,通过when判断类型并渲染对应的Composable组件

2. 关键函数深度解析

A. getxxxxxGridItemSpan:动态网格跨度控制

  • 逻辑:网格总列数为COLUM_COUNT_TEN (10列)
  • 计算公式:
    • TYPE_SONG_ITEM 占5份 → 一行显示2个
    • TYPE_SONG_FOLDER_SUPPLIER_STATION 占2份 → 一行显示5个
    • TYPE_BANNER 根据bannerWidgetStyle切换:Style为2占一半(一行两个),否则占满全屏
  • 好处:布局比例在函数内清晰可见,无需在UI代码中查找配置

B.xxxxxData.getItemPadding():业务感知间距

扩展函数,赋予数据对象"自我描述间距"的能力

  • 原理:根据xxxxxxData的类型,自动返回对应的PaddingValues
  • 规范化:避免Modifier.padding()写死魔法数值,保证同类组件间距完全统一

C. SimpleDataScreen:一站式状态包装

  • 功能:内置loadState监听,数据加载中显示Loading,加载失败显示重试按钮并触发onRetry
  • 优势:开发者只需关注LazyVerticalGrid内的内容,无需重复编写状态页逻辑

3. 开发规范与建议

新增业务类型(如TYPE_VIDEO)执行步骤:

  1. 定义间距:在getItemPadding()扩展函数中添加该类型的Padding规范
  2. 定义跨度:在getxxxxGridItemSpan中定义占列数(占满全屏赋值COLUM_COUNT_TEN)
  3. 编写UI:在xxxxScreen的when分支里,调用对应组件,并传入item.getItemPadding()

总结

配置化与扩展函数实现网格布局规则与UI渲染彻底解耦,遵循"改一处,动全身"原则,修改统一规则即可同步全页面样式,降低维护成本与出错率,完成从"写死代码"到"编写协议"的思维转变。

相关推荐
涵涵(互关)3 小时前
Naive-ui树型选择器只显示根节点
前端·ui·vue
测试员周周6 小时前
【Appium 系列】第13节-混合测试执行器 — API + UI 的协同执行
开发语言·人工智能·python·功能测试·ui·appium·pytest
莽夫搞战术7 小时前
【Google Stitch】AI原生画布重新定义设计,让想法变成可交互界面
前端·人工智能·ui
ZC跨境爬虫10 小时前
跟着 MDN 学CSS day_3:(为一个传记页面添加样式)
前端·javascript·css·ui·音视频·html5
UI设计兰亭妙微12 小时前
兰亭妙微|打破色彩对比度迷思:UI设计公司中的无障碍设计灵活之道
ui·b端界面设计·高端网站设计
轻口味13 小时前
HarmonyOS 6.1 全栈实战录 - 14 渲染树透镜:FrameNode 渲染状态感知与高性能 UI 调优实战
ui·华为·harmonyos
ZC跨境爬虫15 小时前
跟着 MDN 学CSS day_5:掌握属性选择器的存否匹配与子字符串匹配
前端·javascript·css·ui·html
ZC跨境爬虫15 小时前
模块化烹饪小程序开发日记 Day5:(后端Flask接口开发与AI智能解析菜谱的实现)
前端·人工智能·后端·python·ui·flask
薛定猫AI1 天前
【深度解析】Gemini Omni 多模态生成与 Agent 化创作工作流:从视频编辑到 UI 生成的技术演进
人工智能·ui·音视频
赏金术士1 天前
第七章:状态管理实战与架构总结
android·ui·kotlin·compose