坑爹的shadow -- 总结 与 各种坑

news2024/12/20 14:28:39

作者:snwrking

最近公司来了新UX总监, 很喜欢给设计添加浓重的, 而且是好几层的阴影. 这下就苦了我们Android开发了. 因为是Android不支持啊, 巧妇也难为无米之炊啊. (折中方法也不是没有, 就是自己把阴影做个view, 但它的blur这些比较麻烦, 做过Android的都知道这个Blur要用到BlurScript之类, 做起来不容易)

Android的shadow之痛

以下图中一个矩形有阴影为例, 它的shadow是有多种参数的, 主要就是: offsetX, offsetY, blur, spread, color & alpha. Ux在figma等软件上设计好的样子如下:

而UX与PO天天挂嘴边的: “为什么人家iOS可以, 为什么人家web可以, 就你Android不行?”, 这个是因为人家有支持啊.

  • web用css: filter: drop-shadow(5px 5px 5px rgba(0,0,0,0.3));
  • iOS的view的layer也支持 (iOS的绘制也是有一层一层的, 类似Android中的FrameLayout一样, 可以一层层堆叠):
let yourView = UIView()
yourView.layer.shadowColor = UIColor.black.cgColor
yourView.layer.shadowOpacity = 1
yourView.layer.shadowOffset = .zero
yourView.layer.shadowRadius = 10

但我们Android确实对shadow的支持从来就弱. 若是对文字的阴影, 那TextView确实有部分支持, 但blur效果就不支持:

<TextView android:id="@+id/txt_example1"
      ...
      android:shadowColor="@color/text_shadow"
      android:shadowDx="1"
      android:shadowDy="1"
      android:shadowRadius="2" 

要是想像UX要求的一样, 给任意View添加阴影, 好就麻烦了. 当然, Android自己也意识到这一点了, 所以在Android 5之后, 即在新引入的Material Design里添加了阴影的支持. 但它的阴影理论自成一套, 根本跟figma上的shadowOffsetX, shadowOffsetY, shadowBlur, shadowRadius不一样. 它的一套理论更像是光照系统.

Android 5.0之后的阴影&光照系统 – 理论部分

题外话: 现在app的minSDK不至于少于5.0吧, 所以现在只郑重讲Android5.0之后的阴影系统.

这一章节是阴影的理论部分. 话说我也不想讲理论, 太枯燥, 所以我尽量讲得简略些, 只挑重点讲. 后面再结合实践来验证这些理论, 来加深理解.

Material Design其实更像是一个光照系统. 它假定在远方有一个光源, 然后照向你的view. 这样你的view若是离手机屏幕有一些高度的话, 那就会在手机屏幕上形成阴影.

注意这个阴影比较逼真, 在边缘因为有光照与阴影的同时干涉, 所以阴影较浅 (即下图中的红色部分) 而中间的阴影更浓 (即下图的蓝色部分)

  • 较浅阴影在Material Design中的术语叫做: ambient shadow (环境阴影)
  • 较深阴影的术语叫: spot shaodw (聚光灯阴影)

同样, 上面也说了, 若是你的View紧贴手机屏幕, 那也不会有阴影的. 你的View只有抬起来一点高度, 才会形成阴影. 这跟日常生活中的体验是一样的. 而这个"抬起来的高度", 在Android中的术语就是: elevation, 你可以理解为z轴上的高度啦. 当elevation不同, 自然阴影也不一样. 如下面的表格, 分别代表了elevation为2dp与10dp时的结果:

好了, 上面就是重要的三个阴影关键: ambient shadow, spot shadow, 以及elevation.

阴影的实践

当你的view有了elevation时, 你就天然会形成两种阴影: ambient与spot shadow.

同样Android也提供了一共5个API来帮我们设置阴影. 我按照since API Level xx做了分类:

  • Since Api 21:
    • android:elevation : 在view中设定
    • android:spotShadowAlpha: 在theme这个xml中设定
    • android:ambientShadowAlpha: 在theme这个xml中设定
  • Since Api 28:
    • android:spotShadowColor: 在view中设定
    • android:ambientShadowColor: 在view中设定
听起来好像蛮简单的, 有了这5个api, 微调下值就能得到和UX设计的近似的阴影, 这就算完工了. 但现实生活中开发总是悲催得多, 比如说你设了这5个api, 但现实中却发现一点点子阴影都没有. 这是怎么了?
这就不得不说官网上根本没有详细讲述的一些坑了. 不解决这些坑, 我们的阴影仍是不行的.

设置阴影的多个坑

