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

news2024/11/23 2:34:06

如何在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 BlurContainer
    BlurContainer(
        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 button
        Box(
            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.coloradmin.cn/o/1181840.html

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

相关文章

freertos任务参数

实现多个任务利用同一个函数&#xff0c;传进不同的任务参数&#xff0c;打印不同的任务内容。多个人用同一个电脑干不同工作&#xff0c;美工用电脑干美工&#xff0c;程序员用电脑敲代码 #include "stm32f10x.h" // Device header #include &quo…

.NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth

前言 我相信做开发的同学应该都对接过各种各样的第三方平台的登录授权&#xff0c;来获取用户信息&#xff08;如&#xff1a;微信登录、支付宝登录、QQ登录、GitHub登录等等&#xff09;。今天给大家推荐一个.NET开源好用的、全面的、方便第三方登录组件集合框架&#xff1a;…

【ES专题】ElasticSearch功能详解与原理剖析

目录 前言要点阅读对象阅读导航前置知识笔记正文一、ES数据预处理1.1 Ingest Node&#xff1a;摄入节点1.2 Ingest Pipeline&#xff1a;摄入管道1.3 Processor&#xff1a;预处理器——简单加工1.4 Painless Script&#xff1a;脚本——复杂加工1.5 简单实用案例 二、文档/数据…

C语言——从键盘任意输人一个三位数的自然数,求该数个位、十位、百位上的数字之和

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> int main() {int i,ge,shi,bai;printf("输入一个三位数整数&#xff1a;\n");scanf("%d",&i);gei%10; //个位shii%100/10; //十位baii/100; //百位printf("个位:%d,十位:%d,百位:…

51基于matlab模拟退火算法矩形排样

基于matlab模拟退火算法矩形排样&#xff0c;基于最低水平线算法完成矩形板材下料优化&#xff0c;输出最优剩料率和最后的水平线&#xff0c;可替换自己的数据进行优化&#xff0c;程序已调通&#xff0c;可直接运行。 51matlab模拟退火算法矩形排样 (xiaohongshu.com)

两个栈实现队列

要用两个栈实现队列&#xff0c;就需要了解栈和队列的特性&#xff0c;栈是先进后出&#xff0c;队列是先进先出。基本思路是&#xff0c;把数据先压入栈1中&#xff0c;然后数据在栈1中输出再压入栈2&#xff0c;输出后就能实现队列的先进先出。 package Package;import java.…

实施数字工厂管理系统对印刷企业来说有什么意义

随着科技的飞速发展&#xff0c;数字化转型已成为各行各业不可避免的趋势。对于印刷企业而言&#xff0c;实施数字工厂管理系统不仅意味着技术的升级&#xff0c;更关乎企业整体的运营效率、成本控制、市场竞争力等方面的提升。本文将详细分析实施数字工厂管理系统对印刷企业的…

Redis7--基础篇2(Redis的十大数据类型及常用命令)

1. Redis的十大数据类型及常用命令 Redis是key-value键值对类型的数据库&#xff0c;我们所说的数据类型指的是value的数据类型&#xff0c;key的数据类型都是字符串。 1.1 字符串&#xff08;String&#xff09; string是redis最基本的类型&#xff0c;一个key对应一个val…

Docker 学习路线 10:容器安全

容器安全是实施和管理像Docker这样的容器技术的关键方面。它包括一组实践、工具和技术&#xff0c;旨在保护容器化应用程序及其运行的基础架构。在本节中&#xff0c;我们将讨论一些关键的容器安全考虑因素、最佳实践和建议。 容器隔离 隔离对于确保容器化环境的强大性和安全…

CSDN每日一题学习训练——Java版(对给定的两个日期之间的日期进行遍历、子集 II、填充每个节点的下一个右侧节点指针)

版本说明 当前版本号[20231107]。 版本修改说明20231107初版 目录 文章目录 版本说明目录对给定的两个日期之间的日期进行遍历题目解题思路代码思路参考代码 子集 II题目解题思路代码思路参考代码 填充每个节点的下一个右侧节点指针题目解题思路代码思路参考代码 对给定的两…

【计算机组成】实模式/保护模式下地址分段(基段地址+偏移地址)的原因

一.硬编码/静态重定向 我们先来观察下没有地址分段时代CPU是怎么和内存们打交道&#xff0c;在8086CPU以前的老大哥们&#xff0c;访问内存时通常就是实打实的“指哪打哪”&#xff0c;程序指定要放在哪个地址&#xff0c;那就老老实实地放在哪个地址&#xff0c;比如程序A要放…

如何使新手小白编码能力暴涨之Devchat-AI

在这个快速发展的时代&#xff0c;开发者的任务越来越繁重&#xff0c;要求他们快速、高效地完成开发任务。然而&#xff0c;传统的开发方式已经无法满足这个需求。在这种情况下&#xff0c;Devchat的出现给开发者带来了新的帮助。Devchat是一个研发效能分析平台&#xff0c;它…

掌握未来技术趋势:深度学习与量子计算的融合

掌握未来技术趋势&#xff1a;深度学习与量子计算的融合 摘要&#xff1a;本博客将探讨深度学习与量子计算融合的未来趋势&#xff0c;分析这两大技术领域结合带来的潜力和挑战。通过具体案例和技术细节&#xff0c;我们将一睹这两大技术在人工智能、药物研发和金融科技等领域…

【Node.js入门之—1.1Node.js 简介】

Node.js入门之—1.1Node.js 简介 文章目录 Node.js入门之—1.1Node.js 简介什么是 Node.js错误说法 Node.js 的特点跨平台三方类库自带http服务器非阻塞I/O事件驱动单线程 Node.js 的应用场合适合用Node.js的场合不适合用Node.js的场合弥补Node.js不足的解决方案 什么是 Node.j…

Oracle RAC是啥?

Oracle RAC&#xff0c;全称是Oracle Real Application Cluster&#xff0c;翻译过来为Oracle真正的应用集群&#xff0c;它是Oracle提供的一个并行集群系统&#xff0c;由 Oracle Clusterware&#xff08;集群就绪软件&#xff09; 和 Real Application Cluster&#xff08;RA…

Linux之如何使用git提交代码到gitee上

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、创建gitee仓库1、进入gitee2、注册账号3、创建仓库 二、使用Linux创建1、打开Xshell创建一…

SpringBoot工程四种创建方式

创建SpringBoot项目时选择依赖&#xff1a;Spring Web、MySQL Driver、Thymeleaf、lombok 开发控制器类&#xff1a;标记了Controller的类就是一个handler类 handler类在SpringBoot中的作用就是&#xff1a;其内部的方法可以接受请求&#xff0c;处理请求&#xff0c;返回响应…

Intel oneAPI笔记(4)--jupyter官方文档(Unified Shared Memory)学习笔记

前言 本文是对jupyterlab中oneAPI_Essentials/03_Unified_Shared_Memory文档的学习记录&#xff0c;主要包含对统一共享内存的讲解 USM概述 USM (Unified Shared Memory)是SYCL中基于指针的内存管理。对于使用malloc或new来分配数据的C和C程序员来说应该很熟悉。当将现有的C…

如何定义类

类是将数据和方法封装在一起的一种数据结构&#xff0c;其中数据表示类的属性&#xff0c;方法表示类的行为&#xff0c;所以定义类实际上就是定义类的属性与方法。用户定义一个类实际上就是定义一个新的数据类型。在使用类之前&#xff0c;必须先定义它&#xff0c;然后才可利…

【JavaEE初阶】 UDP协议的详细解析

文章目录 &#x1f332;UDP协议概念&#x1f334;UDP协议端格式&#x1f384;UDP的特点&#x1f6a9;无连接&#x1f6a9;不可靠传输&#x1f6a9;面向数据报&#x1f6a9;缓冲区&#x1f6a9;全双工&#x1f6a9;大小受限 &#x1f340;基于UDP的应用层协议&#x1f38d;扩展问…