Jetpack Compose -> 声明式UI Modifier

前言

本章主要介绍下 Compose 的声明式 UI 以及初级写法;

什么是声明式UI

传统UI

传统 UI 方式来声明UI

<androidx.appcompat.widget.LinearLayoutCompat    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <androidx.appcompat.widget.AppCompatImageView        android:layout_width="match_parent"        android:scaleType="centerCrop"        android:layout_height="300dp" />
</androidx.appcompat.widget.LinearLayoutCompat>

是通过 xml 来进行显示的,显示文字的方式是使用 TextView,它内部显示文字的方式有两种,一种是在 xml 中直接设置,通过下面这种方式设置

android:text="@string/app_name"

这种方式是通过初始值在 xml 中进行预设置的;

还有一种是在代码中直接调用 setText 进行设置

TextView textView = findViewById(R.id.text);
textView.setText("xxxx");

代码中是通过 setText 后续对值进行手动更新的;

这种需要手动对界面更新的方式 就不是声明式,属于传统式

声明式UI

指的是:在写界面的时候,只需要对界面进行一次性的声明,而不用在各种条件对界面元素进行跟条件有关的更新;

声明式 UI 自动更新界面,不需要手动更新,而传统式需要手动更新界面;

例如我们写一个 Compose 的 Text

Text(    text = "Hello $name!",    modifier = modifier
)

我们只需要把 name 作为参数传给这个 Text ,这个文字控件就会显示 name 的值;而声明式 UI 它不仅会用 name 来初始化显示 name 的值,当 name 的值发生变化的时候,Text 所显示的内容也会跟着发生改变,比如 name 的初始值是 Mars,界面第一时间显示的就是 Mars,过了一会这个 name 的值变成了 老A 了,那么界面就会跟着变成 老A,而不需要再次调用 setText 进行更新;这就是声明式 UI;并不是说界面是用声明式写出来的,而是说只需要声明就够了,不需要任何的手动更新;

传统式再次调用 setText 才能更新,这个调用不能省略,否则不会发生更新;

Compose 怎么写?

一种是直接创建一个 Compose 项目,一种是项目中增加配置,让它支持 Compose;

我的 Android Stuido 版本,可以直接创建 Compose 项目,并且这个版本的 AS 已经默认创建的就是 Compose 项目了;

点击 Next 创建完成;

完成之后,我们在 MainActivity 中可以看到一个大概的构建逻辑:

区别与传统的方式,我们通过一个 setContent 函数开启了我们的 Compose 之旅;

文字 Text 的声明

setContent {    Text("老A")
}

运行我们的程序,然后就可以在界面上看到 老A

Text 函数还有很多其他的用法,大家根据 API 直接去探索使用即可

图片 Image 的声明

传统 ImageView 显示图片的方式有两种,一种是位图,也就是 Bitmap;一种是矢量图,也就是 VectorDrawable;

但是在 Compose 中位图提供了一个新的 API,ImageBitmap;矢量图也提供了一个新的 API,ImageVector;

到这里的时候,可能很多人就会有疑问了,为什么要区别开来?

因为 Compose 的初衷是独立于 Android 平台,这个 [独立于平台] 说的就是不依赖于最新的 Android 系统才能使用;

例如我们熟悉的 RecyclerView,ViewPager2,AppCompat,协调者布局,ViewModel 等等一系列 Jetpack 组件,任何一个库出了新版本就能直接发布,开发者就可以直接使用,不需要等升级到最新的 Android 系统版本之后才能使用;

Compose 不仅独立与最新版本的 Android ,还独立于 Android 平台;Compose 提供的所有 API 全都不带有 Android 的痕迹;

虽然 Compose 不带有 Android 痕迹了,但是它底层还是使用的 Android 的 drawText()、drawTextRun()(可以看下这个链接,针对这两个 api 的介绍)、Canvas 来进行的绘制;

更精确的说:是上层暴露给开发者的 API 是独立于 Android 平台的;

