打造 Material 形状主题 | 实现篇

打造 Material 形状主题 | 实现篇_第1张图片

作者 / Nick Rout,Material 开发技术推广工程师

Material 主题 (theming) 是一种自定义 Material 组件的方式,目的是使其与品牌保持一致。Material 主题涉及包括颜色、排版和形状,您可以对它们进行调整来获得近乎无限的组件变体,且依然保持其核心结构和易用性。

  • Material 主题

    https://material.io/design/material-theming/overview.html#material-theming

  • Material 组件

    https://material.io/components

  • 颜色

    https://material.io/design/color/

  • 排版

    https://material.io/design/typography/

  • 形状

    https://material.io/design/shape/

打造 Material 形状主题 | 实现篇_第2张图片

自版本 1.1.0 开始,您可在 Android 上通过 Material Components (MDC) 库实现 Material 主题。如果您要从设计支持库 (Design Support Library) 或 MDC 1.0.0 迁移,请查看我们的迁移指南。

  • MDC 库

    https://github.com/material-components/material-components-android

  • 迁移指南

    https://material.io/blog/migrate-android-material-components

本文将重点讨论如何实现形状主题

大多数 widget 都有一个背景形状,但您有没有思考过形状对用户行为的影响?就像颜色和排版一样,形状可以引导用户的注意力,展示交互性,并在视觉上区分界面中的元素。Material 的形状主题可以定义全局形状值,从而改变整个应用中组件的样式。例如,让所有的卡片、对话框和菜单都呈现出打磨过的圆角。

形状属性

Material Design 提供了 3 个形状 "类别",适用于应用中大大小小有形的 widget。每个类别都有一个设计术语 (例如 "小型组件"),以及相应的可以在您的应用主题中进行自定义的形状属性 (例如 shapeAppearanceSmallComponent)。每个类别都有默认的 "基准" 值 (角尺寸、角形状等)。

打造 Material 形状主题 | 实现篇_第3张图片

△ MDC 形状属性与基准值

Material 组件使用这些形状属性来设置 widget 背景的样式。

打造 Material 形状主题 | 实现篇_第4张图片

△ 一个按钮所用的形状属性 (红色)

它们以如下形式:

app:shapeAppearance=”?attr/shapeAppearanceSmallComponent”

应用于布局和 widget 样式中。

在 MDC 主题中,这些属性会映射到样式,如:




ShapeAppearance 叠加层

您还可以定义 ShapeAppearance 叠加层,它支持所有相同的属性,行为上也类似于主题叠加层。

它们可以通过 app:shapeAppearanceOverlay 与常规 ShapeAppearance 样式一起应用,以更改特定某个角的属性值。以下是底部菜单叠加层的示例 (来自 MDC 源代码),将菜单底部两个角的半径设为和屏幕的角半径相同:



注: 某些 MDC widget 默认已应用叠加层,您在调整其 shapeAppearance 时可能需要考虑这些叠加层。例如 FloatingActionButtonChip,它们都通过叠加层将 cornerSize 设置为 50%。

  • FloatingActionButton

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/floatingactionbutton/FloatingActionButton.java

  • Clip

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/chip/Chip.java

填充和描边

与 XML 可绘制对象不同,ShapeAppearance 样式不包括任何填充或描边的概念。MDC 倾向于在主 widget 样式中单独指定这些值来减少耦合:


注: ShapeAppearance 样式和背后的 MaterialShapeDrawable 类仅支持纯色填充和描边。目前尚不支持渐变,您需要将 XML 可绘制对象与 结合使用来实现渐变。

在应用主题中覆盖形状

接下来我们来看如何通过覆盖相关属性将您选择的形状添加到应用主题中。

首先,您的主题需要妥善处理浅色和深色调色板,同时减少与基础主题的重复。有关这方面的更多信息,请查看 Chris Banes 关于深色主题的文章,以及他和 Nick Butcher 的 "如何正确开发外观样式" 演讲。

设置完成后,在基础主题中覆盖要更改的形状属性:



Material Design 组件将响应主题级别的形状覆盖:

打造 Material 形状主题 | 实现篇_第6张图片

△ Material Design 组件响应主题级别的形状覆盖

MaterialShapeDrawable

形状主题由 MaterialShapeDrawable 类驱动。它是所有 MDC widget 默认的背景可绘制对象,并用于呈现形状。与其他可绘制对象不同,它无法在 XML 中使用,需要以编程方式处理。

  • MaterialShapeDrawable

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/shape/MaterialShapeDrawable.java

