安卓学习笔记-声明式UI
声明式UI
Jetpack Compose 是 Google 推出的用于构建 Android UI 的现代化工具包。它采用 声明式编程模型(Declarative UI),用 Kotlin 编写,用于替代传统的 XML + View 的方式。一句话概括:Jetpack Compose = 用 Kotlin 代码写 UI,简洁、响应式、可组合
一个小例子对比
传统 XML + View 写法:
// MainActivity.kt
val textView = findViewById<TextView>(R.id.helloText)
textView.text = "Hello Compose"
Jetpack Compose 写法:
@Composable
fun Greeting() {Text("Hello Compose")
}
实现一个页面
ComponentActivity
是 Android Jetpack 架构组件中的一个核心类,它是 Activity
的子类,为现代 Android 应用程序开发提供了额外的功能。
下面是一个 简洁清晰的 Jetpack Compose 风格 Activity
开发流程说明,帮助你快速掌握使用 Jetpack Compose 编写页面的基本步骤。
使用 Jetpack Compose 编写一个 Activity
的标准流程
第 1 步:创建一个继承 ComponentActivity
的类
class MainActivity : ComponentActivity() {...
}
ComponentActivity
是 Jetpack Compose 所需的基础 Activity 类型。比通常用于 XML + View 系统的传统 UI 开发的AppCompatActivity
更轻量,适合纯 Compose 页面。
第 2 步:重写 onCreate()
方法并调用 setContent {}
override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {// 调用你的 Compose UI 入口函数MyApp()}
}
setContent {}
是 Compose 的入口,用于设置 UI。
括号内是一个或多个 @Composable
函数。
第 3 步:编写你的 @Composable
函数,组织 UI
@Composable
fun MyApp() {Text("Hello Jetpack Compose")
}
使用 @Composable
注解定义可组合的 UI 函数。
可组合函数可嵌套、组合、响应状态。
最简版 Activity 示例:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.*
import androidx.compose.runtime.Composableclass MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyApp()}}
}@Composable
fun MyApp() {Text("你好,Compose!")
}
总结一句话流程:
创建 ComponentActivity
→ 重写 onCreate()
→ 调用 setContent {}
→ 写一个或多个 @Composable
函数来构建 UI。
编写一个预览函数(Preview)
当然,Jetpack Compose 提供了非常方便的 UI 预览功能,你可以不用运行 App,就能在 Android Studio 中看到某个 @Composable
函数的界面效果。
你只需要:
- 给你的
@Composable
函数加一个单独的预览函数 - 用
@Preview
注解标记它 - 这个函数内部调用你想预览的 UI
示例:为 MyApp()
编写一个预览函数
import androidx.compose.ui.tooling.preview.Preview@Preview(showBackground = true, name = "默认预览")
@Composable
fun MyAppPreview() {MyApp()
}
注解参数说明
showBackground = true
显示一个浅色背景,便于在 IDE 中观察 UI 布局name = "..."
给这个预览命名,方便识别多个预览函widthDp
/heightDp
可选,用于指定预览尺uiMode
可指定暗色模式、横屏模式等(如Configuration.UI_MODE_NIGHT_YES
)
如何看预览的UI
- 写好带
@Preview
的函数 - 在 Android Studio 中打开这个文件
- 点击 “Split” / “Design” 预览按钮即可查看 UI 渲染效果
完整代码
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Previewclass DemoActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyApp()}}
}@Composable
fun MyApp() {Text("你好,Compose!")
}@Preview(showBackground = true, name = "默认预览")
@Composable
fun MyAppPreview() {MyApp()
}
Jetpack Compose 的响应式
Jetpack Compose 最大的特点之一,就是它采用了 响应式编程模型(reactive programming model) 来构建 UI。
什么是“响应式”的方式构建 UI?
简单来说:界面不再是你手动更新的,而是“自动响应数据变化”的。
对比传统 View 编程(命令式):
// 传统 View 写法(命令式)
textView.text = "你好," + name // 你得手动更新 UI
你修改了 name
,你得自己调用 textView.setText(...)
去更新。
Jetpack Compose 的响应式写法:
@Composable
fun Greeting(name: String) {Text("你好,$name!") // name 变了,Text 自动刷新
}
你只要修改 name
的状态,UI 会自动重新组合并刷新显示,你不再需要手动更新 UI 控件。
关键机制:remember
和 mutableStateOf
这是 Compose 中响应式状态管理的核心工具:
var name by remember { mutableStateOf("Compose") }
这行代码的作用是:
创建一个名为 name 的状态变量,这个变量的初始值是字符串 “Compose”
当你在 UI 中使用这个变量时,只要它发生变化,相关 UI 会自动刷新(响应式)
mutableStateOf
mutableStateOf
创建一个可以被 Compose 跟踪的可变状态值.一旦值改变,使用它的 Compose 组件会自动 recompose(重新绘制).
mutableStateOf 是一个泛型函数,可以接受任何类型 T. 例如:基础类型、自定义数据类 (Data classes)、集合类型(List, Set, Map)、任何其他 Kotlin 类实例.
示例
- 集合
val itemList = mutableStateOf(listOf("Apple", "Banana"))
val userMap = mutableStateOf(mapOf("id1" to "Alice", "id2" to "Bob"))
注意: 对于集合类型(List, Set, Map),mutableStateOf 包装的是集合本身。这意味着如果你只是修改集合的内容(例如 itemList.value.add(“Orange”)),Compose 不会自动检测到这种内部变化并触发重组。要触发重组,你需要赋一个新的集合实例:
- 类实例
class MyCustomObject(var data: String)
val myObject = mutableStateOf(MyCustomObject("Initial Data"))
remember
remember
在重组(Recomposition)时记住状态值,避免它被重置
如果你写:
val counter = mutableStateOf(0)
这个状态会在每次重组时被重新创建为 0,导致 UI 刷新后又变回初始值。所以你要:
val counter = remember { mutableStateOf(0) }
这样 Compose 就会“记住”这个状态,不在每次重组时重新创建它。
Compose 自动响应过程示意图
状态 (mutableStateOf) --> 改变 → Compose 监听到 → 重组(重绘 UI)
示例:响应式 UI 的体现
package com.wy.diary.activityimport android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dpclass DemoActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyApp()}}
}@Composable
fun MyApp() {var name by remember { mutableStateOf("Compose") }Column(modifier = Modifier.padding(16.dp)) {TextField(value = name,onValueChange = { name = it }, // 用户输入改变 namelabel = { Text("输入名字") })Spacer(modifier = Modifier.height(16.dp))Text("你好,$name!") // name 变了,这里自动更新}
}@Preview(showBackground = true, name = "默认预览")
@Composable
fun MyAppPreview() {MyApp()
}
代码分层(UI、ViewModel)
在 Compose 中,虽然你可以用 remember { mutableStateOf(...) }
存储 UI 状态,但这种状态只在 Composable 内部存在,页面重建(如屏幕旋转)就会丢失。
为什么会丢失状态?
这里的“页面重建”如果指的是配置变更(Configuration Changes),例如屏幕旋转,那么状态会丢失的原因在于:
当 Android 设备发生配置变更时(如屏幕从竖屏旋转到横屏):
- Activity/Fragment 会被销毁并重建。 这是 Android 系统为了适应新的配置(例如,加载新的布局资源、调整屏幕尺寸等)的默认行为。
- Activity 的生命周期方法会被重新调用。 包括
onCreate()
、onStart()
、onResume()
等。 - 您的
@Composable
函数所在的整个 Composable 树也会被销毁并从头开始构建。 - 当 Composable 重新执行时,
remember { ... }
块内的代码会重新执行。由于它是一个新的执行上下文,mutableStateOf(0)
会重新创建一个新的MutableState
实例,并用其初始值0
覆盖之前的值。
简而言之,remember
只在当前 Composable 实例的生命周期**内保持状态。一旦 Composable 实例因为其宿主(如 Activity)被销毁而重建,remember
的作用范围就结束了,所有用 remember
记住的状态都会被重置为它们的初始值。
示例:
如果你的 counter
设为 remember { mutableStateOf(0) }
,用户点击了几次按钮,counter
变成了 5。然后用户旋转屏幕,Activity 重建,MyApp
Composable 重新执行,remember { mutableStateOf(0) }
再次被调用,counter
会被重新初始化为 0,之前的 5 就丢失了。
哪些“重建”会丢失状态?
会丢失使用 remember { mutableStateOf(...) }
存储状态的常见的配置变更包括:
- 屏幕旋转(横竖屏切换)
- 键盘可用性变化(软键盘弹出/隐藏)
- 语言区域设置变化
- 显示大小、字体大小变化
- 主题切换
此外,还有一些非配置变更但也会导致 Activity 销毁重建的情况: - 系统回收进程: 当系统内存不足时,可能会回收后台 Activity 所在的进程。
- 用户从最近任务列表中滑动关闭应用
- Activity 被显式地
finish()
如何在配置变更时保留 UI 状态?
为了在配置变更时保留 UI 状态,可以使用ViewModel。
ViewModel
是一个专门用来存储和管理界面相关数据的类,它与界面组件(如 Activity 或 Composable)生命周期隔离,因此可以安全地保存状态,即使界面配置发生变化(如屏幕旋转)。
Activity 创建
└── ViewModel 创建(只会创建一次)Activity 销毁重建(如横竖屏切换)
└── ViewModel 保持不变,状态仍在Activity 被真正销毁(退出)
└── ViewModel 被清理
接下来我会将前面的例子改造成,使用 ViewModel
+ StateFlow
+ Compose UI 的最佳实践写法。
改造思路
我们需要:
- 创建一个 ViewModel 管理状态
- 用
StateFlow
来代替remember
管理的状态 - 在 Composable 中使用
viewModel()
获取 ViewModel,并通过collectAsState()
观察状态
第一步:创建 ViewModel
创建一个类 DemoViewModel.kt
:
package com.wy.diary.viewmodelimport androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlowclass DemoViewModel : ViewModel() {private val _name = MutableStateFlow("Compose") // 私有可变状态val name: StateFlow<String> = _name // 对外只读fun updateName(newName: String) {_name.value = newName}
}
第二步:修改 Composable 来使用 ViewModel
修改 MyApp()
方法:
@Composable
fun MyApp(viewModel: DemoViewModel = viewModel()) {val name by viewModel.name.collectAsState()Column(modifier = Modifier.padding(16.dp)) {TextField(value = name,onValueChange = { viewModel.updateName(it) }, // 通知 ViewModel 更新状态label = { Text("输入名字") })Spacer(modifier = Modifier.height(16.dp))Text("你好,$name!") // 自动根据 StateFlow 更新}
}
这里有两个地方需要说明一下
- viewModel
Compose 提供的 viewModel() 函数 来获取具体的ViewModel类,需要引入依赖,下面是Kotlin DSL写法
dependencies {// ....省略其他依赖implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
}
- collectAdState
这里collectAsState()
把 StateFlow(响应式流) 转换成 Compose 可观察的 State 对象,这样 UI 就能根据变化自动重组。
第三步:预览函数改动
因为引入了 viewModel()
,它只能在运行时由 Composition Local 提供的 ViewModelStoreOwner
支持,而 Preview 模式下并不具备这个环境,所以我们不能在 @Preview
中直接使用包含 viewModel()
的 Composable 函数。
解决思路是:将 UI 部分抽离出来,变成一个纯函数式的 Composable,接收状态和回调作为参数,这样就可以在预览时传入假的数据。
代码再次调整我们将 MyApp()
拆成两层
展示 UI 的纯 Composable
@Composable
fun MyAppContent(name: String, onNameChange: (String) -> Unit) {Column(modifier = Modifier.padding(16.dp)) {TextField(value = name,onValueChange = onNameChange,label = { Text("输入名字") })Spacer(modifier = Modifier.height(16.dp))Text("你好,$name!")}
}
包装 ViewModel 的 MyApp()
@Composable
fun MyApp(viewModel: DemoViewModel = viewModel()) {val name by viewModel.name.collectAsState()MyAppContent(name = name, onNameChange = { viewModel.updateName(it) })
}
添加 Preview
现在我们可以优雅地添加预览函数:
@Preview(showBackground = true)
@Composable
fun MyAppPreview() {MyAppContent(name = "预览模式", onNameChange = {})
}
通过这种拆分方式,我们既保留了使用 ViewModel
的响应式结构,也让 UI 层具备了良好的可测试性和可预览性。
完整代码
这是完整的改造后代码,包括 ViewModel
、Composable
UI、Preview
和依赖说明
添加依赖(Kotlin DSL)
在 build.gradle.kts
中加入:
dependencies {implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
}
DemoViewModel.kt
package com.wy.diary.viewmodelimport androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlowclass DemoViewModel : ViewModel() {private val _name = MutableStateFlow("Compose")val name: StateFlow<String> = _namefun updateName(newName: String) {_name.value = newName}
}
DemoActivity.kt
package com.wy.diaryimport android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import com.wy.diary.ui.MyApp
import com.wy.diary.viewmodel.DemoViewModelclass DemoActivity : ComponentActivity() {private val viewModel: DemoViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MaterialTheme {Surface {MyApp(viewModel)}}}}
}
MyApp.kt
(UI 和 Preview)
package com.wy.diary.uiimport androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.wy.diary.viewmodel.DemoViewModel@Composable
fun MyApp(viewModel: DemoViewModel = viewModel()) {val name by viewModel.name.collectAsState()MyAppContent(name = name, onNameChange = { viewModel.updateName(it) })
}@Composable
fun MyAppContent(name: String, onNameChange: (String) -> Unit) {Column(modifier = Modifier.padding(16.dp)) {TextField(value = name,onValueChange = onNameChange,label = { Text("输入名字") })Spacer(modifier = Modifier.height(16.dp))Text("你好,$name!")}
}@Preview(showBackground = true)
@Composable
fun MyAppPreview() {MyAppContent(name = "预览模式", onNameChange = {})
}
最终文件结构
ui.theme是创建empty项目时候就有了的,不必理会
相关文章:

安卓学习笔记-声明式UI
声明式UI Jetpack Compose 是 Google 推出的用于构建 Android UI 的现代化工具包。它采用 声明式编程模型(Declarative UI),用 Kotlin 编写,用于替代传统的 XML View 的方式。一句话概括:Jetpack Compose 用 Kotlin…...

AI天气预报进入“大模型时代“:如何用Transformer重构地球大气模拟?
引言:从数值预报到AI大模型的范式变革 传统的天气预报依赖于数值天气预报(NWP, Numerical Weather Prediction),通过求解大气动力学方程(如Navier-Stokes方程)进行物理模拟。然而,NWP计算成本极高,依赖超级计算机,且难以处理小尺度天气现象(如短时强降水)。 近年来…...
本地项目如何设置https(2)——2025-05-19
在配置本地HTTPS时,安装mkcert工具本身是全局操作(安装在系统环境,与项目无关),但生成证书时需要进入项目目录操作。以下是具体说明: 安装 mkcert(全局操作) 安装位置:无…...

数据结构第3章 线性表 (竟成)
目录 第 3 章 线性表 3.1 线性表的基本概念 3.1.1 线性表的定义 3.1.2 线性表的基本操作 3.1.3 线性表的分类 3.1.4 习题精编 3.2 线性表的顺序存储 3.2.1 顺序表的定义 3.2.2 顺序表基本操作的实现 1.顺序表初始化 2.顺序表求表长 3.顺序表按位查找 4.顺序表按值查找 5.顺序表…...

JAVA面试复习知识点
面试中遇到的题目,记录复习(持续更新) Java基础 1.String的最大长度 https://www.cnblogs.com/wupeixuan/p/12187756.html 2.集合 Collection接口的实现: List接口:ArraryList、LinkedList、Vector Set接口:…...

项目中的流程管理之Power相关流程管理
一、低功耗设计架构规划(Power Plan) 低功耗设计的起点是架构级的电源策略规划,主要包括: 电源域划分 基于功能模块的活跃度划分多电压域(Multi-VDD),非关键模块采用低电压…...

SLOT:测试时样本专属语言模型优化,让大模型推理更精准!
SLOT:测试时样本专属语言模型优化,让大模型推理更精准! 大语言模型(LLM)在复杂指令处理上常显不足,本文提出SLOT方法,通过轻量级测试时优化,让模型更贴合单个提示。实验显示&#x…...

《计算机组成原理》第 10 章 - 控制单元的设计
目录 10.1 组合逻辑设计 10.1.1 组合逻辑控制单元框图 10.1.2 微操作的节拍安排 10.1.3 组合逻辑设计步骤 10.2 微程序设计 10.2.1 微程序设计思想的产生 10.2.2 微程序控制单元框图及工作原理 10.2.3 微指令的编码方式 1. 直接编码(水平型) 2.…...

【数据结构与算法】模拟
成熟不是为了走向复杂,而是为了抵达天真;不是为了变得深沉,而是为了保持清醒。 前言 这是我自己刷算法题的第五篇博客总结。 上一期笔记是关于前缀和算法: 【数据结构与算法】前缀和-CSDN博客https://blog.csdn.net/hsy1603914691…...

PyTorch入门-torchvision
torchvision torchvision 是 PyTorch 的一个重要扩展库,专门针对计算机视觉任务设计。它提供了丰富的预训练模型、常用数据集、图像变换工具和计算机视觉组件,大大简化了视觉相关深度学习项目的开发流程。 我们可以在Pytorch的官网找到torchvision的文…...
LVS负载均衡群集技术深度解析
第一章 群集技术概述与LVS基础 1.1 群集技术的核心价值与分类 随着互联网应用的复杂化,单台服务器在性能、可靠性、扩展性等方面逐渐成为瓶颈。群集技术(Cluster)通过整合多台服务器资源,以统一入口对外提供服务,成为…...

18、Python字符串全解析:Unicode支持、三种创建方式与长度计算实战
适合人群:零基础自学者 | 编程小白快速入门 阅读时长:约6分钟 文章目录 一、问题:Python的字符串是什么?1、例子1:多语言支持演示2、例子2:字符串不可变性验证3、答案:(1)…...

5月27日复盘-Transformer介绍
5月27日复盘 二、层归一化 层归一化,Layer Normalization。 Layer Normalizatioh和Batch Normalization都是用来规范化中间特征分布,稳定和加速神经网络训练的,但它们在处理方式、应用场景和结构上有本质区别。 1. 核心区别 特征BatchNo…...
CSV数据处理全指南:从基础到实战
CSV(Comma-Separated Values,逗号分隔值) 是一种简单的文件格式,用于存储和交换表格数据(如电子表格或数据库中的记录)。其核心特点是用逗号分隔字段,以换行符分隔记录。 CSV 的定义与结构 基本…...

MyBatis-Plus一站式增强组件MyBatis-Plus-kit(更新2.0版本):零Controller也能生成API?
MyBatis-Plus-Kit 🚀 MyBatis-Plus-Kit 是基于MyBatis-Plus的增强组件,专注于提升开发效率,支持零侵入、即插即用的能力扩展。它聚焦于 免写 Controller、代码一键生成、通用响应封装 等核心场景,让您只需专注业务建模࿰…...

实时数仓flick+clickhouse启动命令
1、启动zookeeper zk.sh start 2、启动DFS,Hadoop集群 start-dfs.sh 3、启动yarn start-yarn.sh 4、启动kafka 启动Kafka集群 bin/kafka-server-start.sh -daemon config/server.properties 查看Kafka topic 列表 bin/kafka-topics.sh --bootstrap-server local…...

【Git】Commit Hash vs Change-Id
文章目录 1、Commit 号2、Change-Id 号3、区别与联系4、实际场景示例5、为什么需要两者?6、总结附录——Gerrit 在 Git 和代码审查工具(如 Gerrit)中,Commit 号(Commit Hash) 和 Change-Id 号 是两个不同的…...
Netty学习专栏(六):深度解析Netty核心参数——从参数配置到生产级优化
文章目录 前言一、核心参数全景解析1.1 基础网络层参数1.2 内存管理参数1.3 水位线控制1.4 高级参数与系统级优化 二、生产级优化策略2.1 高并发场景优化2.2 低延迟场景优化 总结 前言 在分布式系统和高并发场景中,Netty作为高性能网络通信框架的核心地位无可替代。…...
服务器磁盘按阵列划分为哪几类
以下是服务器磁盘阵列(RAID)的详细分类及技术解析,基于现行行业标准与实践应用: 一、主流RAID级别分类 1. RAID 0(条带化) 技术原理:数据分块后并行写入多块磁盘,无…...
在WPF中添加动画背景
在WPF中添加动画背景 在WPF中创建动画背景可以大大增强应用程序的视觉效果。以下是几种实现动画背景的方法: 方法1:使用动画ImageBrush(图片轮播) <Window x:Class"AnimatedBackground.MainWindow"xmlns"htt…...

【KWDB创作者计划】_KWDB分布式多模数据库智能交通应用——高并发时序处理与多模数据融合实践
导读:本文主要探讨了基于KWDB的分布式多模数据库智能交通应用场景,进行了高并发时序处理与多模数据融合实践方向的思考。探索智慧交通领域的数据实时处理与存储资源利用方面的建设思路。 本文目录 一、智能交通数据架构革命 1.1 传统架构瓶颈 …...
Android 中的 ViewModel详解
在 Android 开发中,ViewModel 是 Jetpack 架构组件的核心成员之一,专为管理与界面相关的数据而设计。它通过生命周期感知能力,确保数据在配置变更(如屏幕旋转)时持久存在,并将数据逻辑与 UI 控制器…...

Java集合框架与三层架构实战指南:从基础到企业级应用
一、集合框架深度解析 1. List集合的武林争霸 ArrayList: 数组结构:内存连续,查询效率O(1) 扩容机制:默认扩容1.5倍(源码示例) private void grow(int minCapacity) {int oldCapacity elementData.len…...

6个月Python学习计划 Day 2 - 条件判断、用户输入、格式化输出
6个月Python学习计划:从入门到AI实战(前端开发者进阶指南) Python 基础入门 & 开发环境搭建 🎯 今日目标 学会使用 input() 获取用户输入掌握 if/else/elif 条件判断语法熟悉格式化输出方式:f-string、format() …...
使用docker容器部署Elasticsearch和Kibana
简介:(Elasticsearch) elasticsearch简称Es, 是位于Elastic Stack核心的分布式搜索和分析引擎。它为所有类型的数据提供近乎实时的搜索和分析。无论您拥有机构化或非结构化的文本、数字数据还是地理空间数据,Es都能以支持快速搜索…...
批量处理合并拆分pdf功能 OCR 准确率高 免费开源
各位 PDF 编辑小白们,今天咱来唠唠 PDFXEdit10_Portable 这款软件。 先说说它的核心功能和适用场景。这玩意儿是个便携式的 PDF 编辑工具,不用安装就能直接用,能改 PDF 里的文本、图片,还能批注、调整格式,老方便了。…...
Unity—lua基础语法
Lua 语言执行方式 编译型语言:代码在运行前需要使用编译器,先将程序源代码编译为可执行文件,再执行 C/C Java C# Go Objective-C 解释型语言(脚本语言) 需要提前安装编译语言解析器,运行时使用解析…...

目标检测 TaskAlignedAssigner 原理
文章目录 TaskAlignedAssigner 原理和代码使用示例 TaskAlignedAssigner 原理和代码 原理主要是结合预测的分类分数和边界框与真实标注的信息,找出与真实目标最匹配的锚点,为这些锚点分配对应的目标标签、边界框和分数。 TaskAlignedAssigner 是目标检…...
Qt popup窗口半透明背景
半透明弹窗需要paintEvent()接口支持 方法一:使用setStyleSheet设置半透明样式,如果是子窗口,则可注释构建函数内属性设置 class TranslucentWidget : public QWidget { public: explicit TranslucentWidget(QWidget *parent nullptr)…...

游戏:元梦之星游戏开发代码(谢苏)
《元梦之星》是一款轻松社交派对游戏,玩家们可以化身星宝,体验纯粹的游玩乐趣,收获简单的快乐。无论i人e人,都能轻松找到属于自己的社交方式。 《元梦之星》的快乐,可以是闯关夺冠时的激动,谁是狼人推理的巧妙,峡谷3V3打赢团战的爽感。也可以是星梦广场开…...