如果我们想创造更好的 Android App, 我相信我们需要遵循 Material Design 的设计规范一般而言, Material Design 是一个包含光线, 材质和投影的三维环境如果我们想要在 App 的开发过程中, 跟随 Material Design 的设计原则, 那么理解 光 与 阴影 就显得尤为重要了
我将尝试解释本文中的以下主题
Android 中的 3D
深度(Depth)
Z 轴, Elevation 和 Translation Z
光源
按钮状态(按下和静止)
Outline
自定义的 ViewOutlineProvider
在深入到阴影和光线之前, 我想告诉你我们的真实环境什么?
什么是 3D?
真实的物质环境, 是一个三维空间, 这意味着所有的物体对象都有 XY 和 Z 维度 Z 轴与显示器的平面垂直对齐, 正 Z 轴朝向观察者延伸在 Material Design 的世界里, 每个物体都有 1dp 的厚度
Android 的 Depth
Material Design 不同于其他的设计指南, 因为它增加了 Depth(深度)的概念而 Depth 在真实的视觉中, 有重要的意义
我们可以认为我们桌子上有一层纸, 如果我们再贴一张纸, 我们的眼睛会觉得它有一个深度
让我们通过 Material Design 的应用截图来想象它
让我们来看看屏幕上的各个元素
屏幕(表层 - 深度为 0 )
CardViews
App UI 布局
浮动动作按钮(Floating Action Button)
这里面, 每个元素都在另一个元素之上 CardView 可以滚动, 所以我们可以说第一层是可滚动的内容, 第二层是 AppBar 布局, 第三层 (顶层) 是浮动动作按钮
那么, 我们如何定义层级? 我们如何让用户感受到深度? 答案是: Z 轴
Android 中的 Z 值是什么?
View 的 Z 值有两个组成部分:
Elevation: 高度, 一个静态值
Translation Z:Z 轴变动值, 用于动画的动态值
我总是在想 Elevation 和 Translation Z 有什么区别
Elevation 是静态的, 所以你最好不要动态的去改变他如果你想在 Z 轴上做动画的效果(如按下态或者静止态), 你需要使用 Translation Z 属性
Translation Z 是动态的, 当你创建一个空白项目, 并在其中增加一个按钮的时候, 当你按下它你将会看到阴影变大了实际上 Elevation 并没有变化, 而是 Translation Z 属性在变化这是 Android 使用默认的状态列表动画, 更改 Z 属性
Z Vaue = Elevation + TranslationZ
如果我们改变两个具有 Z 值的 View, 让它们相交 Android 如何处理屏幕上的层级? 让我用一个我设计的图表向你展示
另外一个问题, 我们如何看到物体的影子? 我们是需要一个阴影吗? 不是的, 我们是需要一个光源
Android 中的光源是什么?
其实问题不在于是什么? 而是在哪里
在现实中, 如果我们手持一个手电筒照桌子上的物体(从它的顶部), 阴影的长度会缩短, 当你降低它的时候, 阴影的长度会增加
那么在 Android 的 Material Design 中, 光源在哪里? 在顶部? 还是有角度的? 经过一番研究, 我发现这个现象
Android 中存在两个光源, 顶部那个是关键的光源, 而另一个是环境光源, 我们看到的阴影实际上是这两个光源的组合
让我们看看显示的效果
在 Android 中, 我们有很多小部件按钮 CardView 对话框, 抽屉等所有这些都是视图如果有一个光源, 我们就有阴影那么我们如何在 Android 中决定 Z 值呢? Material Design 是如何规定这些的?
有一个示意图来反映这种情况
静止或者按下
正如我之前提到的, 在 Android Framework 中, 一些动画是为小部件而实现的如果你在布局中放置浮动操作按钮, 默认情况下它将具有 6dp 的 Elevation 但是你会注意到当你按下按钮时, FAB 的 Elevation 将会提高到 12 dp
让我告诉你, 在这个过程中发生了什么
其实 FAB 有 6dp 的 Elevation 当您按下按钮时, translationZ 值开始增加 ViewPropertyAnimator 通过将 translationZ 值从 0dp 更改为 6dp 来 让视图动起来如果你释放按钮, ViewPropertyAnimator 播放动画, 将 translationZ 从 6dp 变到 0dp 你可以为你的视图创建自定义状态列表动画, 并将其添加到你的视图上
我们来看一下这个过程的流程图
阴影的秘密: Outline
Outline 是一个属于 android.graphic 下的类, 看看它的文档都说了什么
定义一个简单的形状, 用于图形的边界区域
可以为 View 计算, 也可以由 Drawable 计算, 以驱动由视图投射的阴影的形状, 或剪裁视图的内容
每个 View 都有默认的轮廓以显示其阴影如果我们创建一个可绘制的自定义形状, 其轮廓将根据其形状在内部进行计算所以, 如果我们画圆, 轮廓将会是圆的如果我们绘制矩形, 轮廓将是矩形
总而言之, 有一个 Outlin 可以让你以不可见的方式看到这个效果但是, 如果我想创建一个自定义的视图, 并动态地改变它的边界呢? Android 会为我的自定义视图提供了 Outline 吗?
Android 当然为我们提供了自定义 Outline 的办法, 那就是 :
ViewOutlineProvider
什么是 ViewOutlineProvider
在我最近的开源的 ScalingLayout 库中, 我没有对自定义视图实现阴影效果我以为这是非常漂亮, 没有影子但要记住 Material Design 的基础知识, 3D,Depth,Z-Value
在这个动画中, 我们可能无法确定那些地方是可以被点击的, 而且缩放布局中并没有阴影
接下来我们为自定义的视图提供动态的轮廓
- public class ScalingLayoutOutlineProvider extends ViewOutlineProvider {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, width, height, radius);
- }
- }
- public class ScalingLayout extends FrameLayout {
- //...
- viewOutline = new ScalingLayoutOutlineProvider(w, h, currentRadius);
- setOutlineProvider(viewOutline);
- //..
- }
这样, 我们就为自定义的 View 增加了高度的支持
更多有关于 ViewOutlineProvider 的使用中, 被简化的一些基础知识, 你可以在 ScalingLayout 中找到细节
https://github.com/iammert/ScalingLayout
来源: https://juejin.im/post/5a93943c5188257a76634c4d