《Jetpack Compose从入门到实战》第九章 Accompanist 与第三方组件库

目录
- Accompanist
- SystemUiController
- Pager
- SwipeRefresh
- Flow Layout
- Insets
- Lottie
- Coil
- AsyncImage
- SubcomposeAsyncImage
- AsyncImagePainter
Accompanist
- 最新可用版本
- accompanist官方文档
SystemUiController
- 依赖:implementation “com.google.accompanist:accompanist-systemuicontroller:<version>”
@Composable
fun SystemUiTest() {Box(modifier = Modifier.fillMaxSize()) {val systemUiController = rememberSystemUiController()val useDarkIcons = MaterialTheme.colors.isLightSideEffect {systemUiController.setSystemBarsColor(color = Color.Transparent, darkIcons = useDarkIcons)}
// TopAppBar(title = { Text(text = "TopAppBar") }, modifier = Modifier.statusBarsPadding(), backgroundColor = Color.Gray)//使用com.google.accompanist:accompanist-insets-ui:0.30.1 实现沉浸式状态栏的效果TopAppBar(title = { Text(text = "TopAppBar") }, backgroundColor = Color.Gray, contentPadding = WindowInsets.statusBars.asPaddingValues())}
}
@Composable
fun SystemUiControllerDemo() {val systemUiController = rememberSystemUiController()val useDarkIcons = MaterialTheme.colors.isLightval colorPanel = listOf(Color.Gray,Color.Red,Color.Black,Color.Cyan,Color.Transparent,Color.DarkGray,Color.LightGray,Color.Yellow)SideEffect {systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)}Column(Modifier.systemBarsPadding().fillMaxSize().background(Color(0xFF0079D3))) {colorPanel.forEach {Box(modifier = Modifier.fillMaxWidth().height(48.dp).background(it).clickable {systemUiController.setSystemBarsColor(it, useDarkIcons)})}}
}
- 略直接设置状态栏和底部导航栏颜色的方法
Pager
- implementation “com.google.accompanist:accompanist-pager:$accompanist_version”
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerTest() {Box(modifier = Modifier.fillMaxSize()) {val pagerState = rememberPagerState()val scope = rememberCoroutineScope()HorizontalPager(pageCount = 3, modifier = Modifier.fillMaxSize(), state = pagerState) { page ->when (page) {0 -> ColorBox(color = Color.Blue, pageIndex = page)1 -> ColorBox(color = Color.Cyan, pageIndex = page)2 -> ColorBox(color = Color.Magenta, pageIndex = page)}}SideEffect {scope.launch {delay(3000)pagerState.scrollToPage(2)}}}
}@Composable
fun ColorBox(color: Color, pageIndex: Int) {Box(modifier = Modifier.fillMaxSize().background(color = color), contentAlignment = Alignment.Center) {Text(text = "page $pageIndex")}
}
- 略直接跳转到某个页面的方法
@OptIn(ExperimentalPagerApi::class)
@Composable
fun PagerDemo() {val pagerState = rememberPagerState()val scope = rememberCoroutineScope()var selectedScreens by remember { mutableStateOf(0) }val screens = listOf(Screens("首页", Icons.Filled.Home) { Home() },Screens("我喜欢的", Icons.Filled.Favorite) { Favorite() },Screens("设置", Icons.Filled.Settings) { Settings() })Scaffold(bottomBar = {BottomNavigationBar(selectedScreens,screens,onClick = {selectedScreens = itscope.launch { pagerState.scrollToPage(selectedScreens) }})}) {HorizontalPager(count = screens.size,modifier = Modifier.fillMaxSize(),state = pagerState) { page ->screens.forEachIndexed { index, screens ->when (page) {index -> screens.content()}}}}LaunchedEffect(pagerState) {snapshotFlow { pagerState.currentPage }.collect { page ->selectedScreens = page}}}@Composable
fun Home() {Box(modifier = Modifier.fillMaxSize().background(Color.Gray),contentAlignment = Alignment.Center) {Text(text = "1. 首页🤭",style = MaterialTheme.typography.h5)}
}@Composable
fun Favorite() {Box(modifier = Modifier.fillMaxSize().background(Color(0xFFF8F8F8)),contentAlignment = Alignment.Center) {Text(text = "2. 我喜欢的❤",style = MaterialTheme.typography.h5)}
}
@Composable
fun Settings() {Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center) {Text(text = "3. 设置⚙",style = MaterialTheme.typography.h5)}
}
@Composable
fun BottomNavigationBar(selectedScreen: Int,screens: List<Screens>,onClick: (targetIndex: Int) -> Unit
) {NavigationBar {screens.forEachIndexed { index, screen ->NavigationBarItem(icon = { Icon(screen.iconVector, contentDescription = null) },label = { Text(screen.label) },selected = selectedScreen == index,onClick = { onClick(index) })}}
}data class Screens(val label: String,val iconVector: ImageVector,val content: @Composable () -> Unit
)
SwipeRefresh
- implementation “com.google.accompanist:accompanist-swiperefresh:$accompanist_version”
@Preview
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SwipeRefreshTest() {Box(modifier = Modifier.fillMaxSize()) {val viewModel: MyViewModel = viewModel()val isRefreshing by viewModel.isRefreshing.collectAsState()val background by animateColorAsState(targetValue = viewModel.background, animationSpec = tween(1000), label = "backgroundAnim")/* SwipeRefresh(state = rememberSwipeRefreshState(isRefreshing), onRefresh = { viewModel.refresh() }) {Box(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).background(background))}*/val pullRefreshState = rememberPullRefreshState(refreshing = isRefreshing, onRefresh = { viewModel.refresh() })Box(modifier = Modifier.fillMaxSize().pullRefresh(pullRefreshState).verticalScroll(rememberScrollState()).background(background),) {PullRefreshIndicator(isRefreshing, pullRefreshState, modifier = Modifier.align(Alignment.TopCenter))}}
}class MyViewModel : ViewModel() {private val _isRefreshing = MutableStateFlow(false)private val colorPanel = listOf(Color.Gray, Color.Red, Color.Black, Color.Cyan, Color.DarkGray, Color.LightGray, Color.Yellow)val isRefreshing: StateFlow<Boolean>get() = _isRefreshingvar background by mutableStateOf(Color.Gray)fun refresh() {viewModelScope.launch {_isRefreshing.emit(true)delay(1000)background = colorPanel.random()_isRefreshing.emit(false)}}}
Flow Layout
- 会自动换行的row和Column
- implementation “com.google.accompanist:accompanist-flowlayout:$accompanist_version”
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun FlowLayoutTest() {Column(modifier = Modifier.fillMaxSize()) {FlowRow(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically, maxItemsInEachRow = 3) {Text(text = "text1")Text(text = "text2")Text(text = "text3")Text(text = "text4")Text(text = "text5")Text(text = "text6")}Divider(modifier = Modifier.fillMaxWidth())FlowColumn(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally, maxItemsInEachColumn = 3) {Text(text = "text1")Text(text = "text2")Text(text = "text3")Text(text = "text4")Text(text = "text5")Text(text = "text6")}}
}
@Composable
fun Tag(modifier: Modifier = Modifier,shape: Shape = CircleShape,elevation: Dp = 0.dp,leadingIcon: @Composable (() -> Unit)? = null,trailingIcon: @Composable (() -> Unit)? = null,text: String,textStyle: TextStyle = TextStyle(fontWeight = FontWeight.Normal,fontSize = 16.sp,letterSpacing = 0.15.sp),backgroundColor: Color = Color(0xFFE8E8E8),border: BorderStroke? = null,onClick:() -> Unit
){Surface(shape = shape,color = backgroundColor,modifier = modifier,elevation = elevation,border = border) {Row(modifier = Modifier.clickable(onClick = onClick).padding(start = 15.dp, end = 15.dp, top = 8.dp, bottom = 8.dp),verticalAlignment = Alignment.CenterVertically,) {when{leadingIcon != null -> {CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high,content = leadingIcon)Spacer(Modifier.padding(horizontal = 4.dp))Text(text = text,style = textStyle,)}trailingIcon != null -> {Text(text = text,style = textStyle,)Spacer(Modifier.padding(horizontal = 4.dp))CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high,content = trailingIcon)}else -> {Text(text = text,style = textStyle,)}}}}
}@Composable
fun FlowLayoutDemo() {Surface(modifier = Modifier.systemBarsPadding().fillMaxWidth(),elevation = 8.dp) {FlowRow(modifier = Modifier.padding(8.dp),crossAxisSpacing = 12.dp,mainAxisSpacing = 10.dp) {Tag(leadingIcon = {Icon(painterResource(id = R.drawable.wechat), null, tint = Color.White)},text = "WeChat",elevation = 6.dp,textStyle = TextStyle(Color.White),backgroundColor = Color(0xFF07C160)) { }Tag(leadingIcon = {Icon(painterResource(id = R.drawable.twitter), null, tint = Color.White)},text = "Twitter",elevation = 6.dp,textStyle = TextStyle(Color.White),backgroundColor = Color(0xFF1DA1F2)) { }Tag(leadingIcon = {Icon(painterResource(id = R.drawable.github), null, tint = Color.White)},text = "Github",elevation = 6.dp,textStyle = TextStyle(Color.White),backgroundColor = Color(0xFF181717)) { }Tag(leadingIcon = {Icon(painterResource(id = R.drawable.microsoftedge), null, tint = Color(0xFF0078D7))},text = "Edge",elevation = 6.dp) { }Tag(leadingIcon = {Icon(painterResource(id = R.drawable.microsoft), null, tint = Color(0xFF5E5E5E))},text = "Microsoft",elevation = 6.dp) { }}}
}
Insets
- implementation “com.google.accompanist:accompanis-inss-ui:<version>”
@Composable
fun InsetsDemo() {val systemUiController = rememberSystemUiController()val useDarkIcons = MaterialTheme.colors.isLightSideEffect {systemUiController.setSystemBarsColor(Color.Transparent, useDarkIcons)}Scaffold(topBar = {TopAppBar(title = {Text("TopAppBar")},backgroundColor = Color.Gray,contentPadding = WindowInsets.statusBars.asPaddingValues())},modifier = Modifier.fillMaxSize(),contentColor = Color.Black) { }
}
Lottie
- 添加依赖配置 implementation “com.airbnb.android:lottie-compose:$lottieVersion”
- 创建Lottie动画
- 创建两个状态用来描述动画的速度和开始暂停状态
var isPlaying by remember {mutableStateOf(true)}var speed by remember {mutableStateOf(1f)}
- 加载Lottie动画资源,这里用本地加载方式
- Lottie框架提供了加载res/raw,加载URL,加载手机目录下的静态资源,加载asset目录下的静态资源,加载json字符串的功能
val lottieComposition by rememberLottieComposition(spec = RawRes(R.raw.lottie),)
- 接下来创建Lottie动画状态
val lottieAnimationState by animateLottieCompositionAsState (composition = lottieComposition,iterations = LottieConstants.IterateForever,isPlaying = isPlaying,speed = speed,restartOnPlay = false)
- 最后设置动画资源句柄和动画状态
LottieAnimation(lottieComposition,lottieAnimationState,modifier = Modifier.size(400.dp))
Coil
- 添加依赖: implementation “io.coil-kt:coil-compose:$coil_version”
AsyncImage
@Preview
@Composable
fun AsyncImageDemo() {AsyncImage(model = ImageRequest.Builder(LocalContext.current).data(ImageUrl).crossfade(true).build(),//model = (ImageUrl),contentDescription = stringResource(R.string.description),placeholder = painterResource(id = R.drawable.place_holder),error = painterResource(id = R.drawable.error),onSuccess = {Log.d(TAG, "success")},onError = { error ->Log.d(TAG, "error")},onLoading = { loading ->Log.d(TAG, "loading")},modifier = Modifier.clip(CircleShape))
}
SubcomposeAsyncImage
@Preview
@Composable
fun SubcomposeAsyncImageDemo() {SubcomposeAsyncImage(model = "ImageUrl",loading = { CircularProgressIndicator() },contentDescription = "compose_museum")
}
@Preview
@Composable
fun SubcomposeAsyncImageDemo() {SubcomposeAsyncImage(model = "ImageUrl",contentDescription = "compose_museum") {if (painter.state is AsyncImagePainter.State.Loading || painter.state is AsyncImagePainter.State.Error) {CircularProgressIndicator()} else {SubcomposeAsyncImageContent()}}
}
SubcomposeAsyncImage(model = ImageRequest.Builder(LocalContext.current).data(ImageUrl.testUrl1).size(1920,1080),.build(),contentDescription = null,) {val state = painter.statewhen(state) {is AsyncImagePainter.State.Loading -> CircularProgressIndicator()is AsyncImagePainter.State.Error -> Text("${state.result.throwable}")is AsyncImagePainter.State.Success -> SubcomposeAsyncImageContent()is AsyncImagePainter.State.Empty -> Text("Empty")}}
AsyncImagePainter
- 这个组件是底层API,使用时会出现很多不可预期的行为,所以建议用前面两个
- 如果项目要求不能用AsyncImage则用这个
- 不能用Success状态来判断,否则图片不能加载成功
val painter = rememberAsyncImagePainter(model = ImageRequest.Builder(LocalContext.current).data("https://pic-go-bed.oss-cn-beijing.aliyuncs.com/img/20220316151929.png").build()
)
if (painter.state is AsyncImagePainter.State.Loading) {CircularProgressIndicator()
}
Image(painter = painter,contentDescription = stringResource(R.string.description)
)
《Jetpack Compose从入门到实战》第一章 全新的 Android UI 框架
《Jetpack Compose从入门到实战》 第二章 了解常用UI组件
《Jetpack Compose从入门到实战》第三章 定制 UI 视图
《Jetpack Compose从入门到实战》第八章 Compose页面 导航
《Jetpack Compose从入门到实战》第九章 Accompanist 与第三方组件库
相关文章:
《Jetpack Compose从入门到实战》第九章 Accompanist 与第三方组件库
目录 AccompanistSystemUiControllerPagerSwipeRefreshFlow LayoutInsets LottieCoilAsyncImageSubcomposeAsyncImageAsyncImagePainter Accompanist 最新可用版本accompanist官方文档 SystemUiController 依赖:implementation “com.google.accompanist:accompa…...
Centos7 docker 容器内root身份应用自启动 /usr/sbin/init 问题
Centos7 docker 容器内root身份应用自启动 & /usr/sbin/init 问题 环境:我在一个 docker 容器内手动安装了 mysql、nginx、autotestsystem(自己的服务); mysql 和 nginx 都做了服务脚本:mysqld.service、nginx.se…...
STL学习笔记之容器
首先我们要学习的是容器 第一个是容器的初始化(构造方式)有三种方式 分别是 第一种 int arr[]{1,2,3} vector<int> v1(arr,arr3) 即容器存放的种类和从另外一个数组去拷贝一段数据。 第二种 vector<int> v2(3,10); 第一个3是指存放…...
Java基础---第十二篇
系列文章目录 文章目录 系列文章目录一、获取一个类Class对象的方式有哪些?二、ArrayList 和 LinkedList 的区别有哪些?三、用过 ArrayList 吗?说一下它有什么特点?一、获取一个类Class对象的方式有哪些? 搞清楚类对象和实例对象,但都是对象。 第一种:通过类对象的 get…...
Acwing 841. 字符串哈希
Acwing 841. 字符串哈希 题目描述思路讲解代码展示 题目描述 思路讲解 代码展示 #include <iostream> #include <algorithm>using namespace std;typedef unsigned long long ULL;const int N 100010, P 131; // P 131 或者13331(经验值)int n, m; char str[N]…...
NEON优化:性能优化经验总结
NEON优化:性能优化经验总结 1. 什么是 NEONArm Adv SIMD 历史 2. 寄存器3. NEON 命名方式4. 优化技巧5. 优化 NEON 代码(Armv7-A内容,但区别不大)5.1 优化 NEON 汇编代码5.1.1 Cortex-A 处理器之间的 NEON 管道差异5.1.2 内存访问优化 Reference: NEON优…...
C++ 并发编程实战 第九章
目录 9.1 线程池 9.1.1 最简易可行的线程池 9.1.2 等待提交给线程池的任务完成运行 9.1.3等待其他任务完成的任务 9.1.4 避免任务队列上的争夺 9.1.5 任务窃取 9.2 中断线程 9.2.1 发起一个线程,以及把他中断 9.2.2 检测线程是否被中断 9.2.3 中断条件变…...
【Java】super 关键字用法
目录 this与super区别 1.访问成员变量-示例代码 继承中构造方法的访问特点 2.访问构造方法-示例代码: 继承中成员方法访问特点 3.访问成员方法-示例代码: super 关键字的用法和 this 关键字相似 this : 代表本类对象的引用super : 代表父类存储空间…...
前端笔试题总结,带答案和解析
1. 执行以下程序,输出结果为() var x 10; var y 20; var z x < y ? x:y; console.log(xx;yy;zz);A x11;y21;z11 B x11;y20;z10 C x11;y21;z10 D x11;y20;z11 初始化x的值为10,y的值为20,x < y返回结果为tru…...
Omniverse Machinima
Omniverse Machinima App | NVIDIA Omniverse Machinima 是 NVIDIA 推出的一款实时动画创作工具,可用于在虚拟世界中制作和操纵角色及其环境。该工具使用 Universal Scene Description (USD) 作为其通用场景描述格式,可与多种 3D 建模、动画和渲染应用程…...
【测试人生】游戏业务测试落地精准测试专项的一些思路
精准测试在互联网领域有广泛的应用。以变更为出发点,通过对变更内容进行分析,可以确定单次变更具体涉及到哪些模块和功能点,以及是否存在夹带风险,从而从QA的视角,可以知道哪些功能模块需要做测试,以及哪些…...
Redis 数据类型底层原理
String 内部编码有三种:int、embstr、raw int:如果一个字符串对象保存的是整数值,并且这个整数值可以用 long类型来表示(不超过 long 的表示范围,如果超过了 long 的表示范围,那么按照存储字符串的编码来存储…...
EasyEdge 智能边缘控制台通过sdk发布应用
离线部署SDK生成 模型部署完成后会出现下载SDK的按钮,点击按钮下载SDK并保存好SDK。 进入EasyDL官网的技术文档 安装智能边缘控制台 跟着教程,完成安装:点此链接 树莓派4b是Linux arm64的架构,点击对应的链接进行下载。 下载完成…...
centos软件设置开机启动的方式
以下以redis作为案例: 开机启动方式一 [Unit] Descriptionredis-server Afternetwork.target [Service] Typeforking # 这里需要修改自己的配置文件 ExecStart/usr/local/bin/redis-server /etc/redis/redis.conf PrivateTmptrue [Install] WantedBymulti-user.tar…...
二叉树和堆
二叉树不存在度大于2的结点(每个根最多只有两个子结点)二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树 两个特殊的二叉树——(满二叉树,完全二叉树) 满二叉树——每个根结点都有左右…...
洛谷P5732 【深基5.习7】杨辉三角题解
目录 题目【深基5.习7】杨辉三角题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1传送门 代码解释亲测 题目 【深基5.习7】杨辉三角 题目描述 给出 n ( n ≤ 20 ) n(n\le20) n(n≤20),输出杨辉三角的前 n n n 行。 如果你不知道什么是杨辉三角…...
Docker 精简安装 Nacos 2.2.1 单机版本
准备工作: 1)已安装docker 2)数据库准备,演示使用MySql5.7版本 1、拉取 [rootTseng-HW ~]# docker pull nacos/nacos-server:v2.2.1 v2.2.1: Pulling from nacos/nacos-server 2d473b07cdd5: Already exists 77c5a601c050: Pul…...
IntelliJ IDEA配置Cplex12.6.3详细步骤
Cplex12.6.3版IntelliJ IDEA配置详细步骤 一、Cplex12.6.3版下载地址二、Cplex安装步骤三、IDEA配置CPLEX3.1 添加CPLEX安装目录的cplex.jar包到项目文件中3.2 将CPLEX的x64_win64文件夹添加到IDEA的VM options中 四、检查IDEA中Cplex是否安装成功卸载Cplex 一、Cplex12.6.3版下…...
2023 年最佳多 GPU 深度学习系统指南
动动发财的小手,点个赞吧! 本文[1]提供了有关如何构建用于深度学习的多 GPU 系统的指南,并希望为您节省一些研究时间和实验时间。 1. GPU 让我们从有趣(且昂贵)的部分开始! 购买 GPU 时的主要考虑因素是&am…...
Kotlin异常处理runCatching,getOrNull,onFailure,onSuccess(1)
Kotlin异常处理runCatching,getOrNull,onFailure,onSuccess(1) fun main(args: Array<String>) {var s1 runCatching {1 / 1}.getOrNull()println(s1) //s11,打印1println("-")var s2 ru…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
