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.rhkb.cn/news/237475.html

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

相关文章

YOLOv5改进 | 二次创新篇 | 结合iRMB和EMA形成全新的iEMA机制(全网独家创新)

一、本文介绍 本文给大家带来的改进机制是二次创新的机制,二次创新是我们发表论文中关键的一环,为什么这么说,从去年的三月份开始对于图像领域的论文发表其实是变难的了,在那之前大家可能搭搭积木的情况下就可以简单的发表一篇论文,但是从去年开始单纯的搭积木其实发表论…

C# 图解教程 第5版 —— 第24章 预处理指令

文章目录 24.1 什么是预处理指令24.2 基本规则24.3 符号指令&#xff08;#define、#undef &#xff09;24.4 条件编译&#xff08;#if、#else、#elif、#endif&#xff09;24.5 条件编译结构24.6 诊断指令&#xff08;#warning、#error&#xff09;24.7 行号指令&#xff08;#li…

【C++入门到精通】智能指针 [ C++入门 ]

阅读导航 引言一、什么是智能指针二、为什么需要智能指针三、内存泄漏1. 什么是内存泄漏&#xff0c;内存泄漏的危害2. 内存泄漏的示例&#xff0c;以及解决方法3. 内存泄漏分类&#xff08;1&#xff09;堆内存泄漏(Heap leak)&#xff08;2&#xff09;系统资源泄漏 4. 如何检…

Sqoop的增量数据加载策略与示例

当使用Apache Sqoop进行数据加载时&#xff0c;增量数据加载策略是一个关键的话题。增量加载可以仅导入发生变化的数据&#xff0c;而不必每次都导入整个数据集&#xff0c;这可以显著提高任务的效率。本文将深入探讨Sqoop的增量数据加载策略&#xff0c;提供详细的示例代码&am…

大语言模型面试问题

自己在看面经中遇到的一些面试题&#xff0c;结合自己和理解进行了一下整理。 transformer中求和与归一化中“求和”是什么意思&#xff1f; 求和的意思就是残差层求和&#xff0c;原本的等式为y H(x)转化为y x H(x)&#xff0c;这样做的目的是防止网络层数的加深而造成的梯…

管理软件供应链中网络安全工具蔓延的三种方法

软件开发组织不断发展&#xff0c;团队成长&#xff0c;项目数量增加。技术堆栈发生变化&#xff0c;技术和管理决策变得更加分散。 在这一演变过程中&#xff0c;该组织的 AppSec 工具组合也在不断增长。在动态组织中&#xff0c;这可能会导致“工具蔓延”。庞大的 AppSec 工…

Java--RSA非对称加密的实现(使用java.security.KeyPair)

文章目录 前言实现步骤测试结果 前言 非对称加密是指使用不同的两个密钥进行加密和解密的一种加密算法&#xff0c;调用方用使用服务方提供的公钥进行加密&#xff0c;服务方使用自己的私钥进行解密。RSA算法是目前使用最广泛的公钥密码算法。Java提供了KeyPairGenerator类要生…

2024年AMC8模拟考试实测流程、注意事项和常见问题

和往年的AMC8比赛一样&#xff0c;在正式比赛的前一周左右会开放两天的模拟考试时间&#xff0c;AMC8的主办方建议所有的参赛选手重视且参加模拟考试&#xff0c;以测试设备、熟悉流程&#xff0c;避免将来正式考试不小心违规&#xff0c;或者设备不给力。 2024年的AMC8模拟考…

OFBiz RCE漏洞复现(CVE-2023-51467)

漏洞名称 Apache OFBiz 鉴权绕过导致命令执行 漏洞描述 Apache OFBiz是一个非常著名的电子商务平台&#xff0c;是一个非常著名的开源项目&#xff0c;提供了创建基于最新J2EE/XML规范和技术标准&#xff0c;构建大中型企业级、跨平台、跨数据库、跨应用服务器的多层、分布式…

五、带登录窗体的demo

做了一个简单的带登录窗体的demo&#xff0c;有用户名和密码不能为空的验证&#xff0c;原理是在main.cpp的主函数入口处&#xff1a; 1、将默认的MainWindow主窗体注释。 2、新建一个formlogin登录窗体&#xff0c;在主函数中先运行登录窗体。 3、在登录窗体中引用MainWind…

Android json功能解析

1. 简介 JAVAScript Object Notation是一种轻量级的数据交换格式具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案&#xff08;有点类似于正则表达式 &#xff0c;获得了当今大部分语言的支持&#xff09;。  JSON采用兼容性很高的文本格式&#xf…

路由器02_静态路由DHCP

一、静态路由 &#xff11;、静态路由特点 由管理员手工配置&#xff0c;是单向的&#xff0c;缺乏灵活性 &#xff12;、默认路由 默认路由是一种比较特殊静态路由&#xff0c;一般用于末节&#xff08;末梢&#xff09;网络&#xff0c;直接指定目标为任何地方 二、静态…

nodejs+vue+ElementUi音乐分享社交网站77l8j

本文介绍的系统主要分为两个部分&#xff1a;一是前台界面&#xff1a;用户通过注册登录可以实现音乐播放、新闻浏览、留言评论等功能&#xff1b;另一个是后台界面&#xff1a;音乐网站管理员对用户信息进行管理&#xff0c;上传更新音乐资源&#xff0c;发布最新音乐资讯等功…

Casper Network (CSPR)2024 年愿景:通过投资促进增长

Casper Network (CSPR&#xff09;是行业领先的 Layer-1 区块链网络之一&#xff0c;通过推出了一系列值得关注的技术改进和倡议&#xff0c;已经为 2024 年做好了准备。 在过去的一年里&#xff0c;Casper Network (CSPR&#xff09;不断取得里程碑式的进展&#xff0c;例如推…

高性能RPC框架解密

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

linux安装系统遇到的问题

这两天打算攻克下来网络编程&#xff0c;发现这也确实是很重要的一个东西&#xff0c;但我就奇了怪了&#xff0c;老师就压根没提&#xff0c;反正留在我印象的就一个tcp/ip七层网络。也说正好&#xff0c;把linux命令也熟悉熟悉&#xff0c;拿着我大一课本快速过过 连接cento…

一个简易的PHP论坛系统

一个简易的PHP论坛系统 php课程设计&#xff0c;毕业设计 预览 技术 bootstrap 4.x jquery css php mysql 5.7 目录结构 登录 管理员 admin/123456 测试用户 user1/123456 更多文章和源码获取查看

【Java SE语法篇】8.面向对象三大特征——封装、继承和多态

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ 文章目录 1. 封装1.1 封装的概念1.2 为什么封装1.3 封装的实现…

PingCAP 受邀参加 FICC 2023,获 Open100 世纪全球开源贡献奖

2023 年 12 月&#xff0c;2023 国际测试委员会智能计算与芯片联邦大会&#xff08;FICC 2023&#xff09;在海南三亚举办&#xff0c;中外院士和数十位领域专家莅临出席。 大会现场 &#xff0c;开放源代码促进会创始人 Bruce Perens 颁发了 Open100 世纪全球开源贡献奖&…