声明一个图片 使用 Image,如果想使用 drawable 文件夹下的图片,使用 painterResource 来传递一个资源 id;

Image(painterResource(    id = R.drawable.ic_launcher_foreground),    contentDescription = resources.getString(R.string.app_name)
)

painterResource 这个函数内部会创建一个 ImageBitmap 或者 ImageVector 对象,取决于传递进去的时候位图还是矢量图,不过这就跟外部调用的没有关系了;painter 跟 Android 中的 drawable 比较类似,我们也可以不使用 painterResource 直接传递一个 ImageBitmap、ImageVector 也是可以的;

运行效果如上;

网络图片的加载,就需要依赖第三方库了,这里推荐一个图片库 Coil (Android 官方推荐 Coroutine Image Loader)这个库是面向 kotlin 和协程的,同时它还不是面向 View 系统的,

使用的话,直接引入依赖即可

implementation("io.coil-kt:coil-compose:2.5.0")

然后还是使用 Image 函数

Image(    rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),    contentDescription = resources.getString(R.string.app_name),    modifier = Modifier.size(150.dp)
)

只不过 painterResource 替换成了 rememberAsyncImagePainter,就可以传递一个网络图片url,并进行加载;

设置图片尺寸,使用 Modifier 关键字;

别忘了添加网络权限

<uses-permission android:name="android.permission.INTERNET"/>

运行效果如下:

早期的 Coil 并没有支持 Jetpack Compose,如果你使用的 Coil 版本比较低,可以使用另外一个库 Accompanist 

Image 的底层也是最终调用的 Android 的原生 API drawBitmap,如果是纯色,就是 Canvas 的 drawColor 等 API;

图标 Icon 的声明

在 Compose 中,图标的声明使用 Icon 函数;

Column {Icon(    imageVector = Icons.Filled.Favorite, contentDescription = "")
}

运行效果如下:

Icon 的用法基本上和 Image 上一样;

按钮 Button 的声明

在Compose当中,Button 和 Text 之间并没有什么关系。它们是两个独立的控件,并且通常它们还需要配合在一起使用才行;

先来看下不配合 Text 的效果

Button(onClick = {}) {    }

运行效果如下:

按钮是没有文案的,需要给 Button 设置一个文案

Button(onClick = {}) { Text(text = "我是老A")
}

运行效果如下:

点击事件的话,就直接在 onClick = { }  中处理即可

Button(onClick = {    Toast.makeText(LocalContext.current as Context, "", Toast.LENGTH_LONG).show()
}) {    Text(text = "我是老A")
}

输入框 TextFiled 的声明

在 Compose 中,使用 TextFiled 代替了 EditText

var text by remember { mutableStateOf("") }
TextField(value = text, onValueChange = {       text = it                                }, label = {Text(text = "用户名")})

运行效果如下:

可以看到,实现了 EditeText 的效果,还是 Material 风格的;

TextField 函数还有很多其他的用法,大家根据 API 直接去探索使用即可;

使用 Layout 的 Compose 平替

Android 中常用的布局有 FrameLayout、 LinearLayout、RelativeLayout、ConstraintLayout、ScrollView、RecyclerView、ViewPager 等等

Compose 中的平替控件

FrameLayout ->  Box()

Box {Text(text = "老A"color = Color(0XFF886600) )Image(rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.size(150.dp))
}

运行效果如下:

可以看到,和上面截图排列一样,也就是说,不使用布局控件,默认就是 FrameLayout排列;

LinearLayout -> Column()、Row() 表示纵向和横向;

Column {Text(text = "老A"color = Color(0XFF886600) )Image(rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.size(150.dp))
}

运行效果如下:

Row {Text(text = "老A"color = Color.Blue)Image(rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.size(150.dp))
}

运行效果如下:

RelativeLayout -> Box()

RelativeLayout 也是使用 Box 平替,但是通过 Modifier 来修改相对位置;

