使用Android Jetpack Compose渲染效果打造酷炫的动画效果

如何在Android Jetpack Compose中使用渲染效果打造令人惊艳的视觉体验

学习示例:如何使用渲染效果来改变UI界面
效果图

引言

Jetpack Compose提供了各种工具和组件来构建引人入胜的UI,而在Compose中较为鲜为人知的一个宝藏是RenderEffect。

在这篇博文中,我们将通过创建一些渲染效果的酷炫示例来探索RenderEffect。

什么是RenderEffect?

RenderEffect允许你向UI组件应用视觉效果。这些效果可以包括模糊、自定义着色器或者你能想象到的任何其他视觉转换。然而,RenderEffect仅适用于API 31及以上版本。

在我们的示例中,我们将使用RenderEffect为我们的可扩展浮动按钮和一些额外组件创建模糊和着色器效果。

开始

BlurContainer

在第一阶段,让我们介绍“BlurContainer”。这个独特的组件为我们的用户界面增添了额外的视觉优雅和吸引力,创造出令人惊叹的视觉效果。

它包含一个自定义模糊修饰符,将我们的渲染效果提升到了新的水平。

@Composable
fun BlurContainer(modifier: Modifier = Modifier,blur: Float = 60f,component: @Composable BoxScope.() -> Unit,content: @Composable BoxScope.() -> Unit = {},
) {Box(modifier, contentAlignment = Alignment.Center) {Box(modifier = Modifier.customBlur(blur),content = component,)Box(contentAlignment = Alignment.Center) {content()}}
}fun Modifier.customBlur(blur: Float) = this.then(graphicsLayer {if (blur > 0f)renderEffect = RenderEffect.createBlurEffect(blur,blur,Shader.TileMode.DECAL,).asComposeRenderEffect()}
)
  • “customBlur”修饰符扩展接受一个模糊参数,用于指定模糊效果的强度。
  • 它被用来向可组合应用graphicsLayer,进而使用RenderEffect. createBlurEffect来应用模糊效果。graphicsLayer被用来将渲染效果应用到可组合中。
    以下是模糊效果的样子:
    BlurContainer’s Effect

通过这个修饰符,我们可以通过将其链接到现有的修饰符,轻松地向任何可组合添加模糊效果。

将渲染效果应用于父容器

为此,我们将使用自定义着色器——RuntimeShader和Jetpack Compose的graphicsLayer来在父容器中实现所需的视觉效果。
在我们深入了解渲染效果是如何应用之前,让我们先了解一下如何初始化RuntimeShader

@Language("AGSL")
const val ShaderSource = """uniform shader composable;uniform float visibility;half4 main(float2 cord) {half4 color = composable.eval(cord);color.a = step(visibility, color.a);return color;}
"""val runtimeShader = remember {RuntimeShader(ShaderSource)
}

在这段代码片段中,我们创建了一个RuntimeShader实例。remember函数确保着色器只被初始化一次,避免不必要的开销。我们将自定义的着色器源代码(ShaderSource)传递给RuntimeShader的构造函数。

我们的ShaderSource是渲染效果的关键部分。它是用一种名为AGSL(Android Graphics Shading Language)的着色器语言编写的。让我们更仔细地看一下:

  • uniform shader composable:这一行声明了一个名为"composable"的统一着色器变量。如果我们想要应用渲染效果,这个变量将被用来取样可组合元素的颜色。
  • uniform float visibility:我们声明了一个名为"visibility"的统一浮点数变量。这个变量通过指定一个阈值来控制着色器效果的强度。
  • half4 main(float2 cord):main函数是着色器的入口点。它接受一个2D坐标(cord),并以half4的形式返回一个颜色,表示具有红色、绿色、蓝色和alpha分量的颜色。
  • half4 color = composable.eval(cord):在这里,我们从给定坐标处的"composable"着色器统一变量中取样颜色。
  • color.a = step(visibility, color.a):我们通过根据"visibility"阈值将alpha分量(color.a)设置为0或1来应用着色器效果。
  • return color:最后,我们返回修改后的颜色。

在JetLagged应用程序的compose-samples中查看AGSL Shader。

