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 配置执行器 中…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