Box(modifier = Modifier.border(2.dp, Color.Black).size(200.dp)){    Text(        text= "老A",        color = Color.Blue    )    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.size(150.dp).align(Alignment.CenterEnd)    )
}

运行效果如下:

ConstraintLayout ->  ConstraintLayout for Compose

因为约束布局非常好用,所以官方为我们迁移到了 Compose 平台,我们可以直接使用这个控件;这里并不是套壳支持,而是把它们的逻辑移植到了 Compose 里面;

implementation("androidx.constraintlayout:constraintlayout-compose:+")
ConstraintLayout(modifier = Modifier    .border(2.dp, Color.Black)    .size(400.dp)    .padding(10.dp)) {    val (portraitImageRef, usernameTextRef, desTextRef) =  remember { createRefs() }    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = "",        modifier = Modifier.size(150.dp).constrainAs(portraitImageRef){            top.linkTo(parent.top)            bottom.linkTo(parent.bottom)            start.linkTo(parent.start)        })    Text(        text = "我是老A",        fontSize = 16.sp,        maxLines = 1,        textAlign = TextAlign.Left,        modifier = Modifier.constrainAs(usernameTextRef) {            top.linkTo(portraitImageRef.top)            start.linkTo(portraitImageRef.end, 10.dp)        })    Text(        text = "这是我的个人描述",        fontSize = 12.sp,        maxLines = 1,        color = Color.Red,        fontWeight = FontWeight.Light,        modifier = Modifier.constrainAs(desTextRef) {                top.linkTo(usernameTextRef.bottom, 5.dp)            start.linkTo(portraitImageRef.end, 10.dp)        })
}

运行效果如下:

使用 RecyclerView(竖向) 的 Compose 平替 -> LazyColumn

val names = listOf("Jordan", "Kobe", "James", "Wade", "Paul")
LazyColumn { // 没有 adapter,没有 ViewHolder    items(names.size) {        Text(text = names[it])    }
}

运行效果如下:

items 是遍历,item 是设置单个列表项,而且可以使用 item 来添加header、footer,也可以设置不同的 itemType,也就是不同的 ViewHolder;

val names = listOf("Jordan", "Kobe", "James", "Wade", "Paul")
LazyColumn {    item {        Text(text = "header")    }    items(names.size) {        Text(text = names[it])    }    item {        Text(text = "footer")  }
}

运行效果如下:

设置不同的 itemType,

val names = listOf("Jordan", "Kobe", "James", "Wade", "Paul", "Jordan", "Kobe", "James", "Wade", "Paul", "Jordan", "Kobe", "James", "Wade", "Paul")
LazyColumn {    item {        Text(text = "header")    }    items(names.size) {        Text(text = names[it])    }    item {        Image(            rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),            contentDescription = resources.getString(R.string.app_name),            modifier = Modifier.size(150.dp)        )    }    items(names.size) {        Text(text = names[it])    }    item {        Image(            rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),            contentDescription = resources.getString(R.string.app_name),            modifier = Modifier.size(150.dp)        )    }
}

运行效果如下:

可以看到,直接就可以滑动了,也就是实现了 Rechyclerview 的效果;

使用 RecyclerView(横向) 的 Compose 平替 -> LazyRow

val names = listOf("Jordan", "Kobe", "James", "Wade", "Paul", "Jordan", "Kobe", "James", "Wade", "Paul", "Jordan", "Kobe", "James", "Wade", "Paul")
LazyRow {    item {        Text(text = "header")    }    items(names.size) {        Text(text = names[it])    }    item {        Image(            rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),            contentDescription = resources.getString(R.string.app_name),            modifier = Modifier.size(150.dp)        )    }    items(names.size) {        Text(text = names[it])    }    item {        Image(            rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),            contentDescription = resources.getString(R.string.app_name),            modifier = Modifier.size(150.dp)        )    }
}

运行效果如下,横向滑动的列表:

使用 ScrollView(竖向) 的 Compose 平替 -> Modifier.verticalScroll