https://github.com/android/compose-samples/tree/main/JetLagged

应用渲染效果

有了我们的RuntimeShaderShaderSource准备就绪,现在我们可以使用graphicsLayer来应用渲染效果了:

Box(modifier.graphicsLayer {runtimeShader.setFloatUniform("visibility", 0.2f)renderEffect = RenderEffect.createRuntimeShaderEffect(runtimeShader, "composable").asComposeRenderEffect()},content = content,
)

以下是它的工作原理:

  • runtimeShader.setFloatUniform("visibility", 0.2f):我们设置着色器中的"visibility"统一变量,以控制效果的强度。在这种情况下,我们将其设置为0.2f,但您可以调整此值以实现您期望的效果。
  • renderEffect = RenderEffect.createRuntimeShaderEffect(...):我们使用createRuntimeShaderEffect方法创建一个RenderEffect。该方法接受我们的runtimeShader和名称"composable",该名称对应于ShaderSource中的着色器变量。
  • asComposeRenderEffect():我们使用asComposeRenderEffect()RenderEffect转换为适用于Compose的格式。

通过在graphicsLayer中应用这个渲染效果,我们可以在包含在Box中的UI组件上实现着色器效果。

为了将所有这些元素整合在一起并无缝地应用我们的渲染效果,我们将创建一个名为ShaderContainer的可组合元素,具体如下:

@Language("AGSL")
const val Source = """uniform shader composable;uniform float visibility;half4 main(float2 cord) {half4 color = composable.eval(cord);color.a = step(visibility, color.a);return color;}
"""@Composable
fun ShaderContainer(modifier: Modifier = Modifier,content: @Composable BoxScope.() -> Unit,
) {val runtimeShader = remember {RuntimeShader(Source)}Box(modifier.graphicsLayer {runtimeShader.setFloatUniform("visibility", 0.2f)renderEffect = RenderEffect.createRuntimeShaderEffect(runtimeShader, "composable").asComposeRenderEffect()},content = content)
}

这是BlurContainer包裹在ShaderContainer中产生的视觉效果:
Parent

现在我们已经成功地通过ShaderContainerBlurContainer为我们的渲染效果奠定了基础,是时候通过打造ExtendedFabRenderEffect将它们整合在一起了。这个可组合元素将成为我们的可展开浮动按钮和动态渲染效果的核心。

ExtendedFabRenderEffect

ExtendedFabRenderEffect可组合元素负责协调整个用户界面、按钮展开的动画和处理渲染效果。让我们深入探讨它的工作原理,以及它如何创造视觉上令人愉悦的用户体验。

流畅的动画

创建流畅而流畅的动画对于精致的用户体验至关重要。我们应用alpha动画来实现这一点:

alpha动画管理按钮的透明度。当展开为true时,按钮变得完全不透明;否则,它们逐渐消失。与偏移动画一样,我们使用animateFloatAsState函数和适当的参数来确保平滑的过渡。

var expanded: Boolean by remember {mutableStateOf(false)
}val alpha by animateFloatAsState(targetValue = if (expanded) 1f else 0f,animationSpec = tween(durationMillis = 1000, easing = LinearEasing),label = ""
)

组合效果

现在,我们将渲染效果ShaderContainer与按钮组合在一起,以创建一个统一的用户界面。在ShaderContaine内部,我们放置了几个ButtonComponent可组合元素,每个代表一个具有特定图标和交互的按钮。

