kotlin与MVVM结合使用总结(一)
一、Kotlin 与 MVVM 结合的核心优势
-
代码简洁性
- 数据类(
data class
)简化 Model 层定义,自动生成equals/hashCode/toString
- 扩展函数简化 View 层逻辑(如点击事件扩展)
lateinit
/by lazy
优化 ViewModel 属性初始化
- 数据类(
-
异步处理优化
- 协程(Coroutines)替代 RxJava,轻量且代码可读性强
withContext(Dispatchers.IO)
切换线程,配合LiveData
更新 UI
-
响应式编程
LiveData
+StateFlow
实现数据双向绑定Flow
替代LiveData
处理复杂数据流(如网络请求重试)
-
生命周期感知
ViewModel
配合SavedStateHandle
保存状态LifecycleOwner
简化生命周期监听
二、MVVM 实现细节
-
ViewModel 层
- 使用 Kotlin
@HiltViewModel
注解依赖注入(结合 Hilt) - 协程启动任务:
viewModelScope.launch { ... }
StateFlow
封装业务状态,替代可变 LiveData
- 使用 Kotlin
-
View 层
- DataBinding 绑定布局,Kotlin 表达式简化逻辑(如
@{user.name ?: "Guest"}
) ViewBinding
替代findViewById
,类型安全- 协程与
lifecycleScope
结合处理异步任务
- DataBinding 绑定布局,Kotlin 表达式简化逻辑(如
-
Model 层
- 数据类定义实体,
@SerializedName
配合 Retrofit 解析 JSON - 仓库(Repository)模式隔离数据源,Kotlin 密封类定义请求状态(如
Result.Success/Error
)
- 数据类定义实体,
之间的关联:
- View 持有 ViewModel:View(如 Activity、Fragment 等)会创建并持有 ViewModel 的引用,通过数据绑定机制观察 ViewModel 中的数据变化。
- ViewModel 持有 Model:ViewModel 持有 Model 的引用,从 Model 获取数据并处理业务逻辑,将处理后的数据暴露给 View。
- Model 不持有 View 和 ViewModel:Model 专注于数据的存储和获取,不依赖于 View 和 ViewModel。
三、面试高频问题
1、ViewModel 是如何保持数据的
ViewModel 使用了 Android 架构组件中的 SavedStateHandle 来保持数据。
SavedStateHandle 是一个键值对集合,用于在配置更改(如屏幕旋转)时保存和恢复数据。
当 Activity 或 Fragment 因配置更改而销毁重建时,ViewModel 不会被销毁,SavedStateHandle 中的数据会被保留,从而实现数据的保持。
2、 ViewModel 是怎么做到在 Activity 销毁重建新实例之后还能保持不变的
在 Android 中,ViewModel 的生命周期与 Activity 或 Fragment 的生命周期不同。
当 Activity 或 Fragment 因配置更改(如屏幕旋转)而销毁重建时,系统会自动保留 ViewModel 的实例。
这是通过 ViewModelStore 来实现的,ViewModelStore 是一个存储 ViewModel 实例的容器,Activity 或 Fragment 会持有一个 ViewModelStore 的引用。
当 Activity 或 Fragment 重建时,会从 ViewModelStore 中获取之前的 ViewModel 实例,从而保证 ViewModel 中的数据不会丢失。
四、最佳实践
- View:对应 Android 中的 Activity、Fragment、View 等,负责界面的绘制和用户交互的处理。
- ViewModel:对应 Android 中的 ViewModel 类,负责处理业务逻辑和数据的转换,通过 LiveData、StateFlow 等将数据暴露给 View。
- Model:对应数据的实体类(如 Kotlin 中的数据类)和数据获取的仓库类(Repository),负责数据的存储和获取。
演示代码:
ViewModel:
@HiltViewModel
class MainViewModel @Inject constructor(private val repository: UserRepository
) : ViewModel() {private val _user = MutableStateFlow<User?>(null)val user: StateFlow<User?> = _userfun fetchUser(userId: String) {viewModelScope.launch {try {_user.value = repository.getUser(userId)} catch (e: Exception) {// 处理错误}}}
}
View 层(Fragment):
class MainFragment : Fragment() {private val viewModel by viewModels<MainViewModel>()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewModel.user.collectAsState().observe(viewLifecycleOwner) { user ->// 更新UI}}
}
真实操作:
首先,确保在项目的 build.gradle
中添加必要的依赖,如 ViewModel、LiveData、Kotlin 协程等:
dependencies {// ViewModel 和 LiveDataimplementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'// Kotlin 协程implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}
Model 层
Model 层主要负责数据的定义和数据的获取。这里以一个简单的用户数据为例:
// 定义用户数据类
data class User(val id: Int, val name: String, val age: Int)// 模拟数据获取的仓库类
class UserRepository {// 模拟网络请求,使用协程进行异步操作suspend fun getUser(id: Int): User {// 模拟耗时操作delay(1000)return User(id, "John Doe", 30)}
}
ViewModel 层
ViewModel 层负责处理业务逻辑,并将数据暴露给 View 层。它通过 LiveData 或 StateFlow 来实现数据的响应式更新。
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launchclass UserViewModel(private val userRepository: UserRepository) : ViewModel() {// 使用 MutableStateFlow 来存储和更新用户数据private val _user = MutableStateFlow<User?>(null)// 对外暴露不可变的 StateFlowval user: StateFlow<User?> = _user// 获取用户数据的方法fun fetchUser(id: Int) {viewModelScope.launch {try {// 调用仓库类的方法获取用户数据val user = userRepository.getUser(id)// 更新 StateFlow 的值_user.value = user} catch (e: Exception) {// 处理异常e.printStackTrace()}}}
}
View 层
View 层通常是 Activity 或 Fragment,负责显示数据和处理用户交互。这里以 Fragment 为例:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launchclass UserFragment : Fragment() {private val userViewModel: UserViewModel by lazy {UserViewModel(UserRepository())}override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_user, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 启动协程来收集 StateFlow 的数据lifecycleScope.launch {userViewModel.user.collect { user ->user?.let {// 更新 UI// 这里可以根据实际情况更新 TextView 等视图组件// 例如:textView.text = "${it.name}, ${it.age}"}}}// 触发数据获取userViewModel.fetchUser(1)}
}
代码解释
- Model 层:
data class User
:使用 Kotlin 的数据类简洁地定义了用户数据结构,自动生成equals
、hashCode
和toString
方法。UserRepository
:模拟了数据的获取过程,使用suspend
关键字和delay
函数模拟网络请求的异步操作,使用协程进行异步处理。
- ViewModel 层:
MutableStateFlow
和StateFlow
:用于存储和暴露用户数据,实现数据的响应式更新。MutableStateFlow
用于内部数据的更新,StateFlow
用于对外暴露不可变的数据。viewModelScope.launch
:在 ViewModel 中使用协程进行异步操作,确保在 ViewModel 的生命周期内执行。当 ViewModel 被销毁时,协程会自动取消。
- View 层:
lifecycleScope.launch
:在 Fragment 中使用协程来收集StateFlow
的数据,确保在 Fragment 的生命周期内执行。当 Fragment 被销毁时,协程会自动取消。userViewModel.fetchUser(1)
:触发数据获取操作,调用 ViewModel 中的方法获取用户数据。
总结:
Kotlin 通过协程、数据类、扩展函数等特性大幅提升了 MVVM 的开发效率和代码质量,
面试中需重点关注异步处理、数据绑定、依赖注入及生命周期管理。
相关文章:
kotlin与MVVM结合使用总结(一)
一、Kotlin 与 MVVM 结合的核心优势 代码简洁性 数据类(data class)简化 Model 层定义,自动生成equals/hashCode/toString扩展函数简化 View 层逻辑(如点击事件扩展)lateinit/by lazy优化 ViewModel 属性初始化 异步处…...
达妙电机CAN通信及实验
项目进一步往下做的时候,要上实物了,需要用到达妙电机,虽然有说明书和例程,但是STM32控制电机的具体时间还是花了些时间,我的板子和例程的有些区别,中间很多地方都需要进行修改完善,而且还补充了…...
语音合成之四基于LLM的语音合成
基于LLM的语音合成 1.技术架构1.1 LlaSA1.2 CosyVoice (和 CosyVoice2)1.3 SparkTTS 2 特性对比2.1 零样本语音克隆2.2 多语种支持2.3 可控语音生成2.4 计算效率和模型大小 总结 当前,在大型语言模型(Large Language Models,LLMs)…...
Docker Python 官方镜像使用说明(TAG说明)
Docker Python 官方镜像使用说明(TAG说明) 本文将以python的3.12版本,详细讲解官方 Python 镜像 的TAGS含义 官方文档:https://github.com/tuonioooo/docker 🧭 一张图先看懂(最常见 Tag) py…...
Node.js 开发用户登录功能(使用mysql实现)
在 Web 开发中,用户登录功能是一个基础且重要的部分。、 一、环境搭建 在开始开发之前,我们需要搭建好相应的开发环境。以下是所需的工具和库: Node.js:作为 JavaScript 的运行环境,为我们的项目提供支持。mysql2&am…...
程序员学英文之Shipment Claim 运输和索赔
Time is precious , don’t waste your time, you should spend your time on something valuable . 时间很宝贵,不要浪费时间,你应该把时间用在有 价值的事情上。 Dia-1: Shipment by Voyage Charter 租船装运 1. May I know when your bo…...
python实战项目64:selenium采集软科中国大学排名数据
python实战项目64:selenium采集软科中国大学排名数据 一、项目需求二、流程分析三、完整代码一、项目需求 本项目的需求是使用selenium采集软科中国大学排名的数据。网站首页如下: 抓取此网页数据一般有两种方式,一种是直接发requests请求,我们这里采用的是使用selenium控…...
Linux服务器:在ufw防火墙设置这套规则sudo ufw allow from 172.0.0.0/8,为什么容器就可以访问宿主机的服务了?
在 Docker 环境中,容器默认使用 桥接网络(bridge),宿主机和容器之间的通信会受到防火墙(如 ufw)的限制。当你执行 sudo ufw allow from 172.0.0.0/8 后,容器可以访问宿主机上的服务,原因如下: 1. Docker 默认使用 172.x.x.x 网段 Docker 默认会创建一个 docker0 网桥…...
Google搜索技巧
谷歌搜索 1. 使用双引号 (" ") 精确匹配短语 ● 例子:"人工智能的定义" ● 作用:确保搜索结果中包含完全匹配的短语,而不是单独的单词。 2. 使用减号 (-) 排除特定词语 ● 例子:苹果 -水果 ● 作用&…...
Reactor编程模型介绍
Reactor 模型是一种基于事件驱动的编程模型,广泛应用于高并发网络服务器的设计中。它通过事件循环和回调机制,将事件的处理逻辑从主线程中解耦出来,从而实现高效的异步 I/O 操作。Reactor 模型的核心思想是利用一个或多个事件分发器(Reactor)来监听各种事件(如 I/O 事件、…...
C++笔记-stack_queue(含deque,priority_queue,仿函数的讲解)
一.stack和queue的基本使用 stack和queue就是我们之前所学的栈和队列,这两个和之前学的vector,list不太一样: 这是vector和list的,注意第一行中写的containers,代表这两个都是容器,但是: stac…...
意见反馈留言二维码制作
意见反馈对于工作整改具有重要作用,在工作中一味埋头苦干不如抬头多听听反馈声音。而传统的反馈内容投递后,因为繁琐性和时效性的枷锁,往往石沉大海,不知何时才能得到回应,这就导致反馈信息的延迟,一些时效…...
扣子智能体平台深度解读:功能剖析与全流程工作流详解
在上一篇文章中,我们已经带大家了解了“智能体”这一概念的内涵,并通过扣子智能体平台的各大模块做了初步介绍,同时用一个简单的示例演示了如何构建和部署第一个智能体。那篇文章打好了基础,让大家对智能体的基本组成与工作方式有…...
C语言五子棋项目
头文件与宏定义 #include <graphics.h> #include <conio.h> graphics.h:EasyX 图形库,提供图形绘制功能(画线、画圆、显示文字等)。 conio.h:提供控制台输入输出函数(这里只是为了兼容性&…...
建筑安全员 A 证与 C 证:差异决定职业方向
在建筑行业的职业发展道路上,安全员 A 证和 C 证就像两条不同的岔路,它们之间的差异,在很大程度上决定了从业者的职业方向。 从证书性质和用途来看,A 证是从业资格证书,更像是一把开启安全管理高层岗位的 “金钥匙”。…...
长连接、短连接与WebSocket的基本知识
目录 前言正文 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器,无代码爬取,就来:bright.cn Java基本知识: java框架 零基础从入门到精通的学习路线 附…...
MySQL常见问题解答
一、安装与配置问题 1. 安装失败(权限 / 依赖 / 端口冲突) 权限问题:以管理员身份运行安装程序(Windows)或使用sudo(Linux)。依赖缺失: Windows 需安装 Visual C++ Redistributable(如 2013 版)。Linux 通过包管理器安装依赖(如libaio、perl)。端口冲突: 检查 33…...
Codeforces Round 1019 (Div. 2)(ABCD)
A. Common Multiple 翻译: 给你一个整数数组 a1,a2,...,an。如果存在一个数组 y1,y2,...,ym,且 y 的元素是不同的(换句话说,对于所有 1≤i<j≤m 的数组,yi≠yj),并且对于所有 1≤i≤m 的数组…...
爬虫学习总结
通过前几次课,我们学习了爬虫的相关基础知识。 以下是我对爬虫学习做的一些总结: 一、认识爬虫:开启数据抓取之旅 1.1 什么是网络爬虫 网络爬虫就像是一个不知疲倦的 “数据搬运工”,它能按照预先设定的规则,自动…...
UE5的 Modify Curve 蓝图节点
In Unreal Engine’s Animation Blueprints, the Modify Curve node lets you drive and alter any named Animation Curve on your character at runtime. The Apply Mode setting on that node controls how the “new” value you feed in (via the added curve‐input pin)…...
鸿蒙中的并发线程间通信、线程间通信对象
目录 并发线程间通信1. 线程间通信对象1.1 普通对象1.2 ArrayBuufer对象1.3 SharedArrayBuffer对象1.4 Transferable对象(NativeBinding对象)1.5 Sendable对象简介异步锁ASON解析与生成共享容器共享模块Sendable对象冻结 2 线程间通信场景2.1 使用TaskPo…...
Elasticsearch内核探秘:从Shard分配到网络通信的深度实践指南
#作者:孙德新 文章目录 一、底层模块深入解析之shard allocation1、shard allocation的介绍2、cluster level shard allocation介绍3、disk-based shard allocation介绍4、shard allocation awareness5、shard allocation filtering6、node下线时的shard延迟分配7、…...
Vue3 模板语法
目录 一、插值语法 {{ }} 二、核心指令 三、动态属性绑定 四、事件修饰符 五、条件渲染 vs 列表渲染总结 六、最佳实践 示例 1:插值语法 & 基础绑定 示例 2:条件渲染 示例 3:列表渲染 示例 4:事件处理 示例 5&…...
第1节:Backtrader到底是个啥?能干嘛?
——“框架在手,天下我有;不是吹,Backtrader真香警告!” 🐣 一句话简介 Backtrader 是一个 专门为量化交易打造的 Python 回测框架,说白了,它就是一个量化策略“模拟器控制台评审团”ÿ…...
Java基础第21天-正则表达式
正则表达式是对字符串执行模式匹配的技术 如果想灵活的运用正则表达式,必须了解其中各种元字符的功能,元字符从功能上大致分为: 限定符选择匹配符分组组合和反向引用符特殊字符字符匹配符定位符 转义号\\:在我们使用正则表达式去检索某些特…...
【Pandas】pandas DataFrame mod
Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于执行 DataFrame 与另一个对象(如 DataFrame、Series 或标量)的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于执行 DataFrame 与另一个对象&…...
【哈希表】1399. 统计最大组的数目
1399. 统计最大组的数目 - 力扣(LeetCode) 给你一个整数 n 。请你先求出从 1 到 n 的每个整数 10 进制表示下的数位和(每一位上的数字相加),然后把数位和相等的数字放到同一个组中。 请你统计每个组中的数字数目&…...
57、Spring Boot 最佳实践
Spring Boot 最佳实践 一. 开发规范与代码风格 编写高质量的代码不仅需要功能的实现,还需要遵循一定的规范和代码风格,以提高代码的可读性、可维护性和协作效率。以下是 Spring Boot 开发中的一些关键规范和代码风格建议。 1. 代码命名规范 在编写代码时,命名是非常重要的…...
Java高级:数据库访问优化
系列文章目录 Java高级部分 JDBC编程 文章目录 系列文章目录前言一、编写属性文件:二、编写DBUtil工具类:三、使用DBUtil工具类:总结 前言 通过我之前发的数据库连接,数据库连接https://blog.csdn.net/2301_81776550/article/det…...
升级xcode16之后react-native-zip-archive不兼容,unsupported option ‘-G‘
问题 升级xcode到16之后,xcode build报错:unsupported option -G for target x86_64-apple-ios13.4-simulator (in target RNZipArchive from project Pods) 出现原因 在 React Native 项目中,当你将 Xcode 升级到 16 后,可能会遇到 RNZipArchive 相关的编译错误,特别是…...
基于MTF的1D-2D-CNN-LSTM-Attention时序图像多模态融合的故障识别,适合研究学习(Matlab完整源码和数据),附模型研究报告
基于MTF的1D-2D-CNN-LSTM-Attention时序图像多模态融合的故障识别,适合研究学习(Matlab完整源码和数据),附模型研究报告 目录 基于MTF的1D-2D-CNN-LSTM-Attention时序图像多模态融合的故障识别,适合研究学习࿰…...
逻辑漏洞安全
逻辑漏洞是指由于程序逻辑不严导致一些逻辑分支处理错误造成的漏洞。 在实际开发中,因为开发者水平不一没有安全意识,而且业务发展迅速内部测试没有及时到位,所以常常会出现类似的漏洞。 由于开发者/设计者在开发过程中,由于代码…...
基于PaddleOCR对图片中的excel进行识别并转换成word优化(二)
0、原图 一、优化地方 计算行的时候,采用概率分布去统计差值概率比较大的即为所要的值。 def find_common_difference(array):"""判断数组中每个元素的差值是否相等,并返回该差值:param array: 二维数组,其中每个元素是一个…...
5.2.3 WPF 中 XAML 文件 Converter 使用介绍
Converter(转换器)在 WPF 数据绑定中扮演着重要角色,用于在源数据和目标属性之间进行值转换 举例来说:我想用一个bool量来控制一个背景,为true时,显示红色;为false时背景用默认颜色。因此 Backg…...
基于STM32_HAL库的HC-08蓝牙插座项目
基于STM32_HAL库的HC-08蓝牙插座 文章目录 基于STM32_HAL库的HC-08蓝牙插座一、项目需求二、硬件连接三、项目实现3.1 CubeMX配置3.2 以阻塞的方式实现3.3 以中断的方式实现 一、项目需求 通过手机可以控制开发板上的LED或者继电器 二、硬件连接 首先将HC-08蓝牙模块连接到我们…...
SwiftUI 3.Button介绍和使用
SwiftUI 的 Button 是用于触发用户操作的核心交互组件。以下是 Button 的详细介绍和使用示例: 一、基础用法 1. 创建简单按钮 Button("点击我") {print("按钮被点击了") }2. 自定义按钮内容 Button {// 点击动作 } label: {Text("保存&…...
Linux 管道理解
一、什么是管道 1.1 unix中最古老的进程间通信 1.2 一个进程链接到另一个进程的数据流称为“管道”: 图解: 二、管道通信的原理 2.1当我们创建一个进程然后打开一个文件的时候 会经过以下步骤: ①首先要描述这个进程,为这个…...
从并发问题衍生出的Spring的七种事务传播行为
最近在处理一个BPM流程时,遇到了并发问题,原因是事务粒度太大了,导致等待lock超时。今天刚好借此机会分享下Spring框架中提供的7种事务传播行为。 在 Spring中,Transactional 注解支持配置事务的传播行为,用于指定当一…...
第十五届蓝桥杯 2024 C/C++组 艺术与篮球
目录 题目: 题目描述: 题目链接: 思路: 思路详解: 代码: 代码详解: 题目: 题目描述: 题目链接: P10385 [蓝桥杯 2024 省 A] 艺术与篮球 - 洛谷 艺术…...
Python内置函数---bin()
用于将整数转换为二进制字符串 1. 基本语法与参数 bin(x) 参数: x 必须为整数( int 类型),或实现了 __index__() 方法的自定义对象(该方法需返回整数) 。 返回值:以 0b 开头的二进制字符串。…...
网络socks 代理
在系统/终端中设了这样的环境变量,而没有在代码中覆盖,HTTPX 就会启用该 socks 代理。 env | grep proxy https_proxyhttps://proxyhk.zte.com.cn:80 http_proxyhttp://proxyhk.zte.com.cn:80 no_proxylocalhost,127.0.0.0/8,::1,zte.com.cn,zte.intra,…...
【正则表达式】核心知识点全景解析
目录 一、基础语法架构二、核心元字符详解三、高级匹配技巧1. 字符集合2. 分组与引用3. 断言机制 四、Python re模块核心方法五、性能优化策略1. 编译重用2. 避免回溯陷阱3. 选择高效量词 六、典型应用场景1. 数据验证2. 数据提取3. 文本清洗 七、调试技巧宝典1. 可视化调试工具…...
深度学习--ResNet残差神经网络解析
文章目录 前言一、什么是ResNet网络二、传统卷积神经网络存在的问题1、梯度消失和梯度爆炸2、退化问题 三、如何解决问题四、残差结构五、18层残差网络1、解释2、隔层相加优点3、隔层相加数值增大问题 六、18层残差网络以外的表格示例七、BN层(Batch Normalization&…...
数据结构线性表的顺序存储结构
线性表是由零个或多个数据元素组成的有序序列。 特点: 数据元素间是有顺序的; 数据元素的个数是有限的; 一般来说,数据元素的类型是相同的(强类型语言)。c/c是强类型语言,必须指定数据类型。…...
深入解析C++ STL Queue:先进先出的数据结构
一、引言 在计算机科学中,队列(Queue)作为一种遵循先进先出(FIFO)原则的数据结构,是算法设计和系统开发的基础组件。C STL中的queue容器适配器以简洁的接口封装了底层容器的操作,为开发者提供…...
永磁同步电机控制算法-反馈线性化控制
一、原理介绍 基于非线性系统的精确线性化控制方法,采用精确反馈线性化原理对永磁同步电机进行输入-输出线性化,该方法通过坐标变换和状态反馈将系统的数学模型转变为两个线性子系统,在实现线性化的同时也对系统中电流和转速存在的耦合现象进…...
开源 RAG 引擎:文档理解精准、检索高效、可视化干预灵活,一站式搞定
引言: RAGFlow 是一款基于深度文档理解的开源 RAG 引擎,与 LLM 结合后可实现精准引用问答。它支持 20 多种文档格式解析,配备智能分块策略及混合检索方案,还有可视化干预界面,且支持 Docker 快速部署,堪称…...
URP-UGUI相关知识
一、UGUI的基本组成部分 Canvas (画布)所有UI都需要放在Canvas画布下面,不然无法显示EventSystem 所有的事件响应系统都需要依赖于EventSystem,若删除该组件,交互效果就 不会显示 1.Canvas(画…...
COMSOL多孔结构传热模拟
多孔结构传热模拟涉及对多孔介质内部复杂的热量传递过程进行建模和分析,这类模拟对于优化材料设计、提高能源效率以及解决环境问题等方面具有重要意义。本案例介绍在COMSOL内建立全连通多孔结构几何模型,并将孔隙及基体划分两相材料,进行多孔…...
【CSS】层叠,优先级与继承(四):层叠,优先级与继承的关系
层叠,优先级与继承的关系 前文概括 【CSS】层叠,优先级与继承(一):超详细层叠知识点 【CSS】层叠、优先级与继承(二):超详细优先级知识点 【CSS】层叠,优先级与继承&am…...