Android 中还有一个滑动组件:ScrollView,它的内部不是动态的,它内部的内容都是预先编排好的,而不是让它自己算的,它内部的布局也是在第一时间就加载好的,而不是滑动到什么地方才加载,如果 ScrollView 加载一个很长很长的布局,有可能会内存溢出的;

而在 Compose 中的平替是没有 ScrollView 的概念的,你只需要给你需要滑动的组件加上 Modifier.verticalScroll() 函数就可以实现竖向滑动;

Column(Modifier.requiredSize(200.dp)    .background(Color.Blue)    .verticalScroll(rememberScrollState())) {    repeat(20) {        Text(            text= "老A",            color = Color.Red        )    }
}

运行效果如下:

使用 ScrollView(横向) 的 Compose 平替 -> Modifier.horizontalScroll

Row(Modifier.requiredSize(200.dp)    .background(Color.Blue)    .horizontalScroll(rememberScrollState())) {            repeat(20) {        Text(            text= "老A",            color = Color.Red        )    }
}

运行效果如下:

使用 ViewPager(竖向) 的 Compose 平替 -> VerticalPager

VerticalPager(10) { page ->     Text(        text = "Page: $page",        modifier = Modifier.fillMaxWidth()    )
}

运行效果如下:

使用 ViewPager(横向) 的 Compose 平替 -> HorizontalPager

HorizontalPager(10) { page ->     Text(        text = "Page: $page",        modifier = Modifier.fillMaxWidth()    )
}

这里就不运行显示了,大家可以自己运行看下效果

Modifier

padding & margin

现在我们知道怎么去写布局了,其中线性布局是我们开法中用的最多的布局了,但是我们一般并不只是在那放一个线性布局就OK了,而是我们需要做很多很多的细节设置,比如我们需要去设置一些边距,margin、padding 等等,而在 Compose 中需要使用另外一种方式来设置边距,它就是 Modifier;

Modifier 是 Compose 中的一个很重要的角色,UI 的很多设置都是通过 Modifier 来完成的,通过官方文档,我们可以知道,Modifier 有如下四个作用:

借助修饰符(Modifier),您可以修饰或扩充可组合项。您可以使用修饰符(Modifier)来执行以下操作:

  • 更改可组合项的大小、布局、行为和外观;
  • 添加信息,如无障碍标签;
  • 处理用户输入;
  • 添加高级互动,如使元素可点击、可滚动、可拖动或可缩放;

比如,我们想给一个 Row 设置一个 10dp 的 padding,我们可以这么实现

Row(Modifier.padding(8.dp)) {}

但是 Compose 中的 Modifier 是没有 margin 的概念的,我们在 Andriod 中使用 padding 和 margin 来设置内边距和外边距,通常是因为背景色的原因,margin 是外边距,它是不包含在背景之内的,而 padding 它是内边距;

我们在 Compose 中设置背景色的时候,也是通过 Modifier 来执行的,Modifier.background(),但是 Modifier 对调用顺序是有先后要求的,我们可以看下下面的这个例子,以及我们如果和通过 padding 来实现 margin 的效果:

Row(    Modifier        .background(Color.Red)        .padding(10.dp)) {    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.size(150.dp)    )        Text(text = "我是老A", Modifier.background(Color.Green).padding(10.dp))    Text(text = "老A是我", Modifier.padding(10.dp).background(Color.Green))
}

运行效果:

可以看到 我是老A 是把背景色包含进来了,而 老A是我 是不包含的,也是 Modifier  的一个特点,设置有先后

这样,我们就只需要 padding 这一个函数就可以了;

background

我们看到,background 其实是有两个参数的,一个设置背景色,一个是 Shape,也就是形状,也就是说,我们可以给 background 设置一个形状,我们来看下效果

Row(    Modifier        .background(Color.Red, RoundedCornerShape(10.dp)) // 给背景设置一个圆角        .padding(10.dp)) {    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.size(150.dp)    )        Text(text = "我是老A", Modifier.background(Color.Green).padding(10.dp))    Text(text = "老A是我", Modifier.padding(10.dp).background(Color.Green))
}

