移动开发【免费下载链接】nowinandroidA fully functional Android app built entirely with Kotlin and Jetpack Compose项目地址https://gitcode.com/GitHub_Trending/no/nowinandroid点击查看免费下载本文围绕 Now in AndroidNIA项目中的:core:designsystem模块展开系统讲解该 Android 设计系统模块在模块化架构中的定位、依赖关系、Material 3 主题体系NiaTheme、通用组件库NiaButton、NiaTopAppBar、NiaNavigationBar等、图标体系NiaIcons以及配套的截屏测试与预览机制。读完本文你将掌握如何通过源码理解并复用这套定制化 Material 3 组件 应用主题 图标的设计系统并能在自己的 Compose 项目中落地类似的组件抽象与多主题测试方案。:core:designsystem模块定位什么是设计系统模块在 ModularizationLearningJourney.md 的模块职责表格中对:core:designsystem的描述是Design system which includes Core UI components (many of which are customized Material 3 components), app theme and icons. The design system can be viewed by running theapp-nia-catalogrun configuration.即该模块包含三部分核心资产核心 UI 组件大量基于 Material 3 定制封装的可复用组件应用主题完整的 Material 3 颜色、字体、背景体系应用图标统一的NiaIcons图标集合。与之形成对比的是同层的:core:ui模块后者提供的是组合型 UI 组件与资源如新闻信息流的NewsFeed、NewsResourceCardExpanded因为它依赖数据层并渲染业务模型如 news resources所以与designsystem有本质区别——designsystem是纯 UI 层抽象、不依赖任何业务数据而:core:ui依赖数据层。这一点从 core/ui/README.md 的依赖图中可以看到:core:ui -- :core:designsystem即 UI 模块依赖设计系统模块方向是单向、清晰的。模块依赖图一个独立的叶节点core/designsystem/README.md 给出了该模块的 Mermaid 依赖图。图中:core:designsystem作为:core分组下的一个android-library节点独立存在没有任何出边outgoing edges说明它是一个不依赖其它业务模块的叶节点。这正是设计系统的关键特性它位于依赖图的最底层可以被任意 feature 模块和 UI 模块引用而不会反向引入业务耦合。依赖图还附带了一套统一的节点着色图例android-applicationapp 壳工程、android-feature业务 feature 模块、android-library库模块如 designsystem、ui、android-test测试模块、jvm-library如:core:model在图中分别以不同颜色标识图例用application -.- feature表示 app 对 feature 的运行时虚线依赖用library -- jvm表示库模块对 JVM 库的编译期实线依赖图中 ELK 布局与nodePlacementStrategy: SIMPLE配置用于控制依赖图渲染时的节点排布策略方便在文档中直观呈现模块拓扑。目录结构与源码地图:core:designsystem的源码组织非常清晰全部位于core/designsystem/src/main/kotlin/com/google/samples/apps/nowinandroid/core/designsystem/下分为三大包包路径职责核心文件component可复用 UI 组件Button.kt、Chip.kt、Navigation.kt、TopAppBar.kt、Tabs.kt、Tag.kt、IconButton.kt、LoadingWheel.kt、Background.kt、ViewToggle.kt、DynamicAsyncImage.kt、scrollbar/icon统一图标入口NiaIcons.kttheme颜色 / 字体 / 渐变 / 背景 / 主题Theme.kt、Color.kt、Type.kt、Gradient.kt、Background.kt、Tint.kt资源目录core/designsystem/src/main/res/drawable/下还存放了默认占位图core_designsystem_ic_placeholder_default.xml用于网络图片加载失败时的占位。此外测试目录core/designsystem/src/test/kotlin/.../designsystem/下提供了ThemeTest.kt以及针对每个组件的*ScreenshotTests.kt截屏测试配合src/test/screenshots/目录中的多主题截屏黄金文件golden files使用。主题体系NiaTheme与三套 ColorScheme主题是设计系统的灵魂。NiaTheme定义在 Theme.kt它是一个Composable顶层函数签名如下Composable fun NiaTheme( darkTheme: Boolean isSystemInDarkTheme(), androidTheme: Boolean false, disableDynamicTheming: Boolean true, content: Composable () - Unit, )三个参数的含义源码 KDoc 原文darkTheme是否使用深色配色默认跟随系统isSystemInDarkTheme()androidTheme是否使用 Android 主题配色绿色系而非默认主题。当为true时disableDynamicTheming参数无效disableDynamicTheming为true时即使设备支持动态取色也禁用之默认即为true。三套预定义 ColorSchemeColor.kt 定义了全套 Material 3 色调 token10/20/30/40/80/90 等色阶包括Purple紫、Orange橙、Blue蓝、Green绿、Teal青、Red错误色以及带灰调的DarkPurpleGray、DarkGreenGray、PurpleGray、GreenGray系列。基于这些 tokenTheme.kt 定义了四套 ColorSchemeLightDefaultColorScheme/DarkDefaultColorScheme默认品牌主题主色为紫Purple40/Purple80、次色为橙Orange40/Orange80、第三色为蓝Blue40/Blue80LightAndroidColorScheme/DarkAndroidColorSchemeAndroid 主题主色为绿Green40/Green80、次色为深绿DarkGreen40/DarkGreen80、第三色为青Teal40/Teal80。以LightDefaultColorScheme为例完整填充了primary、onPrimary、primaryContainer、secondary、error、background、surface、surfaceVariant、inverseSurface、outline等全部关键槽位保证 Material 3 组件在任何状态下都有正确的角色色可用。颜色方案的选择逻辑NiaTheme内部通过when表达式按优先级决定最终使用的 ColorSchemeval colorScheme when { androidTheme - if (darkTheme) DarkAndroidColorScheme else LightAndroidColorScheme !disableDynamicTheming supportsDynamicTheming() - { val context LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } else - if (darkTheme) DarkDefaultColorScheme else LightDefaultColorScheme }其中supportsDynamicTheming()通过ChecksSdkIntAtLeast(api Build.VERSION_CODES.S)注解判断设备 SDK 是否 ≥ Android 12API 31。也就是说动态取色Material You仅在Android 12且disableDynamicTheming false时启用否则回退到默认品牌色。通过 CompositionLocal 下发的主题扩展Gradient / Background / TintNiaTheme的职责不止是下发 ColorScheme还通过CompositionLocalProvider同时下发三个扩展主题对象供组件按需读取LocalGradientColorsGradientColors定义在 Gradient.kt包含top、bottom、container三个颜色槽默认值均为Color.Unspecified。默认主题下渐变取自colorScheme.inverseOnSurface顶部与primaryContainer底部动态取色模式下退化为仅使用surfaceColorAtElevation(2.dp)的容器色Android 主题下则使用硬编码的浅色DarkGreenGray95与深色Color.Black渐变。LocalBackgroundThemeBackgroundTheme定义在 Background.kt包含color与tonalElevation两个字段。默认主题取colorScheme.surface与2.dp的音调高度Android 主题使用DarkGreenGray95/Color.Black。LocalTintThemeTintTheme定义在 Tint.kt只有一个iconTint槽位。仅在动态取色模式下会被设置为colorScheme.primary用于为占位图标着色。这三个 data class 都标注了Immutable并使用staticCompositionLocalOf确保主题切换时只重组真正读取这些 Local 的子树。最后NiaTheme以MaterialTheme(colorScheme colorScheme, typography NiaTypography, content content)收尾将 Material 3 的MaterialTheme与自定义 Local 一并提供。字体排版NiaTypographyType.kt 定义了internal val NiaTypography Typography(...)基于 Material 3 规范定制了从displayLarge到labelSmall的完整 15 级文字样式。值得注意的细节默认左对齐TextAlign.Left与 LTR 方向TextDirection.Ltr每个 TextStyle 都显式声明保证多语言下的排版一致lineHeightStyle行高裁剪精细控制例如titleLarge、labelLarge等使用LineHeightStyle(alignment Alignment.Bottom, trim Trim.LastLineBottom)用于消除按钮、导航栏等场景中行高带来的多余留白字号阶梯displayLarge57sp →bodyLarge16sp →labelSmall10sp完全对应 Material 3 的字体规范labelSmall在代码注释中被标记为 Used for TaglabelMedium用于导航项labelLarge用于按钮。主题参数在 App 中的实际接线NiaTheme的三个参数在 MainActivity.kt 中由ThemeSettings统一装配而ThemeSettings又来自 MainActivityViewModel.kt 的MainActivityUiStateNiaTheme( darkTheme themeSettings.darkTheme, androidTheme themeSettings.androidTheme, disableDynamicTheming themeSettings.disableDynamicTheming, ) { NiaApp(appState) }MainActivityUiState.Success将用户偏好映射为三个布尔值shouldUseDarkTheme(isSystemDarkTheme)根据DarkThemeConfig定义于 DarkThemeConfig.kt的FOLLOW_SYSTEM/LIGHT/DARK三态决定跟随系统、强制浅色或强制深色shouldUseAndroidTheme根据ThemeBrand定义于 ThemeBrand.kt的DEFAULT/ANDROID决定是否启用 Android 绿色主题shouldDisableDynamicTheming取反自userData.useDynamicColor。也就是说用户在设置页选择的主题品牌、深色模式与动态取色偏好最终都会转化为NiaTheme的三个参数。这一链路完整展示了设计系统主题与用户数据层的对接方式。ThemeTest对主题模式的穷举验证ThemeTest.kt 通过 Robolectric 组合遍历darkTheme、disableDynamicTheming、androidTheme三参数的 8 种组合逐一断言MaterialTheme.colorScheme、LocalGradientColors.current、LocalBackgroundTheme.current、LocalTintTheme.current的期望值。例如composeTestRule.setContent { NiaTheme( darkTheme false, disableDynamicTheming true, androidTheme false, ) { val colorScheme LightDefaultColorScheme assertColorSchemesEqual(colorScheme, MaterialTheme.colorScheme) ... } }这是设计系统行为由测试锁死的最佳实践任何对主题映射逻辑的修改都会立刻被测试捕获。组件体系定制化 Material 3 组件工厂:core:designsystem的组件全部遵循统一模式用Nia*前缀封装对应的 Material 3 组件固定品牌化配色只暴露业务需要的参数槽位Slot API把配色与交互细节收敛在模块内部。以下按用途逐一解读。背景组件NiaBackground与NiaGradientBackgroundBackground.kt 提供两种背景NiaBackground读取LocalBackgroundTheme.current的color与tonalElevation填充一个全屏Surface并通过CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp)防止子组件在背景上叠加音调高度NiaGradientBackground在Surface之上叠加一个带渐变绘制的Box。渐变实现细节值得单独说明drawWithCache中用tan(Math.toRadians(11.06))计算偏移量使渐变方向偏离垂直轴 11.06 度上半部分渐变从顶部颜色在 72.4% 高度处淡出下半部分渐变从 25.52% 高度处淡入两段在中间区域重叠形成 NIA 标志性的双色渐变背景。同一文件中还定义了多预览注解ThemePreviewsPreview(uiMode Configuration.UI_MODE_NIGHT_NO, name Light theme) Preview(uiMode Configuration.UI_MODE_NIGHT_YES, name Dark theme) annotation class ThemePreviews给组件添加该注解即可同时渲染浅色与深色两套预览。按钮NiaButton/NiaOutlinedButton/NiaTextButtonButton.kt 封装了 Material 3 的三类按钮每种都提供两个重载通用内容槽版本接受content: Composable RowScope.() - Unit适合自定义内容文本 前导图标版本接受text与可空的leadingIcon槽内部通过NiaButtonContent排列图标与文字有图标时自动切换为ButtonDefaults.ButtonWithIconContentPadding并应用ButtonDefaults.IconSpacing。品牌化细节NiaButton将容器色固定为MaterialTheme.colorScheme.onBackgroundNiaOutlinedButton使用onBackground作为内容色、outline作为边框色禁用时边框透明度降为NiaButtonDefaults.DISABLED_OUTLINED_BUTTON_BORDER_ALPHA0.12f边框宽度OutlinedButtonBorderWidth 1.dp源码注释注明Material 3 默认不暴露这两个值故自行定义NiaTextButton固定内容色为onBackground。筛选 ChipNiaFilterChipChip.kt 封装 Material 3FilterChip用于兴趣话题选择等场景选中时自动在leadingIcon槽展示NiaIcons.Check勾选图标标签统一使用MaterialTheme.typography.labelSmall文本样式形状固定为CircleShape圆形胶囊选中态容器色为primaryContainer边框色为onBackground禁用时容器透明度 0.12f、内容透明度 0.38fNiaChipDefaults。导航NiaNavigationBar/NiaNavigationRail/NiaNavigationSuiteScaffoldNavigation.kt 是组件库中规模最大、最体现适配式设计系统思路的文件NiaNavigationBarItem/NiaNavigationBar封装底部导航栏tonalElevation 0.dp图标、选中图标、标签均为槽位NiaNavigationRailItem/NiaNavigationRail封装侧边导航栏容器色为透明NiaNavigationSuiteScaffold封装 Material 3 的NavigationSuiteScaffold通过WindowAdaptiveInfo默认取currentWindowAdaptiveInfo()自动计算布局类型calculateFromAdaptiveInfo在窄屏渲染底部 NavigationBar、宽屏渲染 NavigationRail实现导航的响应式适配配套的NiaNavigationSuiteScope提供item(...)DSL 声明导航项统一配色由NiaNavigationDefaults提供未选中内容色onSurfaceVariant、选中项颜色onPrimaryContainer、指示器颜色primaryContainer。顶栏NiaTopAppBarTopAppBar.kt 封装居中标题型CenterAlignedTopAppBar特点是标题使用StringRes资源 ID 而非字符串强制国际化导航图标与操作图标以ImageVector传入并附带 contentDescription图标着色统一为onSurface整个顶栏通过modifier.testTag(niaTopAppBar)暴露测试标签供 UI 测试定位。标签页NiaTab/NiaTabRowTabs.kt 封装TabRow与TabNiaTabRow容器透明、内容色onSurface指示器使用SecondaryIndicator高度 2.dp、颜色onSurface并通过tabIndicatorOffset跟随选中项滑动NiaTab将文本样式固定为居中labelLarge并给文本外层加NiaTabDefaults.TabTopPadding 7.dp的上内边距以微调对齐。话题标签NiaTopicTagTag.kt 封装为关注话题场景设计的标签按钮已关注followed true时容器色为primaryContainer未关注时使用surfaceVariant叠加 0.5f 透明度UNFOLLOWED_TOPIC_TAG_CONTAINER_ALPHA内容色由contentColorFor(backgroundColor containerColor)自动推导文本样式为labelSmall预览中将话题名大写展示。图标切换按钮NiaIconToggleButtonIconButton.kt 用于书签收藏等选中态场景封装FilledIconToggleButton接受icon与checkedIcon两个图标槽选中态图标默认回退为未选中图标选中时容器色primaryContainer、内容色onPrimaryContainer禁用时容器透明度 0.12fNiaIconButtonDefaults.DISABLED_ICON_BUTTON_CONTAINER_ALPHA源码注释说明之所以使用FilledIconToggleButton而非普通IconToggleButton是因为后者不含 shape 参数、渲染出来是方形。加载动画NiaLoadingWheel/NiaOverlayLoadingWheelLoadingWheel.kt 是纯 Canvas 自绘的加载动画12 条线NUM_OF_LINES 12以 30° 间隔旋转整轮旋转周期ROTATION_TIME 12000ms进场时每条线以FastOutSlowInEasing、40ms 递增延迟依次画出颜色动画通过keyframes实现每条线在旋转经过进度位置时从onBackground变为inversePrimary再变回形成追逐光标的视觉各线以ROTATION_TIME / NUM_OF_LINES / 2 * index的StartOffset错开相位NiaOverlayLoadingWheel在 60dp、圆角 60dp、透明度 0.83 的半透明 Surface 中内嵌加载轮用于浮层加载态两者均通过testTag(loadingWheel)与语义化的contentDescription支持测试与无障碍。视图切换按钮NiaViewToggleButtonViewToggle.kt 用于紧凑/展开两种列表视图间的切换展开态显示expandedText与NiaIcons.ViewDay图标紧凑态显示compactText与NiaIcons.ShortText文本统一使用labelSmall内容内边距ViewToggleButtonContentPaddingstart 16dp / top 8dp / end 12dp / bottom 8dp自定义自NiaViewToggleDefaults。动态图片DynamicAsyncImageDynamicAsyncImage.kt 封装 Coil 的AsyncImage其设计意图是让占位图颜色随主题动态变化加载中显示MaterialTheme.colorScheme.tertiary色的CircularProgressIndicator80dp加载失败或预览模式下回退到资源占位图core_designsystem_ic_placeholder_default读取LocalTintTheme.current.iconTint非Unspecified时对图片应用ColorFilter.tint(iconTint)——在动态取色模式下占位图会被染成colorScheme.primary与系统动态色保持一致。自定义滚动条scrollbar/包component/scrollbar/包AppScrollbars.kt、LazyScrollbarUtilities.kt、Scrollbar.kt、ScrollbarExt.kt、ThumbExt.kt提供了列表的懒加载滚动条实现使用ModifierNodeElement/DrawModifierNode自绘滚动条并通过ThumbStateActive / Dormant / Inactive与collectIsDraggedAsState、collectIsPressedAsState等交互源驱动拖动/悬停/按压状态下的显隐与颜色动画。图标体系NiaIcons统一入口icon/NiaIcons.kt 将全部图标收敛到一个object作为功能模块引用图标的唯一入口多数图标直接引用 Material 图标库的ImageVector如Icons.Rounded.Add、Icons.Rounded.Bookmark、Icons.AutoMirrored.Rounded.ArrowBack、Icons.Default.MoreVert等对选中/未选中成对图标做了命名约定Upcoming/UpcomingBorder、Bookmarks/BookmarksBorder导航与书签场景按此配对使用自定义图标drawable 资源也归入该对象管理如资源目录中的core_designsystem_ic_placeholder_default。这种单入口设计使得图标引用路径统一、便于审计也便于未来替换图标库。在 feature 模块中的实际消费方式设计系统组件在各 feature 模块中的消费体现了其价值。例如ForYouScreen.kt 使用NiaButton呈现关注主题引导InterestsScreen.kt 与 BookmarksScreen.kt 使用NiaLoadingWheel展示加载态并用NiaBackground包裹页面导航组件NiaNavigationBarItem/NiaNavigationSuiteScaffold被 app 壳层的主导航结构使用为手机/平板提供统一的底部栏 / 侧栏适配。由于 designsystem 位于依赖图最底层feature 模块引用它不会引入任何反向依赖这正是模块化架构中组件下沉、业务上浮的体现。如何查看设计系统app-nia-catalogModularizationLearningJourney.md 明确指出运行app-nia-catalog运行配置即可查看完整的设计系统。仓库中的app-nia-catalog/是一个独立的 catalog 应用模块其入口 NiaCatalogActivity.kt 在onCreate中通过setContent { NiaCatalog() }渲染组件目录。这意味着你无需运行整个 NIA 应用即可在组件目录中逐个检视按钮、Chip、导航、Tab、加载轮等组件在浅色 / 深色 / 动态取色 / Android 主题下的外观。多主题截屏测试设计系统的质量保障src/test/screenshots/下的黄金文件揭示了设计系统的另一套保障机制——多主题截屏测试。每个组件的*ScreenshotTests.kt都使用 Robolectric Roborazzi 在 JVM 上渲染并比对截图。核心机制定义在 ScreenshotHelper.kt 的captureMultiTheme扩展函数中其 KDoc 明确写道Takes six screenshots combining light/dark and default/Android themes and whether dynamic color is enabled.即默认组合遍历darkMode × androidTheme × dynamicTheming共 8 种组合其中Android 主题 动态取色不兼容被跳过最终产出 6 张截图。生成的截图命名规则一目了然例如Button_dark_defaultTheme_notDynamic.png Button_light_androidTheme_notDynamic.png Button_light_defaultTheme_dynamic.png对应{name}_{dark|light}_{defaultTheme|androidTheme}_{dynamic|notDynamic}.png。每个组件测试都可以通过shouldCompareDarkMode、shouldCompareDynamicColor、shouldCompareAndroidTheme三个开关裁剪组合例如ButtonScreenshotTests中对带前导图标的按钮设置shouldCompareAndroidTheme false以减少冗余截图。测试内部还集成了Roborazzi 无障碍检查checkRoboAccessibility失败级别为 Error在截图的同时执行无障碍规则校验确保设计系统组件满足 ATF 无障碍要求测试还通过LocalInspectionMode provides true与DeviceConfigurationOverride.DarkMode(...)在预览/测试环境中稳定复现各主题。例如 ButtonScreenshotTests.kt 中的测试RunWith(RobolectricTestRunner::class) GraphicsMode(GraphicsMode.Mode.NATIVE) Config(application HiltTestApplication::class, qualifiers 480dpi) LooperMode(LooperMode.Mode.PAUSED) class ButtonScreenshotTests { get:Rule val composeTestRule createAndroidComposeRuleComponentActivity() Test fun niaButton_multipleThemes() { composeTestRule.captureMultiTheme(Button) { description - Surface { NiaButton(onClick {}, text { Text($description Button) }) } } } }截屏目录中还能看到*_fontScale2.png这样的文件如Tabs_fontScale2.png、Tag_fontScale2.png、Navigation_fontScale2.png说明测试同时覆盖了2 倍字体缩放的无障碍场景LoadingWheel_animation_*.png系列则记录了加载动画在多个时间点的帧状态。总结一份可复用的设计系统实现蓝图:core:designsystem为大规模 Compose 应用提供了设计系统模块化的完整范本其要点可归纳为依赖方向单一设计系统作为依赖图叶节点只向上提供组件与主题不依赖任何业务模块见 core/designsystem/README.md 依赖图与 ModularizationLearningJourney.md 模块表主题参数化NiaTheme(darkTheme, androidTheme, disableDynamicTheming)三参数覆盖了深浅色 × 品牌/Android 主题 × 动态取色的全部主题维度并通过LocalGradientColors/LocalBackgroundTheme/LocalTintTheme下发扩展主题真实参数由MainActivityViewModel从用户偏好ThemeBrand、DarkThemeConfig、useDynamicColor映射而来组件封装模式统一Nia*前缀 Slot API固定品牌配色、收敛默认值Nia*Defaults对象业务侧只关心语义参数测试闭环完整ThemeTest穷举主题参数组合、captureMultiTheme生成多主题黄金截图、Roborazzi 无障碍检查、字体缩放场景覆盖共同把设计系统的视觉与行为锁死。对于希望在自己项目中建立 Compose 设计系统的开发者可以按此路径逐步落地先定义颜色/字体 token 与主题入口再将高频组件以统一模式封装并下沉到独立模块最后用多主题截屏测试固化视觉回归——这正是 Now in Android 这份设计系统给我们展示的完整工程实践。赞分享移动开发【免费下载链接】nowinandroidA fully functional Android app built entirely with Kotlin and Jetpack Compose项目地址https://gitcode.com/GitHub_Trending/no/nowinandroid点击查看免费下载相关推荐Ant Design X设计系统组件库架构模块化设计与组件复用Ant Design X设计系统组件库架构模块化设计与组件复用 1. 架构总览 Ant Design X简称AntD X是一个面向AI驱动界面的组件库采AI 应用前端探索 Jekyll 主题 Chirpy 模块化架构JavaScript 组件设计的终极指南探索 Jekyll 主题 Chirpy 模块化架构JavaScript 组件设计的终极指南 Jekyll 主题 Chirpy 是一个基于 Jekyll 框架的前端文档Now in Android 网络层深度解析:core:network 模块的架构、Demo/Prod 双实现与数据同步设计Now in Android 网络层深度解析 :core:network 模块的架构、Demo/Prod 双实现与数据同步设计 :core:network 是移动开发创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考