ShaderContainer(modifier = Modifier.fillMaxSize()
) {ButtonComponent(Modifier.padding(paddingValues = PaddingValues(bottom = 80.dp) * FastOutSlowInEasing.transform((alpha))),onClick = {expanded = !expanded}) {Icon(imageVector = Icons.Default.Edit,contentDescription = null,tint = Color.White,modifier = Modifier.alpha(alpha))}ButtonComponent(Modifier.padding(paddingValues = PaddingValues(bottom = 160.dp) * FastOutSlowInEasing.transform(alpha)),onClick = {expanded = !expanded}) {Icon(imageVector = Icons.Default.LocationOn,contentDescription = null,tint = Color.White,modifier = Modifier.alpha(alpha))}ButtonComponent(Modifier.padding(paddingValues = PaddingValues(bottom = 240.dp) * FastOutSlowInEasing.transform(alpha)),onClick = {expanded = !expanded}) {Icon(imageVector = Icons.Default.Delete,contentDescription = null,tint = Color.White,modifier = Modifier.alpha(alpha))}ButtonComponent(Modifier.align(Alignment.BottomEnd),onClick = {expanded = !expanded},) {val rotation by animateFloatAsState(targetValue = if (expanded) 45f else 0f,label = "",animationSpec = tween(1000, easing = FastOutSlowInEasing))Icon(imageVector = Icons.Default.Add,contentDescription = null,modifier = Modifier.rotate(rotation),tint = Color.White)}
}

有了这个设置,ShaderContainer充当了我们按钮的背景,渲染效果通过ButtonComponent可组合元素无缝地应用到按钮上。alpha修饰符确保按钮根据展开状态变得可见或不可见,从而创建出精致而动态的用户界面。

按钮组件结构

ButtonComponent旨在封装可展开菜单中的每个按钮,它提供了定制按钮外观和行为的灵活性。

以下是ButtonComponent的结构:

@Composable
fun BoxScope.ButtonComponent(modifier: Modifier = Modifier,background: Color = Color.Black,onClick: () -> Unit,content: @Composable BoxScope.() -> Unit
) {// Applying the Blur Effect with the BlurContainerBlurContainer(modifier = modifier.clickable(interactionSource = remember {MutableInteractionSource()},indication = null,onClick = onClick,).align(Alignment.BottomEnd),component = {Box(Modifier.size(40.dp).background(color = background, CircleShape))}) {// Content (Icon or other elements) inside the buttonBox(Modifier.size(80.dp),content = content,contentAlignment = Alignment.Center,)}
}

这样,我们已经从以上的代码中实现了期望的效果!
ExtendedFabRenderEffect

TextRenderEffect

TextRenderEffect的核心是动态文本显示。我们将使用一系列激励性的短语和引语来呈现给用户。这些短语将包括"实现你的目标"、"追逐你的梦想"等情感。

val animateTextList =listOf("\"Reach your goals\"","\"Achieve your dreams\"","\"Be happy\"","\"Be healthy\"","\"Get rid of depression\"","\"Overcome loneliness\"")

我们将创建一个textToDisplay状态变量来保存并显示这些短语,从而创建一个动态的序列。

文本动画化

为了使文本显示更吸引人,我们将利用一些关键动画:

  • 模糊效果:我们将对文本应用模糊效果。模糊值从0到30再返回0进行动画处理,采用线性缓动动画。这会创造出微妙而迷人的视觉效果,增强了文本的外观。
  • 文本转换:我们将使用LaunchedEffect循环显示短语列表中的每个短语,每个短语显示一定时间。当textToDisplay发生变化时,将发生一个scaleIn动画,呈现新文本的放大效果;随着过渡的结束,会应用一个scaleOut效果。这提供了一个视觉上令人愉悦的方式来引入和退出文本。

与ShaderContainer的完全集成

@Composable
fun TextRenderEffect() {val animateTextList =listOf("\"Reach your goals\"","\"Achieve your dreams\"","\"Be happy\"","\"Be healthy\"","\"Get rid of depression\"","\"Overcome loneliness\"")var index by remember {mutableIntStateOf(0)}var textToDisplay by remember {mutableStateOf("")}val blur = remember { Animatable(0f) }LaunchedEffect(textToDisplay) {blur.animateTo(30f, tween(easing = LinearEasing))blur.animateTo(0f, tween(easing = LinearEasing))}LaunchedEffect(key1 = animateTextList) {while (index <= animateTextList.size) {textToDisplay = animateTextList[index]delay(3000)index = (index + 1) % animateTextList.size}}ShaderContainer(modifier = Modifier.fillMaxSize()) {BlurContainer(modifier = Modifier.fillMaxSize(),blur = blur.value,component = {AnimatedContent(targetState = textToDisplay,modifier = Modifier.fillMaxWidth(),transitionSpec = {(scaleIn()).togetherWith(scaleOut())}, label = "") { text ->Text(modifier = Modifier.fillMaxWidth(),text = text,style = MaterialTheme.typography.headlineLarge,color = MaterialTheme.colorScheme.onPrimaryContainer,textAlign = TextAlign.Center)}}) {}}
}