运行效果如下:

我们的 CardView,可以通过 Modifier 来实现;

clip(shape: Shape)

Modifier 还提供了一个切割函数 clip,可以切各种东西,例如可以切图片,我们看下面这个例子

Row(    Modifier        .background(Color.Red, RoundedCornerShape(10.dp)) // 给背景设置一个圆角        .padding(10.dp)) {    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name), // 将图片切成圆的       modifier = Modifier.clip(CircleShape).size(150.dp)    )        Text(text = "我是老A", Modifier.background(Color.Green).padding(10.dp))    Text(text = "老A是我", Modifier.padding(10.dp).background(Color.Green))
}

运行效果如下:

因为图片的左右是透明的,所以看不出来效果;已经把图片切割成圆的了;

layout_width & layout_height 的 Compose 平替 size

layout_width 可以用 width 来替换,layout_height 可以用 height 来替换,如果宽高一样的话,可以直接使用 size

Row(    Modifier        .background(Color.Red, RoundedCornerShape(10.dp))        .width(400.dp) // 设置宽度       .height(800.dp) // 设置高度       .padding(10.dp)) {    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.clip(CircleShape).size(150.dp)    )        Text(text = "我是老A", Modifier.background(Color.Green).padding(10.dp))    Text(text = "老A是我", Modifier.padding(10.dp).background(Color.Green))
}

在 Android 中,我们需要强制给每个控件声明 width 和 height,但是在 Compose 中,这个并不是强制的了,因为每个组件都有一个默认规则,默认规则就是,如果你不写,那么它就相当于是传统 View 的 wrap_content 的了;

如果想使用类似 match_parent 的,则使用 Modifier.fillMaxWidth/ fillMaxHeight/ fillMaxSize 函数;

给 Text 设置颜色和大小

如果我们想给 Text 设置颜色和大小,可能第一时间我们就想到了使用 Modifier,但是,可能没有你想的那么简单,Modifier 并没有提供相关API,我们可以往上看 Text 的基础使用,发现其实 Text 的 API 提供了相关设置;

我们在声明 Text 的时候,直接使用 color 和 fontSize 来设置颜色和大小,而不用通过 Modifier,到这里的时候,可能大家就比较迷糊了,为啥还不统一呢,是不是 Jetpack Compose 的作者脑子没想好呢?然而并不是,因为 Compose 对于 Android 团队来说,是一次重要的革命,Android 团队其实在 Compose 上下了很大的功夫的;判断是 Modifier 还是函数参数,其实很简单:

通用的设置方式,使用 Modifier;专项的设置,使用函数参数

给控件设置点击监听

Android 中其实任何控件都可以设置监听的,这其实可以看做通用的设置方式,也就是说可以通过 Modifier 来给任意的控件设置一个监听

Row(    Modifier        .background(Color.Red, RoundedCornerShape(10.dp))        .width(400.dp)        .height(800.dp)        .padding(10.dp) // 给 Row 设置一个监听       .clickable {            Toast.makeText(context, "我是 Row,我被点击了", Toast.LENGTH_LONG).show()        }) {    Image(        rememberAsyncImagePainter("https://img2.baidu.com/it/u=1866466367,1635278877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=734"),        contentDescription = resources.getString(R.string.app_name),        modifier = Modifier.clip(CircleShape).size(150.dp)    )        Text(text = "我是老A", Modifier.background(Color.Green).padding(10.dp))    Text(text = "老A是我", Modifier.padding(10.dp).background(Color.Green))
}

运行效果如下:

可以看到,点击事件被执行了;

好了,Compose 的初探,就先到这里吧~~

下一节预告

状态订阅和自动更新;

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

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

相关文章

test Property-based Testing-04-junit-quickcheck

拓展阅读 开源 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息) 开源 Junit performance rely on junit5 and jdk8.(java 性能测试框架。性能测试。压测。测试报告生成。) junit-quickcheck&#xff1a;基于 JUnit 风格的属性驱动测试库 junit-qu…

