推荐页核心 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渲染彻底解耦,遵循"改一处,动全身"原则,修改统一规则即可同步全页面样式,降低维护成本与出错率,完成从"写死代码"到"编写协议"的思维转变。

相关推荐
LabVIEW开发4 小时前
LabVIEW图标编辑器文字模糊现象
ui·编辑器·labview·labview知识·labview功能·labview程序
兰亭妙微UI设计公司1 天前
兰亭妙微B端界面设计分享:Vantary海事无人系统任务平台UI/UX设计解析
ui·ux
伊玛目的门徒1 天前
deepseek harness DSH 安装皮肤脚手架(dsh‑client‑ui‑skin‑center)踩坑完整记录
ui·插件·皮肤·harness·dsh
zhchyun20082 天前
【Unity UI 进阶】仿 Element UI 打造企业级 Unity UI 组件库(05)
ui·unity·游戏引擎
tanghonghanhaoli2 天前
【图书翻译】有意义的游戏设计:桌面游戏的方法论与心理学(第1、2章)
前端·游戏·ui
bugcome_com2 天前
Avalonia UI 样式进阶实战:外置样式 + MVVM 主题切换 + 样式优先级全解析
ui
李高钢3 天前
【WPF】高级 UI 与性能优化实战:从卡顿到丝滑
ui·性能优化·wpf
MOONICK3 天前
QT的UI开发框架
qt·ui
界面开发小八哥4 天前
界面控件DevExpress Blazor v26.1新版亮点 - Filter Builder等功能升级
ui·界面控件·blazor·devexpress·ui开发
兰亭妙微UI设计公司4 天前
兰亭妙微UI设计:Neemo Project 企业AI项目管理后台全案运营价值解析
人工智能·ui