打造 Material 形状主题 | 实现篇_第7张图片

△ MaterialShapeDrawable 和 ShapeAppearanceModel 图示

MaterialShapeDrawable 可以这样实例化:

// Default constructor
val msd = MaterialShapeDrawable()
// ShapeAppearanceModel constructor
val msdFromSam = MaterialShapeDrawable(shapeAppearanceModel)
// Style/attr resources constructor (reads shapeAppearance and shapeAppearanceOverlay)
val msdFromStyles = MaterialShapeDrawable(context, attrs, defStyleAttr, defStyleRes)
// Cast from widget background
val msdFromWidget = widget.background as MaterialShapeDrawable

ShapeAppearanceModel

ShapeAppearanceModel 是 ShapeAppearance 样式的程序化等效项,它存储形状的倒角和边线的数据。MaterialShapeDrawable 则使用此类渲染其形状。

  • ShapeAppearanceModel

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/shape/ShapeAppearanceModel.java

生成器模式用于实例化 ShapeAppearanceModel:

// Default builder
val sam = ShapeAppearanceModel.builder()
    .setAllCorners(CornerFamily.CUT, cornerSize)
    // Also setTopRightCorner, setAllEdges, etc.
    .build()
// Style/attr resources builder (reads shapeAppearance and shapeAppearanceOverlay)
val samFromStyles = ShapeAppearanceModel.builder(context, attrs, defStyleAttr, defStyleRes)
    .build()
// Build from existing ShapeAppearanceModel
val samFromExisting = sam.toBuilder()
    ...
    .build()

有关边线和自定义路径更高级的示例,请参阅 MDC 目录中的 BottomAppBarCutCornersTopEdge。

  • BottomAppBarCutCornersTopEdge

    https://github.com/material-components/material-components-android/blob/master/catalog/java/io/material/catalog/bottomappbar/BottomAppBarCutCornersTopEdge.java

填充和描边

MaterialShapeDrawable 处理填充和描边的渲染。有许多方法可以调整这些属性:

// Fill color
msd.setFillColor(fillColorStateList)
// Stroke color
msd.setStrokeColor(strokeColorStateList)
// Stroke width
msd.setStrokeWidth(strokeWidthDimension)

高程和叠加层

MaterialShapeDrawable 负责渲染叠加层以呈现深色主题中的高程 (深色主题时不使用阴影,而是使用明暗表示高程),这些操作由 MDC widget 默认处理。启用和使用此功能的方法如下:

// Initialize elevation overlays
msd.initializeElevationOverlay(context)
// Pass elevation value to MSD to apply overlay (in dark theme)
msd.setElevation(elevation)

如需了解更多信息,请参阅之前发布的《打造 Material 颜色主题 | 实现篇》以及 Chris Banes 关于深色主题的文章。

阴影渲染

平台只在 API 21 及更高级别中支持高程阴影的渲染。MaterialShapeDrawable 为向后移植阴影渲染提供了可能性:

/**
 * Set shadow compat mode to be one of:
 * - SHADOW_COMPAT_MODE_DEFAULT: Use platform rendering on API 21+, else compat rendering
 * - SHADOW_COMPAT_MODE_NEVER: Use platform rendering always
 * - SHADOW_COMPAT_MODE_ALWAYS: Use compay rendering always
 */
msd.setShadowCompatibilityMode(shadowMode)

角插值

MaterialShapeDrawable 提供了所有角尺寸的插值方法,通过提供从 0.0~1.0 之间取值的乘数,方便开发者在动画和转场中使用。

// Set corner interpolation to half of current cornerSize(s)
msd.setInterpolation(0.5f)

了解 MDC widget 中的形状

如前所述,MDC widget 会响应主题级别覆盖的形状属性。但是,举例来说,怎样才能知道一个按钮会使用 shapeAppearanceSmallComponent 作为其容器的样式?让我们来看看几种学习途径。

使用 "构建 Material 主题" 项目

构建 Material 主题是一个交互式 Android 项目,您可以自定义颜色、排版和形状来创建自己的 Material 主题。项目里还包含了所有主题的参数和组件的目录。要确定哪些 widget 响应主题中属性的变化,可以这样操作:

  • 复制项目并在 Android Studio 中运行应用

  • 修改 res/values/shape.xml 以及 res/values/themes.xml 中的值

  • 重新运行应用并观察界面中发生的变化

  • 构建 Material 主题

    https://github.com/material-components/material-components-android-examples/tree/develop/MaterialThemeBuilder

  • Shape.xml

    https://github.com/material-components/material-components-android-examples/blob/develop/MaterialThemeBuilder/app/src/main/res/values/shape.xml

  • Themes.xml

    https://github.com/material-components/material-components-android-examples/blob/develop/MaterialThemeBuilder/app/src/main/res/values/themes.xml