送水小程序开发:如何选择最适合的技术平台

选择最适合的技术平台对于开发送水小程序至关重要。在这篇文章中&#xff0c;我将为您介绍如何选择最适合的技术平台来开发送水小程序&#xff0c;以及各种技术平台的优缺点。无论您是刚刚起步的创业公司还是成熟的送水服务提供商&#xff0c;本文都将为您提供有关送水小程序开…

深信服超融合HCI版本升级,6.0.0R5升级至6.8.0R2

超融合升级&#xff0c;需要满足以下条件及前期准备&#xff1a; 确认HCI的升级序列号有效升级时长大概在一个半小时&#xff0c;安全起见&#xff0c;需预留至少三至四小时窗口期升级前&#xff0c;需要将所有虚拟机关机&#xff0c;涉及到业务无法访问&#xff0c;需提前通知…

网络-DHCP中继(思科)

思科 前提&#xff1a;将R1 R3配置16板卡 将R1更改标识符为三层交换机 将R3更改标识符为交换机 拓扑图&#xff1a; R2进行配置 配置IP地址 为12.0.0.2 配置默认路由到R1的f1/4接口 配置dhcp地址池 配置vlan10的地址池 配置vlan20的地址池 三层交换机R1进行配置 将f1/4接口…

高光谱分类论文解读分享之基于形态卷积神经网络的高光谱影像分类

IEEE TGRS 2021&#xff1a;基于形态卷积神经网络的高光谱影像分类 题目 Morphological Convolutional Neural Networks for Hyperspectral Image Classification 作者 Swalpa Kumar Roy; Ranjan Mondal; Mercedes E. Paoletti; Juan M. Haut; Antonio Plaza 关键词 Clas…

关于git与git-lfs对文件压缩存储方面的研究

先说结论&#xff0c;git使用了Delta增量压缩算法&#xff0c;git-lfs实测没有进行任何压缩&#xff0c;这个结论让我很震惊。 测试过程如下&#xff1a; 测试git仓库自身的压缩 准备一个包含许多杂项文件的文件夹&#xff0c;大概几百M&#xff0c;要保证有一个txt文本文件…

Qt框架学习 --- CTK编译(Qt5.15.2+vs2019+cmake)

系列文章目录 第二章 CTK的测试demo https://blog.csdn.net/yonug1107716573/article/details/135527289 文章目录 系列文章目录前言一、准备工作二、编译步骤1.修改文件2.编译CTK2.1 准备2.2 cmake界面配置2.3 配置编译器2.4 编译的配置设置2.5 选择需要编译的模块2.6 生成2.…

Uibot (RPA设计软件)股票板块行情抓取————课前材料四