TextRenderEffect

ImageRenderEffect

在Jetpack Compose中,我们继续探索RenderEffect,这次着眼于引人入胜的ImageRenderEffect。这个可组合项通过引入动态图像过渡和迷人的渲染效果,将图像渲染提升到了一个新的层次。让我们深入了解其构造方式以及它如何增强视觉体验。

动态图像过渡

ImageRenderEffect的核心在于其能够以视觉上吸引人的方式在图像之间进行过渡。为了演示这一点,我们将设定一个基本情景,即在点击事件中,两个图像ic_firstic_second将交替显示。

var image by remember {mutableIntStateOf(R.drawable.ic_first)
}

图像状态变量保存当前显示的图像,用户只需简单点击按钮,就可以在两者之间切换。

打造引人入胜的效果

模糊效果:就像我们之前的示例一样,我们对图像应用模糊效果。模糊值从0到100再返回0进行动画处理,创造出令人着迷的视觉效果,增强了图像过渡效果。

val blur = remember { Animatable(0f) }
LaunchedEffect(image) {blur.animateTo(100f, tween(easing = LinearEasing))blur.animateTo(0f, tween(easing = LinearEasing))
}
  • 图像过渡:图像过渡的核心是AnimatedContent可组合项。它处理图像之间的平滑过渡,结合了fadeInscaleIn效果,用于进入场景的图像,以及fadeOutscaleOut效果,用于退出场景的图像。
AnimatedContent(targetState = image,modifier = Modifier.fillMaxWidth(),transitionSpec = {(fadeIn(tween(easing = LinearEasing)) + scaleIn(tween(1_000, easing = LinearEasing))).togetherWith(fadeOut(tween(1_000, easing = LinearEasing)) + scaleOut(tween(1_000, easing = LinearEasing)))}, label = ""
) { image ->Image(painter = painterResource(id = image),modifier = Modifier.size(200.dp),contentDescription = "")
}

ShaderContainer的完美集成

就像我们之前的示例一样,ImageRenderEffect也集成在ShaderContainer中。这使我们能够融合图像过渡和渲染效果,创造出引人入胜、沉浸式的视觉体验。

@Composable
fun ImageRenderEffect() {var image by remember {mutableIntStateOf(R.drawable.ic_first)}val blur = remember { Animatable(0f) }LaunchedEffect(image) {blur.animateTo(100f, tween(easing = LinearEasing))blur.animateTo(0f, tween(easing = LinearEasing))}Column(modifier = Modifier.wrapContentSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center,) {ShaderContainer(modifier = Modifier.animateContentSize().clipToBounds().fillMaxWidth()) {BlurContainer(modifier = Modifier.fillMaxWidth(),blur = blur.value,component = {AnimatedContent(targetState = image,modifier = Modifier.fillMaxWidth(),transitionSpec = {(fadeIn(tween(easing = LinearEasing)) + scaleIn(tween(1_000,easing = LinearEasing))).togetherWith(fadeOut(tween(1_000,easing = LinearEasing)) + scaleOut(tween(1_000,easing = LinearEasing)))}, label = "") { image ->Image(painter = painterResource(id = image),modifier = Modifier.size(200.dp),contentDescription = "")}}) {}}Spacer(modifier = Modifier.height(20.dp))Button(onClick = {image =if (image == R.drawable.ic_first) R.drawable.ic_second else R.drawable.ic_first},colors = ButtonDefaults.buttonColors(containerColor = Color.Black)) {Text("Change Image")}}
}

ImageRenderEffect

结论

通过了解ShaderContainerBlurContainerShaderSource以及customBlur修饰符,您拥有了在Jetpack Compose应用程序中创建令人惊叹的渲染效果的工具。这些元素为探索和尝试各种视觉效果和自定义着色器提供了基础,为您的UI设计打开了创意可能性的世界。

