Flutter 状态管理框架Get

状态管理框架 Get的使用

目录

状态管理框架 Get的使用

GetMaterialApp

路由的注册

路由的跳转

middlewares的使用

组件使用 defaultDialog bottomSheet snackbar

状态刷新有很多种方式

ValueBuilder

Obx 基础使用

是时候引入GetxController, 也是Get里面的常用的

GetX

优化控制器的使用 put

优化控制器的使用find

优化控制器的使用 懒加载lazyput

GetView

接下来GetConnect就是网络请求的使用

GetConnect

GetConnectStatemixin

GetConnectDio

切换主题

 多语言切换

常使用的Getx自带的API


不得不说,GetX使你的代码量减少了许多, 一个obs. 一个obx. 搞定你的数据监听刷新, 给你不一样的简洁。

GetMaterialApp

 return GetMaterialApp(title: 'Flutter Demo',getPages: MyRoutes.routes, //注册路由// initialRoute: MyHomePage.route,  //该属性, 设置有有问题, 在自定义转场动画的时候, 跳转的时候右边会黑屏 unknownRoute: MyRoutes.notFoundPage, //默认404的路由地址theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),useMaterial3: true,),home: const MyHomePage(),);

// initialRoute: MyHomePage.route,//该属性,在自定义转场动画的时候, 跳转的时候右边会黑屏

路由的注册

class MyRoutes {static List<GetPage> routes = [//正常注册GetPage(name: MySettingPage.route, page: () => const MySettingPage()),GetPage(name: MyHomePage.route, page: () => const MyHomePage(), children: [//children 这里可以注册子页面, 一级, 二级, 三级, 页面都可以GetPage(name: MyProductPage.route, page: () => const MyProductPage(), children: [GetPage(name: MyProductDetailPage.route, page: () => const MyProductDetailPage()),GetPage(name: MySettingPage.route, page: () => const MySettingPage()),GetPage(name: MySettingPage.routeID, page: () => const MySettingPage()),]), ]),];
}

路由的跳转

//使用路由String跳转, 这样写, 注册的时候 /MyProductPage 必须要在 MyHomePage 的children: [ 里面] 如上展示
Get.toNamed("/MyHomePage/MyProductPage");
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage");
//直接跳转对应的页面
Get.to(const MyProductPage());
//使用路由跳转并传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");//返回上一个页面, 并传值, 接收参数的 var result = await Get.toNamed("/MyHomePage/MyProductPage/MySettingPage?id==345");
Get.back(result: {"success": true}),//特殊路由传值 需要定义路由: "/MySettingPage/:id";, 才能使用下方跳转传值
Get.toNamed("/MyHomePage/MyProductPage/MySettingPage/8910");//跳转后, 消除上页面的堆栈
Get.off(const MyProductDetailPage());
Get.offNamed(MyProductDetailPage.route);//跳转后, 消除所有的堆栈
Get.offAll(const MyProductDetailPage());
Get.offAllNamed(MyProductDetailPage.route);

middlewares的使用

一个类似拦截器的功能,可以传入多个,需要自定研究下优先级。

 GetPage(name: MyMinePage.route, page: () => const MyMinePage(), middlewares: [        MyRouteAuthMiddleware()]),
class MyRouteAuthMiddleware extends GetMiddleware {@overrideRouteSettings? redirect(String? route) {// 加入 AuthService 这里可以判断下用户是否登录,  如果true return super.的方法//否则去都登陆页面Future.delayed(const Duration(seconds: 1), () => Get.snackbar("提示", "请先登录APP"));return const RouteSettings(name: MyLoginPage.route);}
}

组件使用 defaultDialog bottomSheet snackbar

Get.snackbar
Get.bottomSheet
Get.defaultDialog
自行调用, 没有难度

状态刷新有很多种方式

ValueBuilder

爱了, 爱了, 直接导入头文件, 只刷新该作用域的.

ValueBuilder<List<String>?>(initialValue: const ["A", "B", "C"],builder: (value, updateFn) {return Column(children: [Text("List -> $value"),ElevatedButton(onPressed: () {List<String> newList = [...value!]; // 使用扩展运算符来创建列表的浅拷贝newList.add("${newList.length}");// newList.removeAt(1); // 移除索引为1的元素newList.shuffle();//元素随机updateFn(newList); // 使用新列表更新状态},child: const Text('ValueBuilder -> add'),)]);}),   

Obx 基础使用

比如在Widget, 声明一个属性, 使用的时候使用Obx(()=>{}) 包裹一下,然后在其他地方点击,修改count值就OK了, 因为增加了obs, 就成了, count.value++ 来修改值

//Int
RxInt count = 0.obs;
var count1 = 0.obs;
Obx(() => Column(children: [Text("int -> $count"),Text("int -> $count1"),
])//刷新
updateCount() {count.value++;count1.value++;}
在监听list. map. 枚举 的时候要注意, 一定要从新赋值才会刷新//刷新枚举void updateViewState(ViewState newValue) {  viewState.value = newValue; // 更新枚举值  }  
//刷新listupdateList() {list[0] = "BMW";list1[0] = "BMW"; 
}
//刷新Map
updateMap() {map["Audi"] = "BMW";map1["Audi"] = "BMW";
}
//刷新List中的Model
updateModels() {models[0] = GetXModels(name: "BMW");models1[0] = GetXModels(name: "BMW");}
//刷新Model
updateModel() {model.value = GetXModels(name: "BMW");model1.value = GetXModels(name: "BMW");
}
使用的地方一地个要加上Obx()

是时候引入GetxController, 也是Get里面的常用的

GetX<MyGetxController>

可以包裹引用Controller值的地方, 进行局部刷新, 还有声明周期的回调 推荐 ☆☆☆

//要创建控制器Controller
class MyPutController extends GetxController {}
//在widget的build方法中
final MyGetxController getxController = MyGetxController();
//使用例子ListTile(title: const Text("update list"),subtitle: GetX<MyGetxController>(init: getxController,initState: (_) {},builder: (_) {return Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Text('我要买${getxController.list.toString()}'),Text('我要买${getxController.list1.toString()}'),],);},),onTap: () {//list, 要修改list里面的元素, 才会去更新UIgetxController.updateList();},
),

优化控制器的使用 put

//在Widget的build方法里面我们注册一个controller 推荐 ☆☆☆

final MyPutController countController = Get.put<MyPutController>(MyPutController());
在使用过程中使用Obx(())包裹即可, 
ListTile(title: const Text("update Count"),subtitle: Obx(() => Column(crossAxisAlignment: CrossAxisAlignment.start,children: [Text('count = ${countController.count.value}'),Text('count = ${countController.count1.value}'),],)),onTap: () {countController.updateCount();},
),

优化控制器的使用find

//在put过MyProductController()的Widget的build方法里面即二级页面, 三级页面....子页面

final MyProductController productController = Get.find<MyProductController>();
在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
ListTile(title: Text("传值 Get.arguments; = ${details.toString()} parameters == ${parameters.toString()}"),subtitle: Obx(() => Text(productController.myProductList.length.toString())),onTap: () {productController.addProduct(MyProduct(name: 'iPhone 16', description: 'APPle 设备', price: 8199));productController.addCount();}),

优化控制器的使用 懒加载lazyput

这里就要引出Binding的使用了

class MyGetLazyPutBinding implements Bindings {@overridevoid dependencies() {Get.lazyPut(() => MyGetLazyPutController());}
}
// MyGetLazyPutController 上面说的controller. 必须创建, 切继承GetxController
//注册路由的时候
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在Widget页面的时候就,不用调用Getx.put的方法, 会自动Put
//直接去find找到该Controller
final MyGetLazyPutController getLazyPutController = Get.find<MyGetLazyPutController>();
//在使用过程中使用Obx(())包裹即可, 也可以使用GetX<MyProductController>build 也可以
//建议自己撸一遍代码, 会印象更加深刻

GetView

这个更加简单, 省略put, find 的步骤, 不过还是要一个Controller的

//创建Widget的时候,需要增加一个泛型
class MyGetViewPage extends GetView<MyGetLazyPutController> 
//上面的路由注册, 还是要注册一个Controller
GetPage(name: MyLazyPutPage.route, page: () => const MyLazyPutPage(),binding: MyGetLazyPutBinding()),
//在widget中, 就自带了controller的变量 ,可以翻看源码中
abstract class GetView<T> extends StatelessWidget {const GetView({Key? key}) : super(key: key);final String? tag = null;T get controller => GetInstance().find<T>(tag: tag)!;@overrideWidget build(BuildContext context);
}
//就会知道, 内部帮忙find_Controller

 

接下来GetConnect就是网络请求的使用

GetConnect

这里跟官网的不太一样, 官网我感觉有点麻烦, 创建那么多的文件, 其实就是请求层, 数据层, 页面布局划分就好了

//1.我们先继承GetConnect 设置下你的请求相关配置
//如: baseUrl headers 以及其他设置
class MyBaseHttp extends GetConnect {@overridevoid onInit() {httpClient.baseUrl = "xxxxxx";// 请求拦截httpClient.addRequestModifier<void>((request) {Map<String, String> headerMap = {"os-type": GetPlatform.isIOS ? "ios" : "android","timestamp": DateTime.now().microsecondsSinceEpoch.toString(),};request.headers.addAll(headerMap);return request;});// 响应拦截httpClient.addResponseModifier((request, response) {return response;});}
}
//2.然后继承 MyBaseHttp, 创建你的serviceController
class MyServiceController extends MyBaseHttp {//获取数据, 自定义数据Future<MyGetConnectModel> getContent() async {Response response = await get('/route/external_link.json'); //这里去请求的, 有post. put. 自己看下APIif (response.statusCode == 200) {debugPrint(response.bodyString);return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');} else {debugPrint(response.bodyString);return MyGetConnectModel(userId: 1, id: 2, title: 'title', body: 'body');}}
}
//3.创建页面的controller
class MyGetConnectController extends GetxController {// 获取实例final MyServiceController serviceController = Get.find<MyServiceController>();// modellate MyGetConnectModel getConnectModel;//监听状态, 这里是一个枚举, 这里你也可以监听其他的,比如model的变化Rx<ViewState> viewState = ViewState.normal.obs;getData() async {updateViewState(ViewState.loading);getConnectModel = await serviceController.getContent();updateViewState(ViewState.normal);}void updateViewState(ViewState newValue) {  viewState.value = newValue; // 更新枚举值  }  
}
//4.注册
class MyGetContentBuinding extends Bindings {@overridevoid dependencies() {Get.lazyPut(() => MyGetConnectService());Get.lazyPut(() => MyGetConnectController());}
}
//路由binding
GetPage(name: MyGetContentPage.route, page: () => const MyGetContentPage(),binding: MyGetContentBuinding()),
//5.创建Widget,布局
方式1: class MyGetContentPage extends GetView<MyGetConnectController> {}
方式2: class MyHomePage extends StatelessWidget{
final controller = Get.find<MyGetConnectController>();}
//数据刷新
GetX<MyGetConnectController>(initState: (state) {controller.getData();
}, init: controller,
builder: ((_) {debugPrint("controller.viewState.value: ${controller.viewState.value}")switch (controller.viewState.value) {case ViewState.loading:return const Center(child: CircularProgressIndicator(),);default:return _buildListView(controller.getConnectModel);}
})),
或者
Obx(()=>)
更新值的时候, 需要 controller哟

GetConnectStatemixin

//跟上面基本一致, 这里说下不同点
//创建页面控制器的时候需要增加监听的类型数据
class MyGetConnectStateMixinController extends GetxController with StateMixin<List<MyGetConnectModel>> { }
//List<MyGetConnectModel>就是我要监听的数据类型
//创建页面方式
class MyGetConnectStateMixinPage extends GetView<MyGetConnectStateMixinController> {}
也可以使用find的方式找到该 MyGetConnectStateMixinController
然后通过controller.obx 进行监听处理, 有多种状态,final bool isLoading;final bool isError;final bool isSuccess;final bool isEmpty;final bool isLoadingMore;final String? errorMessage;
// 是在页面的MyGetConnectStateMixinController 控制器里面进行定义的.相关代码@overridevoid onInit() {fetchList(); //获取数据super.onInit();}// 拉取数据列表Future<void> fetchList() async {// 获取数据, 也可以处理好处理传过来,final Response response = await connectStateMixinService.getContent(); //通过find找到该控制器 connectStateMixinService, 是继承上面的MyBaseHttp的请求类控制器// 判断请求是否有错误if (response.hasError) {// 改变数据,传入错误状态,在ui中会处理这些错误//返回状态的定义// change(null, status: RxStatus.error(response.statusText));// change(null, status: RxStatus.empty());} else {// 成功,自定义数据,改变状态为成功List<MyGetConnectModel> dataList = [];for (var i = 0; i < 10; i++) {MyGetConnectModel getConnectModel = MyGetConnectModel(userId: 12312, id: 231231, title: "title", body: "body");dataList.add(getConnectModel);}change(dataList, status: RxStatus.success());}//页面布局Widget, 根据不同的状态, 可以写不同的结果
controller.obx((state) {return ListView.separated(itemCount: state!.length,itemBuilder: (context, index) {final MyGetConnectModel getConnectModel = state[index];return ListTile(onTap: () {},title: Text(getConnectModel.title),trailing: Text("\$${getConnectModel.body}"),);},separatorBuilder: (BuildContext context, int index) {return const Divider();},);},onError: (error) {return Center(child: Text(error.toString()),);},onLoading: const SizedBox(width: double.infinity,height: double.infinity,child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [CircularProgressIndicator(),SizedBox(height: 10),Text("疯狂加载中...",style: TextStyle(color: Colors.blue, fontSize: 16),),],),),onEmpty: const Center(child: Text("没有数据"),),
));

GetConnectDio

需要自行导入Dio的框架
把上面的 GetConnect 部分中的请求部分, 更换为Dio请求, 然后使用Rx定义,监听的变量。

//在Widget中, 通过Obx, 或者GetX<MyGetConnectController> 去刷新数据即可
class MyGetConnectDioPage extends GetView<MyGetConnectDioController> 

切换主题

return Scaffold(appBar: AppBar(title: const Text("切换主题"),),body: Center(child: ElevatedButton(onPressed: () {Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());},child: Text("是否黑色主题 -> ${Get.isDarkMode}"),)),);

 多语言切换

class MyTranslationService extends Translations {static Locale? get locale => Get.deviceLocale;static const fallbackLocale = Locale('en', 'US');@overrideMap<String, Map<String, String>> get keys => {'en_US': enUS,  'zh_Hans': zhHans,'zh_HK': zhHK,};
}
//enUS  zhHans zhHK 要去创建文件 如: 
const Map<String, String> zhHans = {'title': '这是标题','login': '登录用户 @name,邮箱账号 @email',
};GetMaterialApp下:locale: MyTranslationService.locale,fallbackLocale: MyTranslationService.fallbackLocale,translations: MyTranslationService(),
// 使用
Text("title -> ${'title'.tr}"),
Text("login -> ${'login'.trParams({'name': 'xxx', 'email': 'xxx@gmail.com'})}"),//使用tr. trParams 来处理多语言
//设置多语音
var locale = const Locale('zh', 'HK');
Get.updateLocale(locale);

常使用的Getx自带的API

Get.arguments//给出以前的路由名称
Get.previousRoute// 给出要访问的原始路由,例如,rawRoute.isFirst()
Get.rawRoute// 允许从GetObserver访问Rounting API。
Get.routing// 检查 snackbar 是否打开
Get.isSnackbarOpen// 检查 dialog 是否打开
Get.isDialogOpen// 检查 bottomsheet 是否打开
Get.isBottomSheetOpen// 删除一个路由。
Get.removeRoute()//反复返回,直到表达式返回真。
Get.until()// 转到下一条路由,并删除所有之前的路由,直到表达式返回true。
Get.offUntil()// 转到下一个命名的路由,并删除所有之前的路由,直到表达式返回true。
Get.offNamedUntil()//检查应用程序在哪个平台上运行。
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia//检查设备类型
GetPlatform.isMobile
GetPlatform.isDesktop
//所有平台都是独立支持web的!
//你可以知道你是否在浏览器内运行。
//在Windows、iOS、OSX、Android等系统上。
GetPlatform.isWeb// 相当于.MediaQuery.of(context).size.height,
//但不可改变。
Get.height
Get.width// 提供当前上下文。
Get.context// 在你的代码中的任何地方,在前台提供 snackbar/dialog/bottomsheet 的上下文。
Get.contextOverlay// 注意:以下方法是对上下文的扩展。
// 因为在你的UI的任何地方都可以访问上下文,你可以在UI代码的任何地方使用它。// 如果你需要一个可改变的高度/宽度(如桌面或浏览器窗口可以缩放),你将需要使用上下文。
context.width
context.height// 让您可以定义一半的页面、三分之一的页面等。
// 对响应式应用很有用。
// 参数: dividedBy (double) 可选 - 默认值:1
// 参数: reducedBy (double) 可选 - 默认值:0。
context.heightTransformer()
context.widthTransformer()/// 类似于 MediaQuery.of(context).size。
context.mediaQuerySize()/// 类似于 MediaQuery.of(context).padding。
context.mediaQueryPadding()/// 类似于 MediaQuery.of(context).viewPadding。
context.mediaQueryViewPadding()/// 类似于 MediaQuery.of(context).viewInsets。
context.mediaQueryViewInsets()/// 类似于 MediaQuery.of(context).orientation;
context.orientation()///检查设备是否处于横向模式
context.isLandscape()///检查设备是否处于纵向模式。
context.isPortrait()///类似于MediaQuery.of(context).devicePixelRatio。
context.devicePixelRatio()///类似于MediaQuery.of(context).textScaleFactor。
context.textScaleFactor()///查询设备最短边。
context.mediaQueryShortestSide()///如果宽度大于800,则为真。
context.showNavbar()///如果最短边小于600p,则为真。
context.isPhone()///如果最短边大于600p,则为真。
context.isSmallTablet()///如果最短边大于720p,则为真。
context.isLargeTablet()///如果当前设备是平板电脑,则为真
context.isTablet()///根据页面大小返回一个值<T>。
///可以给值为:
///watch:如果最短边小于300
///mobile:如果最短边小于600
///tablet:如果最短边(shortestSide)小于1200
///desktop:如果宽度大于1200
context.responsiveValue<T>()

状态管理框架 Provider 和 Get 的Likes, 各有千秋

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/456525.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV与AI深度学习 | 实战 | OpenCV中更稳更快的找圆方法--EdgeDrawing使用演示(详细步骤 + 代码)

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;实战 | OpenCV中更稳更快的找圆方法--EdgeDrawing使用演示&#xff08;详细步骤 代码&#xff09; 导 读 本文主要介绍如何在OpenCV中使用E…

pycharm 中提示ModuleNotFoundError: No module named ‘distutils‘

在Pycharm 中的命令行中输入 pip install setuptools&#xff0c;即可解决

K8S测试pod内存和CPU资源不足

只设置requests参数 mysql主从pod启动后监控 读压测之后 同时设置limits和requests&#xff0c;只调低内存值 监控 压力测试 同时设置limits和requests&#xff0c;只调低CPU值 初始状态 开始压测 结论 对于CPU&#xff0c;如果pod中服务使用CPU超过设置的limits&…

(小白教程)MPV.NET 播放器安装和添加插件脚本Bilibili弹幕

MPV.NET安装和添加插件脚本 MPV跨平台播放器&#xff1a;该播放器基于流行的mpv媒体播放器。mpv.net 设计为与 mpv 兼容&#xff0c;几乎所有 mpv 功能都可用&#xff0c;这意味着官方mpv 手册适用于 mpv.net&#xff0c;差异记录在mpv.net 手册中。 主要差异是mpv.net为MPV添加…

Linux 字符设备驱动 之 无法归类的《杂项设备驱动》

学习目标&#xff1a; 了解 杂项设备驱动 和普通字符设备的异同&#xff0c;及杂项设备驱动程序的写法 学习内容&#xff1a; 一、杂项设备驱动的特别之处 杂项设备&#xff08;Miscellaneous Devices&#xff09;是一种通用的设备类型&#xff0c;用于表示那些不适合其他设备…

基于springboot企业微信SCRM管理系统源码带本地搭建教程

系统是前后端分离的架构&#xff0c;前端使用Vue2&#xff0c;后端使用SpringBoot2。 技术框架&#xff1a;SpringBoot2.0.0 Mybatis1.3.2 Shiro swagger-ui jpa lombok Vue2 Mysql5.7 运行环境&#xff1a;jdk8 IntelliJ IDEA maven 宝塔面板 系统与功能介绍 基…

ubuntu 安装haproxy

####安装##### sudo apt update sudo apt install haproxy sudo haproxy -v sudo systemctl status haproxy sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg-org####配置站点##### nano /etc/haproxy/haproxy.cfgfrontend www-httpbind *:5001mode httpdefault_ba…

SAP RFC 的几种类型

SRFC: ARFC: ARFC: TRFC: QRFC: QRFC-QIN Scheduler: *RFC: tables: RSTRFCTA RFC-TEST: Outbound Queue: STOP/RESTART (after STOP) RSTRFCTB RFC TEST: Outbound Queue: Get/Execute LUWs from Local/Remote Syst RSTRFCTC RFC-TEST: Inbound Que…

当有违法数据时,浏览器不解析,返回了undefined,导致数据不解析

现象&#xff1a;页面上没有看到数据 排查&#xff1a;断点到线上的源码里&#xff1a;1、协议回调确实没有拿到数据是个undefined 2、network里看服务确实响应了数据 3、控制台没有任何报错。 心情&#xff1a;莫名其妙的现象 我本地有json格式化工具&#xff0c;copy进去后&…

【论文阅读】Tabbed Out: Subverting the Android Custom Tab Security Model

论文链接&#xff1a;Tabbed Out: Subverting the Android Custom Tab Security Model | IEEE Conference Publication | IEEE Xplore 总览 “Tabbed Out: Subverting the Android Custom Tab Security Model” 由 Philipp Beer 等人撰写&#xff0c;发表于 2024 年 IEEE Symp…

linux入门之必掌握知识点

#1024程序员节&#xff5c;征文# Linux基础 top命令详解 top命令是用来查看进程系统资源使用情况的工具&#xff0c;它可以动态的现实。 top命令执行后&#xff0c;按大写M可以按内存使用情况进行排序&#xff0c;大写P可以按CPU使用情况进行排序&#xff0c;大写H可以显示线…

vue-vant框架引入

一、工具说明 vscode编辑器 二、安装 使用包管理器安装 npm install vant -S 查看是否安装成功&#xff1a;查看项目下的package.json文件中的依赖是否有vant: 三、导入 1、按需导入 按照node_mouduls目录下的vant文件夹的lib目录中的路径导入你要的组件 2、整体导入 在…

WPS电信定制版 v12.8.2.18205 自带 VBA\无广告

下载&#xff08;哪个方便就下哪个&#xff09;&#xff1a;【1】https://pan.quark.cn/s/5373bf6cdcf5【2】链接: https://pan.baidu.com/s/1Vn2Bbhp8px-BBtlalkIIYg?pwdjgry 提取码: jgry 软件介绍&#xff1a; 1、VBA 组件更换为电信定制版&#xff0c;签名日期&#xf…

【进阶OpenCV】 (19)-- Dlib库 --人脸表情识别

文章目录 表情识别一、原理二、代码实现1. 摄像头前预处理2. 计算嘴唇变化3. 绘制嘴唇轮廓4. 显示结果5. 完整代码展示 总结 表情识别 目标&#xff1a;识别人物的喜悦状态。 一、原理 我们在对一张人脸图片进行关键点定位后&#xff0c;得到每个关键点的位置&#xff1a; 比…

疯狂变现!5分钟教你如何高效率制作AI商业海报!

在这个快节奏的时代&#xff0c;效率就是生命力。无论你是创业者、还是设计师&#xff0c;制作吸引人的详情海报都是日常工作中不可或缺的一环。传统的设计从构思到定稿&#xff0c;往往需要数小时甚至数天的时间。但现在&#xff0c;有了AI技术的加持——仅5分钟&#xff0c;你…

红帽Linux认证与其他认证相比优势在哪?

在各种各样的 IT 认证里头&#xff0c;红帽 Linux 认证凭借自身独特的地方和长处崭露头角。那红帽 Linux 认证跟其他认证相比&#xff0c;长处到底在啥地方呢&#xff1f; 接下来就给大伙简单说道说道。 首先&#xff0c;红帽 Linux 认证特别注重实践。它主要考查考生实际操作…

AI智能监测系统:全面赋能燃气安全管理的智能化转型方案

燃气安全智能化的需求&#xff1a; 随着燃气供应系统的广泛应用&#xff0c;燃气安全成为城市管理和企业运营中的重要环节。由于燃气泄漏、操作不规范等事故会造成巨大的人员伤亡和财产损失&#xff0c;传统的安全管理方法往往效率低下&#xff0c;依赖人工巡检&#xff0c;无…

JavaEE----多线程(二)

文章目录 1.进程的状态2.线程的安全引入3.线程安全的问题产生原因4.synchronized关键字的引入4.1修饰代码块4.2修饰实例方法4.3修饰静态方法4.4对象头介绍4.5死锁-可重入的特性 5.关于死锁的分析总结5.1死锁的分析5.2死锁成因的必要条件5.3死锁的解决方案 1.进程的状态 public…

深入了解 kotlinx-datetime:配置与使用指南

深入了解 kotlinx-datetime&#xff1a;配置与使用指南 在Kotlin多平台开发中&#xff0c;处理日期和时间是常见的需求。kotlinx-datetime库提供了强大且简洁的API来帮助开发者应对这一挑战。本文将详细介绍如何配置kotlinx-datetime库&#xff0c;并通过生动的示例演示其核心…

java中Set,Map,List集合的比较(不包含增删改查函数方法)

目录 1. 集合的简介2. List3. Set4. Map5. 比较5.1 结构特点5.2 实现类5.3 区别 6. 其他问题6.1 集合与数组的区别6.2 哪些集合类是线程安全的 7. 参考链接 1. 集合的简介 所有的集合类和集合接口都在java.util包下。 在内存中申请一块空间用来存储数据&#xff0c;在Java中集…