坑1: 设置了elevation仍没有阴影

下面的代码就是我以前写过的一个代码. 按理说我的elevation已经有了, 而ambient + spot shadow的color, alpha都有默认值 (手机上就淡淡的黑色灰影; TV上则是更重些的灰色), 那就应该有阴影. 但不幸的是, 最终效果是完全没有阴影效果.

<SomeView
    android:elevation="24dp"
    />

原因是: Android中你的View得有一个背景, 颜色或图片都可以, 那你才会有阴影. 当我上面的view没有背景, 那Android根本就不会为它生成背景, 因为它把这个view当成透明的了, 一个透明的东西在光照下自然是没有阴影的.

解决办法:

<SomeView
    android:background="@drawable/some_bg"
    android:elevation="24dp"
    />

坑2: 下载的图片素材做bg, 但仍没有阴影

我有一个view, 其是有背景的. 我去figma上下载这个背景,

并导入到Android Studio后, 命名为bg_pink_polygon, 但下面的代码仍是没有阴影.

<SomeView
    android:background="@drawable/bg_pink_polygon"
    android:elevation="24dp"
    />

: 原因其实是Android要求你的view有bg, 才可能会有阴影 但前提是这个bg, 不能是SVG形成的<vector> xml, 不然Android也不会为它生成阴影.

也就是说, 你的bg可以是这样的:

  • 纯color值, 如 #ff00cc
  • 纯png, jpg图片, 如bg_abc.png
  • 或为点9图片, 如bg_abc.9.png
  • 或是<shape>的drawable xml,
  • 或是item为<shape><layer-list>的drawable xml

但是, 唯独有一点, 你的bg不能是<vector>的drawable, 不然就没有阴影.

坑3: 阴影需要额外空间吗?

比如说我们的View的宽高是100x100, 而UX要求阴影的offsetX, offestY为20dp, 我换算成elevation为多少dp后, 假设阴影占20x20的空间, 那最终UI效果要这样吗?

<FrameLayout width=120dp height=120dp>
    <View width=100dp height=100dp/>
</FrameLayout>

: 一般来说不需要. 这一点Android做得还是可以的, 你只要考虑你的View的尺寸就行了, 阴影的空间Android会自动画出来, 不用你担心.

但是现实生活中更复杂些, 比如要求一个view有阴影, 而这个view在另外的其它自定义View中, 那这时就不好讲. 可能这时的阴影就被cut off了. 我就碰到过这样的实例.

至于原因则可能是为android中默认是child view不能超过parent view group的边界, 超出了的部分会不被绘制出来. 这也包括了阴影. 所以阴影也被截断了.

这里你可能就要做额外一层layout. (这一点蛮恶心了, 牺牲了性能就为了个阴影, 所以我也不喜欢过多的阴影设计)

<!-- 原来是 --> 
<ConstraintLayout elevation=24dp backgorund=xx> 
    .... <!-- children -->

现在为了给这个constraint layout添加阴影, 并不被截断, 就得外加一层layout

<FrameLayout padding=8dp clipToPadding=false>
    <ConstraintLayout elevation=24dp backgorund=xx>

这个FrameLayout存在的唯一目的就是留出空间, 来给constraitLayout提供绘制阴影的空间.

更多阴影设置

前言: 为什么view一定要有bg, 才会有阴影的可能?

其实我们上面的话不太对, 即这句: “光源对view照下来, 形成了阴影”

而上一节中, 我们修正了这句话, 即应该是: “光源对view的背景照下来, 形成了阴影”.

其实这次的修正仍是不对的, 正确的说法是"光源对View的outline provider照下来, 形成了阴影".

Android中View是自带了outline provider的, 默认值就是background. 其它的可选值如下:

这下其实也就解释了, 为什么要有bg, 才会有shadow : 因为默认就是background的outline provider啊. 要是view没有background, 就没了outline provider, 那就自然就没了阴影.

outline provider

那是不是说当我把outline provider设置了非background的其它值, 那即使这个view没有bg, 只要有elevation就会有阴影? : 是的, 答对了.

自定义shadow的形状

outline provider的另一作用, 就是可以让你自定义shadow的shape, 比如说不再是矩形, 可以变成圆形, 五角星形, …

你所需要做的, 就是自定义一个outline provider

class MyShadowOutlineProvider(
    val cornerRadius: Float = 0f,
    var offsetX: Int = 0
    var offsetY: Int = 0
) : ViewOutlineProvider() {

    private val rect: Rect = Rect()

    override fun getOutline(view: View?, outline: Outline?) {
        view?.background?.copyBounds(rect)
        rect.offset(offsetX, offsetY)
        outline?.setRoundRect(rect, cornerRadius)
    }
    
}

