开源项目分享一个非常简单易用的Compose版骨架屏,快来看看!
Posted Android开发骆驼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了开源项目分享一个非常简单易用的Compose版骨架屏,快来看看!相关的知识,希望对你有一定的参考价值。
Jetpack Compose是谷歌在2019Google i/o大会上发布的新的库,一个用于构建原生android UI的现代工具包。他有强大的工具和直观的Kotlin API,简化并加速了Android上的UI开发。可以帮助开发者用更少更直观的代码创建View,有更强大的功能,还能提高开发速度。
今天要和大家分享的是一个简单易用的Compose版骨架屏,笔者觉得用起来挺棒,大家可以看看。希望对大家的学习和工作有所帮助。
(PS:开源地址在文末)
前言
骨架屏是页面的一个空白版本,通常会在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容。
骨架屏加载中效果,比起传统的加载中效果可以提供更多信息,用户体验更好,因此也变得越来越流行
本文主要介绍如何使用Compose实现一个简单易用的骨架屏效果
效果图
首先看下最终的效果图
特性
- 简单易用,可复用页面UI,不需要针对骨架屏定制UI
- 支持设置骨架屏是否显示,一般结合加载状态使用
- 支持设置骨架屏背景与高亮颜色
- 支持设置骨架屏高度部分宽度,渐变部分宽度
- 支持设置骨架屏动画的角度与方向
- 支持设置骨架屏动画的时间与两次动画间隔
使用
接入
第 1 步:在工程的build.gradle中添加:
allprojects
repositories
...
mavenCentral()
第2步:在应用的build.gradle中添加:
dependencies
implementation 'io.github.shenzhen2017:shimmer:1.0.0'
简单使用
@Composable
fun ShimmerSample()
var loading: Boolean by remember
mutableStateOf(true)
Column(
modifier = Modifier
.fillMaxWidth()
.shimmer(loading,config = ShimmerConfig())
)
repeat(3)
PlaceHolderItem()
Spacer(modifier = Modifier.height(10.dp))
如上所示:
- 只需要在Column的Modifier中加上shimmer,Column下的所有组件即可实现骨架屏效果
- 可通过loading参数,控制骨架屏效果是否显示
- 如果需要定制骨架屏动画效果,也可通过一些参数配置
具体主要有以下这些参数
data class ShimmerConfig(
// 未高亮部分颜色
val contentColor: Color = Color.LightGray.copy(alpha = 0.3f),
// 高亮部分颜色
val higLightColor: Color = Color.LightGray.copy(alpha = 0.9f),
// 渐变部分宽度
@FloatRange(from = 0.0, to = 1.0)
val dropOff: Float = 0.5f,
// 高亮部分宽度
@FloatRange(from = 0.0, to = 1.0)
val intensity: Float = 0.2f,
//骨架屏动画方向
val direction: ShimmerDirection = ShimmerDirection.LeftToRight,
//动画旋转角度
val angle: Float = 20f,
//动画时长
val duration: Float = 1000f,
//两次动画间隔
val delay: Float = 200f
)
主要原理
通过图像混合模式复用页面UI
如果我们要实现骨架屏效果,首先想到的是需要按照页面的结构再写一套UI,然后在加载中的时候,显示这套UI,否则隐藏
一般的加载中效果都是这样实现的,但这样会带来一个问题,不同的页面结构不同,那我们岂不是要一个页面就重写一套UI?这显然是不可接受的
我们可以想到,页面的结构其实我们已经写过一遍了,如果我们能复用我们写的页面结构不就好了吗?
我们可以通过图像混合模式来实现这一点
图像混合模式定义的是,当两个图像合成时,图像最终的展示方式。在Androd中,有相应的API接口来支持图像混合模式,即Xfermode.
图像混合模式主要有以下16种,以下这张图片从一定程度上形象地说明了图像混合的作用,两个图形一圆一方通过一定的计算产生不同的组合效果,具体如下
我们介绍几个常用的,其它的感兴趣的同学可自行查阅
- SRC_IN:只在源图像和目标图像相交的地方绘制【源图像】
- DST_IN:只在源图像和目标图像相交的地方绘制【目标图像】,绘制效果受到源图像对应地方透明度影响
- SRC_OUT:只在源图像和目标图像不相交的地方绘制【源图像】,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤
- DST_OUT:只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤
如果我们把页面的UI结构作为目标图像,骨架屏效果作为源图像,然后使用SRC_IN混合模式,就可以实现只在页面的结构上显示骨架屏,在空白部分不显示,这样就可以避免重复写UI了
通过平移实现动画效果
上面我们已经实现了在页面结构上显示骨架屏,但是骨架屏效果还有一个动画效果
其实也很简单,给骨架屏设置一个渐变效果,然后做一个平移动画,然后看起来就是现在的骨架屏闪光动画了
fun Modifier.shimmer(): Modifier = composed
var progress: Float by remember mutableStateOf(0f)
val infiniteTransition = rememberInfiniteTransition()
progress = infiniteTransition.animateFloat().value // 动画效果,计算百分比
ShimmerModifier(visible = visible, progress = progress, config = config)
internal class ShimmerModifier(progress:Float) : DrawModifier, LayoutModifier
private val paint = Paint().apply
blendMode = BlendMode.SrcIn //设置混合模式
shader = LinearGradientShader(Offset(0f, 0f),toOffset,colors,colorStops)//设置渐变色
override fun ContentDrawScope.draw()
drawContent()
val (dx, dy) = getOffset(progress) //根据progress,设置平移的位置
paint.shader?.postTranslate(dx, dy) // 平移操作
it.drawRect(Rect(0f, 0f, size.width, size.height), paint = paint)//绘制骨架屏效果
如上所示,主要是几步:
- 启动动画,获得当前进度progress,并根据progress获得当前平移的位置
- 设置骨架屏的背景渐变颜色与混合模式
- 绘制骨架屏效果
自定义骨架屏效果
上面介绍了我们提供了一些参数,可以自定义骨架屏的效果,其它参数都比较好理解,主要是以下两个参数有点难理解
- dropOff:渐变部分宽度
- intensity: 高亮部分宽度
我们知道,可以通过contentColor自定义普通部分颜色,higLightColor自定义高亮部分颜色
但是这两种颜色是如何分布的呢?渐变的比例是怎样的呢?可以看下下面的代码:
private val paint = Paint().apply
shader = LinearGradientShader(Offset(0f, 0f),toOffset,colors,colorStops)//设置渐变色
private val colors = listOf(
config.contentColor,
config.higLightColor,
config.higLightColor,
config.contentColor
)
private val colorStops: List<Float> = listOf(
((1f - intensity - dropOff) / 2f).coerceIn(0f, 1f),
((1f - intensity - 0.001f) / 2f).coerceIn(0f, 1f),
((1f + intensity + 0.001f) / 2f).coerceIn(0f, 1f),
((1f + intensity + dropOff) / 2f).coerceIn(0f, 1f)
)
可以看出,我们的颜色渐变有以下特点:
- 渐变颜色分布为:contentColor->higLightColor->higLightColor->contentColor
- LinearGradientShader使用colors定义颜色,colorStops定义颜色渐变的分布,colorStops由intensity与dropoff计算得来
- intensity决定了高亮部分的宽度,即intensity越大,高亮部分越大
- dropOff决定了渐变部分的宽度,即dropOff越大,渐变部分越大
总结
特别鸣谢
在实现Compose版本骨架屏的过程中,主要借鉴了以下开源框架的思想,有兴趣的同学也可以了解下
Facebook开源的shimmer-android
Habib Kazemi开源的compose-shimmer
项目地址
简单易用的Compose版骨架屏
开源不易,如果项目对你有所帮助,欢迎点赞,Star,收藏~
学习资料整理
整理总目录
第一章 初识 Jetpack Compose 整理信息
-
为什么我们需要一个新的UI 工具?
-
Jetpack Compose的着重点
加速开发
强大的UI工具
直观的Kotlin API
-
API 设计
-
Compose API 的原则
一切都是函数
顶层函数(Top-level function)
组合优于继承
信任单一来源 -
深入了解Compose
Core
Foundation
Material
-
插槽API
第二章 Jetpack Compose构建Android UI 整理信息
-
Android Jetpack Compose 最全上手指南
Jetpack Compose 环境准备和Hello World
布局
使用Material design 设计
Compose 布局实时预览
……
-
深入详解 Jetpack Compose | 优化 UI 构建
Compose 所解决的问题
Composable 函数剖析
声明式 UI
组合 vs 继承
封装
重组
……
-
深入详解 Jetpack Compose | 实现原理
@Composable 注解意味着什么?
执行模式
Positional Memoization (位置记忆化)
存储参数
重组
……
第三章 Jetpack Compose 项目实战演练(附Demo) 整理信息
-
Jetpack Compose应用1
开始前的准备
创建DEMO
遇到的问题
-
Jetpack Compose应用2
-
Jetpack Compose应用做一个倒计时器
数据结构
倒计时功能
状态模式
Compose 布局
绘制时钟
-
用Jetpack Compose写一个玩安卓App
准备工作
引入依赖
新建 Activity
创建 Compose
PlayTheme
画页面
底部导航栏
管理状态
添加页面 -
用Compose Android 写一个天气应用
开篇
画页面
画背景
画内容
…… -
用Compose快速打造一个“电影App”
成品
实现方案
实战
不足
……
有需要的朋友可以【扫描下方二维码】直接找我免费领取。
希望这份资料可以给想了解、学习、应用Android Jetpack Compose的小伙伴一个参考。
以上是关于开源项目分享一个非常简单易用的Compose版骨架屏,快来看看!的主要内容,如果未能解决你的问题,请参考以下文章
开源项目分享一个非常简单易用的Compose版骨架屏,快来看看!
[Android开源]一个非常简单易用用来花式展示二维码样式生成的库QRCodeStyle
[Android开源]一个非常简单易用用来花式展示二维码样式生成的库QRCodeStyle