Material Design
问题
Material Design 3 在 Android 中如何使用?主题和样式是如何工作的?
答案
1. Material Design 3 概览
Material Design 3(Material You)是 Google 最新的设计语言,核心特性:
- Dynamic Color:基于壁纸自动生成主题色(Android 12+)
- Color Scheme:基于一个种子色生成完整调色板
- Shape System:圆角形状体系
- Typography:类型比例系统
2. 主题配置
<!-- res/values/themes.xml -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight.NoActionBar">
<!-- 主色调 -->
<item name="colorPrimary">@color/md_theme_primary</item>
<item name="colorOnPrimary">@color/md_theme_on_primary</item>
<item name="colorPrimaryContainer">@color/md_theme_primary_container</item>
<!-- 次要色 -->
<item name="colorSecondary">@color/md_theme_secondary</item>
<item name="colorOnSecondary">@color/md_theme_on_secondary</item>
<!-- 背景/表面 -->
<item name="colorSurface">@color/md_theme_surface</item>
<item name="colorOnSurface">@color/md_theme_on_surface</item>
<!-- 错误色 -->
<item name="colorError">@color/md_theme_error</item>
</style>
3. Theme vs Style
| 概念 | 作用范围 | 继承方式 |
|---|---|---|
| Theme | 整个 Activity 或 Application | 通过 parent 显式继承 |
| Style | 单个 View | 通过 . 隐式继承或 parent 显式继承 |
<!-- Style 示例 -->
<style name="Widget.MyApp.Button" parent="Widget.Material3.Button">
<item name="cornerRadius">16dp</item>
<item name="android:textAllCaps">false</item>
</style>
<!-- 隐式继承(通过 . 号)-->
<style name="Widget.MyApp.Button.Outlined">
<!-- 继承 Widget.MyApp.Button -->
<item name="strokeColor">?attr/colorPrimary</item>
</style>
4. 属性优先级
View 的属性查找顺序(优先级从高到低):
- View 上直接设置的属性(
android:textColor="@color/red") - View 的
style属性 - Theme 中的
defStyleAttr(如textViewStyle) - Theme 中的
defStyleRes - Theme 自身
5. 常用 Material 组件
// MaterialButton
binding.button.apply {
icon = ContextCompat.getDrawable(context, R.drawable.ic_send)
iconGravity = MaterialButton.ICON_GRAVITY_TEXT_START
}
// TextInputLayout(带浮动标签的输入框)
// <com.google.android.material.textfield.TextInputLayout
// style="@style/Widget.Material3.TextInputLayout.OutlinedBox">
// <com.google.android.material.textfield.TextInputEditText />
// </com.google.android.material.textfield.TextInputLayout>
// Snackbar
Snackbar.make(view, "已删除", Snackbar.LENGTH_LONG)
.setAction("撤销") { undoDelete() }
.show()
// BottomSheet
val bottomSheet = BottomSheetDialog(this)
bottomSheet.setContentView(R.layout.sheet_content)
bottomSheet.show()
常见面试问题
Q1: ?attr/colorPrimary 和 @color/colorPrimary 有什么区别?
答案:
@color/colorPrimary:直接引用colors.xml中的固定颜色值?attr/colorPrimary:引用当前 Theme 中colorPrimary属性的值。不同 Theme 下可能是不同的颜色
使用 ?attr/ 可以让 View 颜色跟随主题变化,这是 Material Design 的推荐做法。
Q2: 如何实现 Dynamic Color(动态取色)?
答案:
Android 12+ 系统会根据壁纸生成动态颜色,通过 DynamicColors API 应用:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
}
}
低版本设备会 fallback 到 Theme 中定义的默认颜色。
Q3: Theme Overlay 是什么?
答案:
Theme Overlay 是一种轻量级的局部主题覆盖,只需定义想要修改的属性,应用于特定 View:
<style name="ThemeOverlay.MyApp.DarkSurface" parent="">
<item name="colorSurface">@color/black</item>
<item name="colorOnSurface">@color/white</item>
</style>
<LinearLayout android:theme="@style/ThemeOverlay.MyApp.DarkSurface">
<!-- 内部 View 使用深色表面 -->
</LinearLayout>
适用于一个浅色页面中需要一块深色区域的场景。