然后view中指明使用这个outline provider:

        outlineProvider = MyShadowOutlineProvider(14f.dpToPx(), 1f, 1f, 0)
        btnShadowDemo.outlineProvider = outlineProvider
        btnShadowDemo.elevation = 30f //elevation仍是需要的!

最佳实践推荐

theme中设置alpha为1

因为theme中把ambientShadowAlpha, spotShadowAlpha给定死了. "定死了"就是无法在代码中修改这两个的alpha值, 就不太灵活.

一个取巧的办法则是:

    <style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="android:ambientShadowAlpha">1</item>
        <item name="android:spotShadowAlpha">1</item>      
		... ...
    </style>

1). theme中把这两个alpha全设为1 2). 然后阴影color就可以加上alpha了, 如

// 原来是:
setSpotShadowColor(0x000000) //颜色是 rgb

// 现在则要给color加上alpha
setSpotShadowColor(0x88000000) //变成了 argb

这样一来我们就能灵活更改shadow的颜色与透明度了.

若无定制shadow形状的要求

那就是:

1). 给view添加非svg的bg
2). 再加上elevation
3). (可选) 可修改ambient/spot shadow的color

阴影就出来了

若有定制shadow形式的要求

那就要:

1). 自定义一个outline provider
2). view.outlineProvider = myOutlineProvider
3). 再加上elevation

阴影也同样出来了, 还是你自己定制的shape.

Android 学习笔录

Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/788840.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DAY1,Qt [ 手动实现登录框(信息调试类,按钮类,行编辑器类,标签类的使用)]

1.手动实现登录框&#xff1b; ---mychat.h---头文件 #ifndef MYCHAT_H #define MYCHAT_H#include <QWidget> #include <QDebug> //打印信息 #include <QIcon> //图标 #include <QPushButton> //按钮 #include <QLineEdit> //行编辑器类 #in…

ERC20 allowance,approve 和 transferFrom

allowance&#xff0c;approve 和 transferFrom&#xff0c;这几个函数提供了一些高级功能&#xff0c;用于授权其他以太坊地址的所有者(spender)代表你使用你的token。这个“其他以太坊地址”可能是一个智能合约&#xff0c;也可能只是一个普通token账户。 ● approve函数。T…

output delay 约束

output delay 约束 一、output delay约束概述二、output delay约束系统同步三、output delay约束源同步 一、output delay约束概述 特别注意&#xff1a;在源同步接口中&#xff0c;定义接口约束之前&#xff0c;需要用create_generated_clock 先定义送出的随路时钟。 二、out…

labview 多线程同步

所谓通讯的同步是指多个线程同时进行或严格按照顺序执行&#xff0c;数据的严格性是指发送多少数据接收多少数据&#xff0c;不能出现数据丢失或重复接收的现象。 labview的同步机制有事件发生、集合点、通知器、信号量。 可以这么来记忆&#xff1a;事急&#xff08;集&…

kotlin高阶函数

kotlin高阶函数 函数式API:一个函数的入参数为Lambda表达式的函数就是函数式api 例子: public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {return filterTo(ArrayList<T>(), predicate) }上面这段函数: 首先这个函…

Nginx配置server_name讲解

文章目录 1.Nginx配置中没有server_name会怎样&#xff1f;2.Nginx配置server_name的匹配规则3.正则表达式规则 1.Nginx配置中没有server_name会怎样&#xff1f; 此时Nginx会自动设置成 server_name ""; 它不会匹配任何域名&#xff0c;导致Nginx会优先将HTTP请求交…

2023年7月第3周大模型荟萃

2023年7月第3周大模型荟萃 2023.7.25版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、华为发布大模型时代 AI 存储新品 7 月 14 日华为在深圳发布了大模型时代 AI 存储新品&#xff0c;为基础模型训练、行业模型训练&#xff0c;细分场…

连锁反应开始了!Linux 发行版迎新变化!

任何企业都有合法权利捍卫其模型和产品。撇开大量不真正了解开源许可证如何工作的人不谈&#xff0c;我们的印象是&#xff0c;有很多人觉得仅仅因为这是Linux&#xff0c;他们就有某种权利免费获得它。但事实上&#xff0c;他们没有。这不是自由软件中的“自由”的意思&#x…

【VCS】(5)Fast RTL-level Verification