打造 Material 形状主题 | 实现篇_第8张图片

△ 构建 Material 主题中形状值的变化

阅读 MDC 开发者文档

我们最近更新了 MDC 开发者文档,加入了属性表,其中给出了库中所使用的设计术语和默认值。例如,下图是按钮文档的 "Anatomy and key properties"(详解和关键属性) 部分。

  • 按钮文档

    https://material.io/components/buttons/android

打造 Material 形状主题 | 实现篇_第9张图片

△ MDC 开发者文档中按钮的形状属性表以及默认值

阅读源代码

阅读 MDC 源代码可谓是最可靠的方法。MDC 使用默认样式实现 Material 主题,因此看一看这些样式以及所有可设置的属性和 java 源文件是个不错的办法。例如,查看 MaterialButton 的 styles、attrs 和 java 源文件。

  • Material Button 的源文件

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/button/res/values/styles.xml

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/button/res/values/attrs.xml

    https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/button/MaterialButton.java

一个有趣的做法是观察 MDC widget 如何使用默认样式来确保 MaterialShapeDrawable 为默认背景。一般的做法是:

  • 在 widget 默认样式中将 android:background 设置为 @null 或 @empty

  • 如果在解析属性时未检测到背景,则以编程方式实例化一个 MaterialShapeDrawable 并将其设置为背景

  • 如果已设置背景 (例如在布局或自定义样式中),则予以保留,并不再使用 MaterialShapeDrawable

打造 Material 形状主题 | 实现篇_第10张图片

△ MDC 按钮默认样式以及取值

自定义视图中的形状

您的应用可能包含自己构建或从现有库中获得的自定义 widget。在和标准 MDC widget 进行混用时,让这些 widget 支持 Material 主题非常有用。我们来看看让自定义 widget 支持形状主题时要注意些什么。

和默认样式中使用 MDC 属性

通过使用 可以让自定义视图支持样式。在保持一致性方面,复用 MDC 中的属性名称是个很好的做法。使用 的默认样式还可以引用 MDC 主题的形状属性为其赋值,同时也可以通过使用 @null/@empty 来实现 MaterialShapeDrawable 背景:



    
    ...







注意高程和叠加层

如果您希望自定义视图支持高程叠加层或向后移植阴影渲染,最好覆盖 setElevation 方法并将其值传递至 MaterialShapeDrawable 背景:

class AppCustomView ... {




    ...




    private lateinit var materialShapeDrawable: MaterialShapeDrawable




    override fun setElevation(elevation: Float) {
        super.setElevation(elevation)
        materialShapeDrawable.setElevation(elevation)
    }
}

下一步

现在,我们已经在 Android 应用中实现了 MDC 形状主题。有关 Material 主题的其他课题,请阅读我们相关的介绍文章。

  • 为什么推荐使用 MDC

    https://medium.com/androiddevelopers/we-recommend-material-design-components-81e6d165c2dd

  • 排版主题

    https://material.io/blog/android-material-theme-type

  • 颜色主题

    https://material.io/blog/android-material-theme-color

  • 深色主题

  • 动效系统

    https://material.io/blog/android-material-motion

我们一如既往地期待您在 GitHub 上提交错误报告和功能需求。另外,请务必查看 Android 组件示例应用。

  • 提交错误报告

    https://github.com/material-components/material-components-android/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BComponent+name%5D+Short+description+of+issue

  • 提交功能需求

    https://github.com/material-components/material-components-android/issues/new?assignees=&labels=feature+request&template=feature_request.md&title=%5BComponent+name%5D+Short+description+of+request

  • Android 组件示例应用

    https://github.com/material-components/material-components-android-examples

如果您已成功实现形状主题,或您在实现期间遇到问题,欢迎在下方评论区和我们分享。

打造 Material 形状主题 | 实现篇_第11张图片


推荐阅读

打造 Material 形状主题 | 实现篇_第12张图片

打造 Material 形状主题 | 实现篇_第13张图片

打造 Material 形状主题 | 实现篇_第14张图片

 点击屏末  | 查看 Material Design 设计指南


你可能感兴趣的:(java,编程语言,html,android,项目管理)