Github

https://github.com/cp-megh-l/render-effect-jetpack-compose

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

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

相关文章

Linux文件系统的功能规划

对于运行的进程来说&#xff0c;内存就像一个纸箱子&#xff0c;仅仅是一个暂存数据的地方&#xff0c;而且空间有限。如果我们想要进程结束之后&#xff0c;数据依然能够保存下来&#xff0c;就不能只保存在内存里&#xff0c;而是应该保存在外部存储中。就像图书馆这种地方&a…

操作系统引论(二)

操作系统发展动力及技术基础 推动操作系统的发展和资源利用率的提高是相关联的。 让输入输出相对独立&#xff0c;通道技术&#xff0c;通道相当于简单的处理器&#xff0c;通过输入输出指令&#xff0c;控制外设完成输入输出。 输入和输出过程不是由主机控制的&#xff0c;是…

flink的AggregateFunction,merge方法作用范围

背景 AggregateFunction接口是我们经常用的窗口聚合函数&#xff0c;其中有一个merge方法&#xff0c;我们一般情况下也是实现了的&#xff0c;但是你知道吗&#xff0c;其实这个方法只有在你使用会话窗口需要进行窗口合并的时候才需要实现 AggregateFunction.merge方法调用时…

大数据技术之集群数据迁移

文章目录 数据治理之集群迁移数据 数据治理之集群迁移数据 准备两套集群&#xff0c;我这使用apache集群和CDH集群。 启动集群 启动完毕后&#xff0c;将apache集群中&#xff0c;hive库里dwd,dws,ads三个库的数据迁移到CDH集群 在apache集群里hosts加上CDH Namenode对应域名并…

bff层解决了什么痛点

bff层 -- 服务于前端的后端 什么是bff&#xff1f; Backend For Frontend&#xff08;服务于前端的后端&#xff09;&#xff0c;也就是服务器设计API的时候会考虑前端的使用&#xff0c;并在服务端直接进行业务逻辑的处理&#xff0c;又称为用户体验适配器。BFF只是一种逻辑…

【hcie-cloud】【2】华为云Stack解决方案介绍、缩略语整理 【下】

文章目录 华为文档获取方式、云计算发展背景、坚实基座华为云Stack&#xff0c;政企只能升级首选智能数据湖仓一体&#xff0c;让业务洞见更准&#xff0c;价值兑现更快MRS&#xff1a;一个架构可构建三种数据湖&#xff0c;业务场景更丰富离线数据湖&#xff1a;提供云原生、湖…

服务器感染了.locked勒索病毒,如何确保数据文件完整恢复?

引言&#xff1a; 网络安全威胁的不断演变使得恶意软件如.locked勒索病毒成为当今数字时代的一大挑战。.locked勒索病毒能够加密您的文件&#xff0c;然后要求支付赎金以解锁它们。本文将深入探讨.locked勒索病毒的特点&#xff0c;以及如何应对感染&#xff0c;以及预防这种类…

3款免费又好用的 Docker 可视化管理工具

前言 Docker提供了命令行工具&#xff08;Docker CLI&#xff09;来管理Docker容器、镜像、网络和数据卷等Docker组件。我们也可以使用可视化管理工具来更方便地查看和管理Docker容器、镜像、网络和数据卷等Docker组件。今天我们来介绍3款免费且好用的 Docker 可视化管理工具。…

网络安全深入学习第八课——反向代理(工具:frp)

文章目录 一、实验环境二、实验要求三、开始模拟1、攻击机配置frp文件2、攻击拿下跳板机&#xff0c;并且上传frpc.ini、frpc.exe、frpc_full.ini文件3、把frps.ini、、frps.exe、frps_full.ini文件放到VPS主机上4、VPS机开启frp5、跳板机开启frp6、验证 一、实验环境 攻击机&…

云尘 命令执行系列

第一题 system <?php include "flag.php";if (isset($_POST[cmd])) {system($_POST[cmd]); }show_source(__FILE__);代码如上 system($_POST[cmd]); POST请求发送一个名为 cmd 的参数&#xff0c;然后将该参数的值传递给系统命令执行函数 system()&#xff0c…

