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 的初探,就先到这里吧~~
下一节预告
状态订阅和自动更新;
相关文章:
Jetpack Compose -> 声明式UI Modifier
前言 本章主要介绍下 Compose 的声明式 UI 以及初级写法; 什么是声明式UI 传统UI 传统 UI 方式来声明UI <androidx.appcompat.widget.LinearLayoutCompat android:layout_width"match_parent" android:layout_height"match_parent&quo…...
windows10 装docker和docker compose
一.windows环境准备 开启过程中的问题,进入bios修复 二.docker下载安装 1.下载 Docker Desktop: The #1 Containerization Tool for Developers | Docker 下载最新版有问题,下载老版本试试 Docker Desktop release notes | Docker Docs 2.安装 三.do…...
第二次面试总结 - 宏汉科技 - Java后端开发
🧸欢迎来到dream_ready的博客,📜相信您对专栏 “本人真实面经” 很感兴趣o (ˉ▽ˉ;) 专栏 —— 本人真实面经,更多真实面试经验,中大厂面试总结等您挖掘 目录 总结 (非详细) 面试内容(提问内容) - 带答案…...
GPT-4:人工智能的新纪元与未来的无限可能
在人工智能的发展史上,GPT-4的问世标志着一个新的里程碑。作为最新一代的自然语言处理模型,GPT-4不仅在技术上取得了突破,更在应用层面展现了前所未有的潜力。本文将探讨GPT-4的核心技术、应用场景以及它对未来社会的潜在影响。 GPT-4的技术…...
2.右值引用和移动语义
文章目录 右值引用和移动语义&&的特性右值引用优化性能,避免深拷贝移动(move )语义forward 完美转发emplace_back 减少内存拷贝和移动unordered container 无序容器map和unordered_map的差别内部实现机理不同优缺点以及适用处 小结优缺点以及适用处 小结 代…...
深入浅出线程原理
Linux 中的线程本质 线程接口由 Native POSIX Thread Library 提供,即:NPTL 库函数 线程被称为轻量级进程 (Light Weight Process) 每一个线程在内核中都对应一个调度实体,拥有独立的结构体 (task_struct) 内核设计:一个进程对…...
openssl3.2 - 官方demo学习 - saccept.c
文章目录 openssl3.2 - 官方demo学习 - saccept.cEND openssl3.2 - 官方demo学习 - saccept.c 建立TLSServer(使用了证书, 和证书中的私钥), 接收客户端的连接, 并将客户端发来的信息打印到屏幕 笔记 /*! \file saccept.c */ /*! \brief 建立TLSServer(使用了证书, 和证书中…...
JavaScript基础(26)_dom增删改练习
<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>DOM增删改练习</title><link rel"stylesheet" href"../browser_default_style/reset.css"><style>table {borde…...
mac上部署单体hbase
1. 简介 HBase 是一个开源的、分布式的、版本化的典型非关系型数据库。它是 Google BigTable 的开源实现,并且是 Apache 基金会的 Hadoop 项目的一部分1。HBase 在 Hadoop Distributed File System (HDFS) 上运行,作为一个列式存储非关系数据库管理系统…...
【RV1126 学习】SDK/ U-Boot/kernel/rootfs 编译学习
文章目录 RV1126芯片介绍rv1126 模块代码目录相关说明 SDK 包下的脚本使用build.sh 脚本使用envsetup.sh 脚本使用mkfirmware.sh 脚本使用rkflash.sh 脚本使用 U-Boot 编译和配置uboot 的配置修改编译操作 kernel 的修改编译rootfs 编译和配置buildroot 配置busybox 配置 RV112…...
Golang 使用 AST 获取方法和参数名以及应用举例
背景 在做一些自动生成的代码工作时,有时需要知道方法以及对应的参数名 如果仅是方法,利用反射机制就可以解决 而参数名,程序编译后,已经丢失 可以通过 AST 事先获取方法的参数名 有了方法、参数名,加上反射&…...
DC-DC变换集成电路芯片B34063——工作电压范围宽,静态电流小
B34063为一单片DC-DC变换集成电路,内含温度补偿的参考电压源(1.25V)、比较器、能有效限制电流及控制工作周期的振荡器,驱动器及大电流输出开关管等,外配少量元件,就能组成升压、降压及电压反转型DC-DC变换器。 主要特点: ● 工作…...
强力推荐:本地文件加密软件—超详细加密步骤来了!
在数字化时代,数据安全问题日益受到人们的关注。 为了保护个人和企业的重要信息不被泄露,越来越多的人开始使用文件加密软件。 尤其是常常会有数据泄露风险的企业更是需要一款非常给力的加密工具来保护企业数据安全。 一、选择合适的加密软件 在选择加…...
在qml中,ListModel可以与WorkerScript一起使用,从多个线程访问列表模型
在QML中,您可以使用ListModel和WorkerScript一起实现多线程访问列表模型。以下是一个简单的例子,演示了如何在QML中使用这两个元素: import QtQuick 2.15 import QtQuick.Controls 2.15ApplicationWindow {visible: truewidth: 400height: 3…...
rocketmq实现延迟消息
SpringBoot整合RocketMQ发送延时消息 springboot rocketmq 延迟消息 Windows下RocketMQ安装及可视化界面搭建 Java 客户端 RocketMQ延迟消息 项目背景 项目中有延时消息的需求,综合考量RocketMQ比较适合。 RocketMQ支持多维度的延迟级别 支持多种消息类型 基…...
vue倒计时60秒改变按钮状态效果demo(整理)
你可以使用Vue的计时器和绑定状态的方法来实现这个功能。 首先,在data中添加一个计时器countdown,初始值为0。 data() {return {countdown: 0} }<template><div><button click"startCountdown" :disabled"countdown > …...
多区域isis配置实验
一、预习: IS-IS:Intermediate System to Intermediate System,中间系统到中间系统,是ISO为它的CLNP(ConnectionLess Network Protocol)设计的一种动态路由协议,后来为了提供对IP路由的支持&…...
Ubuntu 22.04.3 LTS arm64 aarch64 ISO jammy-desktop-arm64.iso 下载
Ubuntu 22.04.3 LTS (Jammy Jellyfish) Daily Build 参考 Are there official Ubuntu ARM / aarch64 desktop images? - Ask Ubuntu...
软件测试面试必备知识
随着软件测试领域对于技术要求越来越清晰,到现在测试人员在市场上的岗位需求也变得越来越复杂。极大部分的企业都开始对自动化测试岗位有了更多的需要。自然而然,面试就相对于非常重要了。 网络上有着各式各样的测试框架的存在,我也不知道我…...
4.4 媒资管理模块 - 分布式任务处理介绍、视频处理技术方案
媒资管理模块 - 视频处理 文章目录 媒资管理模块 - 视频处理一、视频转码1.1 视频转码介绍1.2 FFmpeg 基本使用1.2.1 下载安装配置1.2.2 转码测试 1.3 工具类1.3.1 VideoUtil1.3.2 Mp4VideoUtil1.3.3 测试工具类 二、分布式任务处理2.1 分布式任务调度2.2 XXL-JOB 配置执行器 中…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