Fast RTL-level Verification General Coding GuidlinesLab --- simprofile$display() 输出彩色内容 前面的内容都是在说怎样进行仿真和验证&#xff0c;即如何使用 VCS 。 但是&#xff0c;仿真和验证是不是也有所讲究&#xff1f; 有没有一些标准来衡量设计代码和验证代码的质…

面向初学者的APP开发教程:开始你的编程之旅

不管你是已经在 APP开发行业中工作了很长时间&#xff0c;还是正在学习该领域的知识&#xff0c;都有必要开始学习如何编写一个应用程序。对于初学者来说&#xff0c;编写应用程序的第一步是使用 HTML和 CSS构建一个漂亮的UI。 一旦你学会了这些基本技能&#xff0c;你就可以开…

全志F1C200S嵌入式驱动开发(解决spi加载过慢的问题)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 之前的几个章节当中,我们陆续解决了spi-nor驱动的问题、uboot支持spi-nor的问题。按道理来说,下面要做的应该就是用uboot的loady命令把kernel、dtb、rootfs这些文件下载到ddr,然…

【Milvus】记录一次基于milvus-backup做的Milvus备份与恢复

文章目录 环境代码准备备份构建/运行验证 恢复遇到的问题 环境 milvus&#xff1a;v2.2.4 go&#xff1a;1.20.2 darwin/amd64 milvus-backup&#xff1a;v0.2.2 代码准备 https://github.com/zilliztech/milvus-backup/releases 如果你的milvus是2.2.9版本及以上&#xf…

SAP CAP篇十一:支持Media Object:图片、附件等

文章目录 本系列此前的文章官方文档详细修改更新数据库修改Annotation使其显示在Object Page上 运行结果Fiori Object Page上的Attachment Facet选择完文件后的UI效果前台与后台的交互 对应代码及branch 本系列此前的文章 SAP CAP篇一: 快速创建一个Service&#xff0c;基于Ja…

软件测试如何做到充分性测试?

目录 1 提前介入测试 2 测试分析&#xff0c;测试用例设计 3 测试用例评审 4 严格按照测试用例执行测试 5 分解需求 6 交叉测试 7 重点功能要及时跟踪进行测试充分性分析 做软件测试要想保质保量&#xff0c;就要做到测试充分&#xff0c;什么是测试充分&#xff0c;就是…

将数组和减半的最少操作次数(力扣)

将数组和减半的最少操作次数 题目描述思路测试代码复杂度测试结果 题目描述 给你一个正整数数组 nums 。每一次操作中&#xff0c;你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。&#xff08;注意&#xff0c;在后续操作中你可以对减半过的数继续执行操作&#xf…

(笔记)深度理解-主成分分析PCA

主成分分析 PCA(Principal Component Analysis)&#xff0c;即主成分分析方法&#xff0c;是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上&#xff0c;这k维是全新的正交特征也被称为主成分&#xff0c;是在原有n维特征的基础上重新构造出来的k维特征。…

蛋白质分子结构设计

paper read 1 Created by: 银晗 张 Created time: May 27, 2023 3:47 PM Tags: Product 补充了解蛋白质的生物学知识学习一下Diffusion的原理 &#x1f4a1; Method & Innovations Framework Summary: first deep learning models to perform antibody sequence-stru…

随笔--更改已经启动中的容器的配置文件

文章目录 docker 容器的配置信息地址修改文件映射 docker 容器的配置信息地址 # 一般在 sudo su cd /cd /var/lib/docker/containers/{容器id}/ # 查看容器的id,CONTAINER ID就是容器id的前部分 docker ps修改文件映射 进入容器的配置文件位置一般包含这些文件 # 先stop容器…

0基础学习VR全景平台篇 第69篇:VR直播-如何设置广告

直播间可以插入轮播广告&#xff0c;并且支持外链跳转&#xff0c;能够有效地提升VR直播活动的转化率。 1、点击&#xff0c;添加广告 2、广告图展现形式分为两种&#xff1a;普通广告和全屏广告&#xff0c;普通广告在非全屏播放的直播间显示&#xff0c;全屏广告在全屏播放的…

特征选择策略:为检测乳腺癌生物标志物寻找新出口

内容一览&#xff1a;microRNA&#xff08;小分子核糖核酸&#xff09;是一类短小的单链非编码 RNA 转录体。这些分子在多种恶性肿瘤中呈现失控性生长&#xff0c;因此近年来被诸多研究确定为确诊癌症的可靠的生物标志物 (biomarker)。在多种病理分析中&#xff0c;差异表达分析…