NVIDIA大模型平台软件全家桶开启云智能第二曲线

第二曲线由英国管理思想大师查尔斯汉迪提出&#xff0c;讲的是在企业第一曲线达到巅峰的时候&#xff0c;找到驱动企业二次腾飞的第二曲线。而如果企业想实现基业长青&#xff0c;就需要通过主动式破局式创新&#xff0c;跨越到第二曲线中。对于当下的云智能产业以及基于云智能…

【电源专题】PSE如何与PD握手协商功率等级?

在文章:【电源专题】POE连接方式与功率等级划分 中我们讲到POE协议对不同的PD设备进行划分,比如根据不同的供电标准又可以细分成好几种不同的类型(Type1~Type4)和功率等级。 那么有这么多功率等级,PSE怎么知道PD是哪种类型呢?怎么能进行握手协商呢? 下图为PSE与PD设备在…

python 之生成器表达式,以及与列表推导式的区别

文章目录 生成器表达式基本结构示例生成一个简单的生成器遍历生成器并获取值使用条件过滤 优点 生成器表达式与列表推导式的区别1. 返回类型2. 生成方式3. 内存占用4. 访问方式示例总结 生成器表达式是一种在 Python 中用来创建生成器的高效方法。生成器表达式和列表推导式类似…

jstat虚拟机统计信息监控工具

jstat虚拟机统计信息监控工具 1、jstat&#xff08;JVM Statistics Monitorning Tool&#xff09; 用于监控虚拟机各种运行状态信息的命令行工具。 它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据&#xff0c;它是运行期定位虚拟机 性能问题…

数据结构——基于顺序表实现通讯录

一、. 基于动态顺序表实现通讯录 1.1 功能要求 1&#xff09;⾄少能够存储100个⼈的通讯信息 2&#xff09;能够保存⽤⼾信息&#xff1a;名字、性别、年龄、电话、地址等 3&#xff09;增加联系⼈信息 4&#xff09;删除指定联系⼈ 5&#xff09;查找制定联系⼈ 6&…

vuepress 打包后左侧菜单链接 404 问题解决办法

背景 上周看到一本开源书 《深入架构原理与实践》&#xff0c;是基于 vuepress 搭建的&#xff0c;下载了源码&#xff0c;本地部署了一下&#xff0c;本文记录如何打包该源码遇到的路径问题及思考。 结论&#xff1a; vuepress 插件的 sideBar 的菜单路径默认是相对 / 的&am…

FPGA高端项目:图像缩放+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明本项目特点 2、相关方案推荐我这里已有的 GT 高速接口解决方案我这里已有的以太网方案我这里已有的图像处理方案 3、设计思路框架设计框图视频源选择ADV7611 解码芯片配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择…

【Java 进阶篇】MVC 模式

欢迎来到本篇详细解释 MVC&#xff08;Model-View-Controller&#xff09;设计模式的教程。MVC 是一种用于组织应用程序的设计模式&#xff0c;有助于将应用程序分成不同的部分&#xff0c;以提高代码的可维护性和可扩展性。在本文中&#xff0c;我们将深入研究 MVC 模式&#…

SpringBoot整合定时任务遇到的多实例问题

唠嗑部分 是这样&#xff0c;前几日完善了定时任务的日志记录&#xff0c;今日切换了服务器&#xff0c;多部署了一个节点&#xff0c;使用nginx负载均衡&#xff0c;但是查看日志却发现了如下情况 那糟糕了&#xff0c;传说中的多实例问题出现了&#xff0c;今天我们就来聊聊…

虚幻引擎 5.1 中全新的增强型输入操作系统

教程链接 https://www.youtube.com/watch?vCYiHNbAIp4s 前提 虚幻引擎5.1之后&#xff0c;项目设置里的input选项&#xff0c;默认会有一条警告&#xff0c;告知旧的input系统已经不能用了。 做法 在content文件夹下新建一个input按钮 input文件夹里面分成两部分内容 1.…