微信群发助手机器人的小项目友友们可以参考小北的课前材料二博客~ (本博客中会有部分课程ppt截屏,如有侵权请及请及时与小北我取得联系~&#xff09; 紧接着小北的前两篇博客&#xff0c;友友们我们即将开展新课的学习~RPA 培训前期准备指南——安装Uibot(RPA设计软件&#x…

Java 并发性和多线程3

七、线程安全及不可变性 当多个线程同时访问同一个资源&#xff0c;并且其中的一个或者多个线程对这个资源进行了写操作&#xff0c;才会产生竞态条件。多个线程同时读同一个资源不会产生竞态条件。 我们可以通过创建不可变的共享对象来保证对象在线程间共享时不会被修改&…

AI绘画风格化实战

在社交软件和短视频平台上&#xff0c;我们时常能看到各种特色鲜明的视觉效果&#xff0c;比如卡通化的图片和中国风的视频剪辑。这些有趣的风格化效果其实都是图像风格化技术的应用成果。 风格化效果举例 MidLibrary 这个网站提供了不同的图像风格&#xff0c;每一种都带有鲜…

Neo4j知识图谱(2)创建与删除

Neo4j - CQL简介_w3cschoolhttps://www.w3cschool.cn/neo4j/neo4j_cql_introduction.html一、创建节点 create(n:Person{name:何仙鸟,age:21}) create就是创建&#xff0c;无论是点还是边都是用create来创建 n相当于一个别名&#xff0c;比如创建一个Person&#xff0c;而Pe…

嵌入式软件面试之程序在存储器中的分布

Hi, 大家好&#xff0c;今天阿目分享的是一个嵌入式软件面试的常见问题&#xff0c;内存分布或者说程序在内存中的布局&#xff0c;我们写的程序是按照怎么的准则放在内存中的&#xff1f; 一般有操作系统的嵌入式设备&#xff0c;都会有一个Bootloader, 它负责在上电后初始化…

漏洞修复整理

一、Geoserver Apache HTTP/2拒绝服务漏洞&#xff08;CVE-2023-44487&#xff09;、Eclipse Jetty 资源管理错误漏洞(CVE-2023-26048)、Eclipse Jetty 信息泄露漏洞(CVE-2023-26049) 受影响版本&#xff1a;9.4.53以下版本 处理方式&#xff1a;原地升级 &#xff08; jdk版本…

学习redis有效期和数据类型

1、安装redis和连接redis 参考&#xff1a;ubuntu安装单个redis服务_ubuntu redis单机版安装-CSDN博客 连接redis&#xff1a;redis-cli.exe -h localhost -p 6379 -a 123456 2、Redis数据类型 以下操作我们在图形化界面演示。 2.1、五种常用数据类型介绍 Redis存储的是key…

jenkins-cl参数化构建

pipeline片段&#xff08;对应jenkins-cli -p参数的BRANCHdevelop&#xff09; parameters {string(name: BRANCH, defaultValue: master, description: Enter the branch name)}stages {stage(Get Code) {steps {script {def branch params.BRANCHcheckout scmGit(branches: …

算法通关村第十五关—继续研究超大规模数据场景的问题(黄金)

继续研究超大规模数据场景的问题 一、对20GB文件进行排序 题目要求&#xff1a;假设你有一个20GB的文件&#xff0c;每行一个字符串&#xff0c;请说明如何对这个文件进行排序&#xff1f;  分析&#xff1a;这里给出大小是20GB,其实面试官就在暗示你不要将所有的文件都装入到…

世邦通信 SPON IP网络对讲广播系统addscenedata.php任意文件上传漏洞

产品介绍 世邦通信SPON IP网络对讲广播系统采用领先的IPAudio™技术,将音频信号以数据包形式在局域网和广域网上进行传送,是一套纯数字传输系统。 漏洞描述 spon IP网络对讲广播系统存在任意文件上传漏洞&#xff0c;攻击者可以通过构造特殊请求包上传恶意后门文件&#xff…

Sentinel微服务保护

文章目录 Sentinel微服务保护1.初识Sentinel1.1.雪崩问题及解决方案1.1.1.雪崩问题1.1.2.解决方案1.1.3.总结 1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel 1.4.微服务整合Sentinel 2.流量控制2.1.簇点链路2.1.快速入门2.2.流控模式2.2.1.…

Zung氏焦虑症测试SAS

SAS被称为焦虑自评量表&#xff0c;是一种用来测量焦虑症状程度以及观察治疗过程中变化情况的心理量表。主要用于评估心理状态&#xff0c;辅助参考数据&#xff0c;也是焦虑评定的标准。焦虑自评量表系是由William W.K. Zung编制的&#xff0c;该量表已成为心理咨询师、心理医…

【GitHub项目推荐--克隆你的声音】【转载】

今天推荐一个黑科技开源项目&#xff0c;只需要你 5 秒钟的声音对话&#xff0c;就能克隆出你的声音&#xff0c;而且能够实时的生成你任意语音。 是不是很顶&#xff1f; 我举个例子&#xff0c;如果我这里有 300 条你说话的语音&#xff0c;我把你的语音数据用这个开源项目…