flutter学习-day12-可滚动组件和监听
📚 目录
- 简介
- 可滚动组件
- SingleChildScrollView
- ListView
- separated分割线
- 无限加载列表
- 带标题列表
- 滚动监听和控制
- ScrollController滚动监听
- NotificationListener滚动监听
- AnimatedList动画列表
- 滚动网格布局GridView
- 横轴子元素为固定数量
- 横轴子元素为固定最大长度
- 动态创建
- PageView页面切换和缓存
- PageState缓存
- TabBarView
- CustomScrollView和Slivers
- 常用的Slivers
本文学习和引用自《Flutter实战·第二版》:作者:杜文
1. 简介
通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大;如果要一次性将子组件全部构建出将会非常昂贵!为此,Flutter中提出一个Sliver(中文为“薄片”的意思)概念,Sliver 可以包含一个或多个子组件。Sliver 的主要作用是配合:加载子组件并确定每一个子组件的布局和绘制信息,如果 Sliver 可以包含多个子组件时,通常会实现按需加载模型。只有当 Sliver 出现在视口中时才会去构建它,这种模型也称为“基于Sliver的列表按需加载模型”。可滚动组件中有很多都支持基于Sliver的按需加载模型,如ListView、GridView,但是也有不支持该模型的,如SingleChildScrollView。
Flutter 中的可滚动组件主要由三个角色组成:
- Scrollable :用于处理滑动手势,确定滑动偏移,滑动偏移变化时构建 Viewport。
方法 | 值 | 描述 |
---|---|---|
axisDirection | - | 滚动方向 |
physics | ClampingScrollPhysics、BouncingScrollPhysics | 决定可滚动组件如何响应用户操作 |
controller | - | 控制滚动位置和监听滚动事件 |
viewportBuilder | - | 当用户滑动时,Scrollable 会调用此回调构建新的 Viewport,同时传递一个 ViewportOffset 类型的 offset 参数,该参数描述 Viewport 应该显示那一部分内容 |
- Viewport:显示的视窗,即列表的可视区域。
方法 | 值 | 描述 |
---|---|---|
axisDirection | - | 滚动方向 |
offset | - | 用户的滚动偏移 |
cacheExtent | - | 预渲染区域 |
CacheExtentStyle | - | 用于配合解释cacheExtent的含义,也可以为主轴长度的乘数 |
slivers | - | 需要显示的 Sliver 列表 |
- Sliver:视窗里显示的元素,对子组件进行构建和布局。
如果要给可滚动组件添加滚动条,只需将Scrollbar作为可滚动组件的任意一个父级组件即可
2. 可滚动组件
本节讲一下SingleChildScrollView和ListView。
2-1 SingleChildScrollView
SingleChildScrollView类似于Android中的ScrollView,它只能接收一个子组件。通常SingleChildScrollView只应在期望的内容不会超过屏幕太多时使用,因为不支持基于 Sliver 的延迟加载模型,可能性能差。
方法 | 值 | 描述 |
---|---|---|
scrollDirection | - | 滚动方向 |
reverse | - | |
padding | - | 内边距 |
primary | - | 是否使用 widget 树中默认的PrimaryScrollControlle |
physics | - | 决定可滚动组件如何响应用户操作 |
controller | - | 控制器 |
child | - | 子组件 |
竖向排列的字母表例子:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {String alphabet = 'ABCDEFGHIJKMLNOPKRSTUVWSYZ';Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: Scrollbar(child: SingleChildScrollView(padding: const EdgeInsets.all(16.0),child: Center(child: Column(children: alphabet.split('').map((e) => Text(e,textScaleFactor: 2.0,)).toList(),)),),));}
}
2-2. ListView
ListView是最常用的可滚动组件之一,内部组合了 Scrollable、Viewport 和 Sliver。它可以沿一个方向线性排布所有子组件,并且它也支持列表项懒加载(在需要时才会创建)。它有一个children参数,它接受一个Widget列表。这种方式适合只有少量的子组件数量已知且比较少的情况,反之则应该使用ListView.builder 按需动态构建列表项。
方法 | 值 | 描述 |
---|---|---|
itemExtent | - | 子组件长度,如果指定了该值,就不用动态计算listview高度,性能更好 |
prototypeItem | - | 指定一个列表项,可滚动组件会在 layout 时计算一次它延主轴方向的长度,和itemExtent互斥 |
shrinkWrap | false | 是否根据子组件的总长度来设置ListView的长度 |
addRepaintBoundaries | - | 是否将列表项(子组件)包裹在RepaintBoundary组件中,看list复杂情况设置 |
- 数量固定的情况:
ListView(shrinkWrap: true, padding: const EdgeInsets.all(16.0),children: <Widget>[const Text('a'),const Text('b'),const Text('c'),const Text('d'),],
);
- 数量不固定的情况:
ListView.builder(itemCount: 100,// 强制高度为50.0itemExtent: 50.0,itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("$index"));}
);
2-2-1. separated分割线
ListView.separated可以在生成的列表项之间添加一个分割组件,它比ListView.builder多了一个separatorBuilder参数,该参数是一个分割组件生成器。
- 奇数行添加一条红色下划线,偶数行添加一条绿色下划线:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {Widget divider1 = const Divider(color: Colors.red);Widget divider2 = const Divider(color: Colors.green);Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: ListView.separated(itemCount: 100,// 列表项构造器itemBuilder: (BuildContext context, int index) {return ListTile(title: Text("$index"));},// 分割器构造器separatorBuilder: (BuildContext context, int index) {return index % 2 == 0 ? divider1 : divider2;},));}
}
2-2-2. 无限加载列表
在实际开发中,我们经常需要实现一个无限加载列表,即当列表滚动到底部时,再向服务器请求数据,并添加到列表,例子如下:
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {static const loadingTag = "##loading##"; //表尾标记var words = <String>[loadingTag];void initState() {super.initState();_retrieveData();}// 加载数据void _retrieveData() {Future.delayed(const Duration(seconds: 2)).then((e) {setState(() {//重新构建列表words.insertAll(words.length - 1,//每次生成20个单词generateWordPairs().take(20).map((e) => e.asPascalCase).toList(),);});});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: ListView.separated(itemCount: words.length,itemBuilder: (context, index) {// 如果到了表尾if (words[index] == loadingTag) {// 不足100条,继续获取数据if (words.length - 1 < 100) {// 获取数据_retrieveData();// 加载时显示loadingreturn Container(padding: const EdgeInsets.all(16.0),alignment: Alignment.center,child: const SizedBox(width: 24.0,height: 24.0,child: CircularProgressIndicator(strokeWidth: 2.0),),);} else {// 已经加载了100条数据,不再获取数据return Container(alignment: Alignment.center,padding: const EdgeInsets.all(16.0),child: const Text("没有更多了",style: TextStyle(color: Colors.grey),),);}}// 显示单词列表项return ListTile(title: Text(words[index]));},separatorBuilder: (context, index) => const Divider(height: .0),));}
}
2-2-3. 带标题列表
Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: Column(children: [// 标题const ListTile(title: Text('标题')),// 用Expanded 占满剩余空间Expanded(child: ListView.builder(itemBuilder: (BuildContext context, int index) {return ListTile(title: Text('行:$index'),);}),)],));}
3. 滚动监听和控制
可以用ScrollController来控制可滚动组件的滚动位置。而,可滚动组件在滚动时会发送ScrollNotification类型的通知,ScrollBar正是通过监听滚动通知来实现的。
3-1. ScrollController滚动监听
ScrollController间接继承自Listenable,我们可以根据ScrollController来监听滚动事件。如下例子:
- 滚动到1000的时候,出现回到顶部的按钮,点击即可回到顶部
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {/// 监听控制器ScrollController myController = ScrollController();/// 是否显示回到顶部按钮bool toTop = false;void initState() {super.initState();/// 开启监听myController.addListener(() {if (myController.offset < 1000 && toTop) {setState(() {toTop = false;});} else if (myController.offset >= 1000 && toTop == false) {setState(() {toTop = true;});}});}void dispose() {/// 注销监听myController.dispose();super.dispose();}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: Scrollbar(child: ListView.builder(/// 固定列数itemCount: 100,/// 固定列高itemExtent: 50.0,controller: myController,/// 渲染行itemBuilder: (BuildContext context, int index) {return ListTile(title: Text('行:$index'));},)),/// 返回顶部按钮floatingActionButton: !toTop? null: FloatingActionButton(child: const Icon(Icons.arrow_upward),onPressed: () {/// 动画返回myController.animateTo(0.0,duration: const Duration(microseconds: 200),curve: Curves.ease);},),);}
}
3-2. NotificationListener滚动监听
和ScrollController不同,NotificationListener可以在可滚动组件到widget树根之间任意位置监听,而ScrollController只能和具体的可滚动组件关联后才可以。并且,收到滚动事件时,NotificationListener通知中会携带当前滚动位置和ViewPort的一些信息,而ScrollController只能获取当前滚动位置。如下例子:
- 中间有个文字会显示滚动的进度是多少
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {/// 进度文案String myProgress = '0%';Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: NotificationListener(onNotification: (ScrollNotification notification) {double progress = notification.metrics.pixels / notification.metrics.maxScrollExtent;setState(() {myProgress = '${(progress * 100).toInt()}%';});return false;},child: Stack(alignment: Alignment.center,children: [ListView.builder(itemCount: 100,itemExtent: 50.0,itemBuilder: (context, index) => ListTile(title: Text('第$index行'),),),CircleAvatar(radius: 30.0,backgroundColor: Colors.black12,child: Text(myProgress))],),));}
}
4. AnimatedList动画列表
AnimatedList 和 ListView 的功能大体相似,不同的是, AnimatedList 可以在列表中插入或删除节点时执行一个动画,在需要添加或删除列表项的场景中会提高用户体验。如下例子:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {/// 数据列表var dataList = <String>[];/// 初始个数int counter = 6;/// Keyfinal myKey = GlobalKey<AnimatedListState>();void initState() {super.initState();/// 初始化数据for (var i = 0; i < counter; i++) {dataList.add('标题:${i + 1}');}}Widget buildItem(context, index) {String str = dataList[index];return ListTile(key: ValueKey(str),title: Text(str),trailing: IconButton(icon: const Icon(Icons.delete),onPressed: () {/// 删除的逻辑和动画setState(() {myKey.currentState!.removeItem(index, (context, animation) {var item = buildItem(context, index);dataList.removeAt(index);return FadeTransition(opacity: CurvedAnimation(parent: animation, curve: const Interval(0.5, 1.0)),child: SizeTransition(sizeFactor: animation,axisAlignment: 0.0,child: item,),);}, duration: const Duration(milliseconds: 200));});},),);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: Stack(alignment: Alignment.center,children: [AnimatedList(key: myKey,initialItemCount: dataList.length,itemBuilder: (context, index, animation) {return FadeTransition(opacity: animation, child: buildItem(context, index));},),Positioned(left: 0,right: 0,bottom: 30.0,child: FloatingActionButton(/// 添加项onPressed: () {dataList.add('标题:${++counter}');myKey.currentState!.insertItem(dataList.length - 1);},child: const Icon(Icons.add),),)],));}
}
5. 滚动网格布局GridView
GridView是ListView的升级版,可以构建二维网格列表,可以指定子组件的布局方式,如网格布局、列表布局等。他的参数和ListView是一致的,唯一区别是多了个gridDelegate参数,控制GridView子组件的排列。它提供了SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent两个子类。
5-1. 横轴子元素为固定数量
使用SliverGridDelegateWithFixedCrossAxisCount,可以实现一个横轴为固定数量子元素的layout布局。子元素的大小(最大显示空间)是通过crossAxisCount和childAspectRatio两个参数共同决定的。
/// 写法一
GridView(gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(/// 横轴子元素的数量crossAxisCount: 3,/// 主轴方向的间距mainAxisSpacing: 10.0,/// 横轴方向子元素的间距crossAxisSpacing: 20.0,/// 子元素在横轴长度和主轴长度的比例childAspectRatio: 1.0),/// 元素列表children: const [Icon(Icons.ac_unit),Icon(Icons.airport_shuttle),Icon(Icons.all_inclusive),Icon(Icons.beach_access),Icon(Icons.cake),Icon(Icons.free_breakfast)]
)
/// 写法二
GridView.count( crossAxisCount: 3,childAspectRatio: 1.0,children: const [Icon(Icons.ac_unit),Icon(Icons.airport_shuttle),Icon(Icons.all_inclusive),Icon(Icons.beach_access),Icon(Icons.cake),Icon(Icons.free_breakfast),],
)
5-2. 横轴子元素为固定最大长度
使用SliverGridDelegateWithMaxCrossAxisExtent,可以实现一个横轴子元素为固定最大长度的layout布局,子元素的大小(最大显示空间)是通过maxCrossAxisExtent参数决定的。
/// 写法一
GridView(gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(/// 子元素在横轴上的最大长度maxCrossAxisExtent: 100.0,/// 子元素在横轴长度和主轴长度的比例childAspectRatio: 1.0,/// 主轴方向的间距mainAxisSpacing: 10.0,/// 横轴方向子元素的间距crossAxisSpacing: 20.0,),children: const [Icon(Icons.ac_unit),Icon(Icons.airport_shuttle),Icon(Icons.all_inclusive),Icon(Icons.beach_access),Icon(Icons.cake),Icon(Icons.free_breakfast)]
)/// 写法二
GridView.extent(maxCrossAxisExtent: 120.0,childAspectRatio: 1.0,children: const [Icon(Icons.ac_unit),Icon(Icons.airport_shuttle),Icon(Icons.all_inclusive),Icon(Icons.beach_access),Icon(Icons.cake),Icon(Icons.free_breakfast),],
)
5-3. 动态创建
当子widget比较多时,我们可以通过GridView.builder来动态创建子widget。如下例子:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {/// Icon列表List<IconData> myIcons = [];void initState() {super.initState();/// 初始化数据handleSyncAddIcon();}/// 模拟异步获取数据void handleSyncAddIcon() {Future.delayed(const Duration(milliseconds: 2000)).then((e) {setState(() {myIcons.addAll([Icons.ac_unit,Icons.airport_shuttle,Icons.all_inclusive,Icons.beach_access,Icons.cake,Icons.free_breakfast,]);});});}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: GridView.builder(gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(/// 每行三列crossAxisCount: 3,/// 显示区域宽高相等childAspectRatio: 1.0,),itemCount: myIcons.length,itemBuilder: (context, index) {/// 如果显示到最后一个 并且Icon总数小于200时 继续获取数据if (index == myIcons.length - 1 && myIcons.length < 200) {handleSyncAddIcon();}return Icon(myIcons[index]);},));}
}
6. PageView页面切换和缓存
如果要实现页面切换和 Tab 布局,Tab 换页效果、图片轮动以及抖音上下滑页切换视频功能等等,我们可以使用 PageView 组件。如下全屏切换的例子:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class PageItem extends StatefulWidget {const PageItem({Key? key,required this.text}) : super(key: key);final String text;PageItemState createState() => PageItemState();
}class PageItemState extends State<PageItem> {Widget build(BuildContext context) {debugPrint('触发${widget.text}');return Center(child: Text("第${widget.text}页", textScaleFactor: 5));}
}class HomePageState extends State<HomePage> {var children = <Widget>[];void initState() {super.initState();for (int i = 0; i < 6; ++i) {children.add(PageItem(text: '$i'));}}Widget build(BuildContext context) {debugPrint('触发');return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: PageView(/// 每次滑动是否强制切换整个页面pageSnapping: true,padEnds: true,/// 滑动方向scrollDirection: Axis.vertical,/// 子元素children: children,));}
}
6-1. PageState缓存
只需要让 PageState 混入这个 AutomaticKeepAliveClientMixin ,简单改造就可以实现缓存。
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class PageItem extends StatefulWidget {const PageItem({Key? key,required this.text}) : super(key: key);final String text;PageItemState createState() => PageItemState();
}class PageItemState extends State<PageItem> with AutomaticKeepAliveClientMixin {Widget build(BuildContext context) {super.build(context);debugPrint('触发${widget.text}');return Center(child: Text("第${widget.text}页", textScaleFactor: 5));}/// 是否需要缓存 bool get wantKeepAlive => true;
}class HomePageState extends State<HomePage> {var children = <Widget>[];void initState() {super.initState();for (int i = 0; i < 6; ++i) {children.add(PageItem(text: '$i'));}}Widget build(BuildContext context) {debugPrint('触发');return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: PageView(/// 每次滑动是否强制切换整个页面pageSnapping: true,padEnds: true,/// 滑动方向scrollDirection: Axis.vertical,/// 子元素children: children,));}
}
7. TabBarView
abBarView 封装了 PageView,而abController 用于监听和控制 TabBarView 的页面切换,通常和 TabBar 联动。如果没有指定,则会在组件树中向上查找并使用最近的一个 DefaultTabController。如下例子:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class PageItem extends StatefulWidget {const PageItem({Key? key, required this.text}) : super(key: key);final String text;PageItemState createState() => PageItemState();
}class PageItemState extends State<PageItem> with AutomaticKeepAliveClientMixin {Widget build(BuildContext context) {super.build(context);debugPrint('触发${widget.text}');return Center(child: Text("${widget.text}", textScaleFactor: 5));}/// 是否需要缓存 bool get wantKeepAlive => true;
}class HomePageState extends State<HomePage> {var children = <Widget>[];List tabs = ["新闻", "历史", "图片"];void initState() {super.initState();for (int i = 0; i < tabs.length; ++i) {children.add(PageItem(text: '${tabs[i]}'));}}Widget build(BuildContext context) {return DefaultTabController(length: children.length,child: Scaffold(appBar: AppBar(title: const Text("App Name"),bottom: TabBar(tabs: tabs.map((e) => Tab(text: e)).toList(),),),body: TabBarView(/// tab子页面children: children,),),);}
}
8. CustomScrollView和Slivers
CustomScrollView的 slivers 参数接受一个 Sliver 数组,这样就可以在一个页面中,同时包含多个可滚动组件,且使它们的滑动效果能统一起来。如下例子:
import 'package:flutter/material.dart';class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}class HomePageState extends State<HomePage> {var listView = SliverFixedExtentList(itemExtent: 56,delegate: SliverChildBuilderDelegate((_, index) => ListTile(title: Text('$index')),childCount: 10,));Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Home Page'),backgroundColor: Theme.of(context).colorScheme.inversePrimary,),body: Center(child: CustomScrollView(slivers: [listView,listView,],),));}
}
8-1. 常用的Slivers
- 可滚动组件对应的Sliver
名称 | 功能 | 对应组件 |
---|---|---|
SliverList | 列表 | ListView |
SliverFixedExtentList | 高度固定的列表 | ListView,指定itemExtent后 |
SliverAnimatedList | 添加/删除列表项可以执行动画 | AnimatedList |
SliverGrid | 网格 | GridView |
SliverPrototypeExtentList | 根据原型生成高度固定的列表 | ListView,指定prototypeItem后 |
SliverFillViewport | 包含多个子组件,每个都可以填满屏幕 | PageView |
- 子组件必须是Sliver
名称 | 对应RenderBox容器 |
---|---|
SliverPadding | Padding |
SliverVisibility、SliverOpacity | Visibility、Opacity |
SliverFadeTransition | FadeTransition |
SliverLayoutBuilder | LayoutBuilder |
- 其他常用的Sliver
名称 | 功能 |
---|---|
SliverAppBar | 对应 AppBar,主要是为了在 CustomScrollView 中使用 |
SliverToBoxAdapter | 一个适配器,可以将 RenderBox 适配为 Sliver |
SliverPersistentHeader | 滑动到顶部时可以固定住 |
本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~
往期文章
- 手把手教你搭建规范的团队vue项目,包含commitlint,eslint,prettier,husky,commitizen等等
- Web Woeker和Shared Worker的使用以及案例
- Vue2全家桶+Element搭建的PC端在线音乐网站
- vue3+element-plus配置cdn
- 助你上手Vue3全家桶之Vue3教程
- 助你上手Vue3全家桶之VueX4教程
- 助你上手Vue3全家桶之Vue-Router4教程
- 超详细!Vue的九种通信方式
- 超详细!Vuex手把手教程
- 使用nvm管理node.js版本以及更换npm淘宝镜像源
- vue中利用.env文件存储全局环境变量,以及配置vue启动和打包命令
- 超详细!Vue-Router手把手教程
个人主页
- CSDN
- GitHub
- 简书
- 博客园
- 掘金
相关文章:
flutter学习-day12-可滚动组件和监听
📚 目录 简介可滚动组件 SingleChildScrollViewListView separated分割线无限加载列表带标题列表 滚动监听和控制 ScrollController滚动监听NotificationListener滚动监听 AnimatedList动画列表滚动网格布局GridView 横轴子元素为固定数量横轴子元素为固定最大长度…...
LeetCode:967连续查相同的数字(DFS)
题目 返回所有长度为 n 且满足其每两个连续位上的数字之间的差的绝对值为 k 的 非负整数 。 请注意,除了 数字 0 本身之外,答案中的每个数字都 不能 有前导零。例如,01 有一个前导零,所以是无效的;但 0 是有效的。 …...
深入剖析NPM: Node包管理器的介绍和使用指南
导言:NPM(Node Package Manager)是JavaScript世界中最受欢迎的包管理器之一。它的出现大大简化了JavaScript开发过程中的依赖管理和模块化。本文将向您介绍NPM的基本概念、功能和常见用法,并为您提供一份详尽的NPM使用指南。 一、…...
AI视频-stable-video-diffusio介绍
介绍 stbilityai/stable-video-diffusion-img2vid-xt模型,由Stability AI开发和训练的基于散度的图像到视频生成模型。该模型可以接受一张静态图像作为条件,并生成出一个短视频。 该模型通过在SVD Image-to-Video [14帧]的基础上进行微调而来,可以生成576x1024分辨…...
day01-报表技术POI
前言 报表[forms for reporting to the higher organizations],就是向上级报告情况的表格。简单的说:报表就是用表格、图表等格式来动态显示数据,可以用公式表示为:“报表 多样的格式 动态的数据”。 1、开发环境搭建 功能说…...
如何预防最新的.locked、.locked1勒索病毒感染您的计算机?
尊敬的读者: 近期,网络安全领域迎来一股新潮——.locked、.locked1勒索病毒的威胁,其先进的加密技术令人生畏。本文将深入剖析.locked、.locked1勒索病毒的阴谋,提供特色数据恢复策略,并揭示锁定恶劣行径的先锋预防手…...
实现两张图片的接缝线拼接
使用ORB算法检测特征点,并通过BFMatcher进行特征点匹配。然后,根据Lowes ratio test选择好的匹配点,并使用findHomography计算单应性矩阵。最后,使用warpPerspective将图像进行透视变换,然后将第二张图像粘贴到变换后的…...
基于JNI 实现 嵌套 List 类型参数解析
基于JNI 实现 嵌套 List 类型参数解析 背景分析解决 背景 在前面两篇文章中,我们总结了Java 调用 C/C SDK 的几种方案,分享了JNI在实践过程中的一些踩坑点,而在这篇文章将继续分享针对Java List类型及其嵌套类型,我们的JNI如何接…...
探索灵活性与可维护性的利器:策略(Strategy)模式详解
目录 编辑 1. 策略模式概述: 2. 主要角色: 3. 实例场景: 4. 具体实现步骤: 步骤一:定义策略接口 5. 使用策略模式的客户端代码: 总结: 我的其他博客 1. 策略模式概述: 策…...
压缩包文件暴力破解 -Server2005(解析)
任务五十一: 压缩包文件暴力破解 任务环境说明:Server2005 1. 通过本地PC中渗透测试平台Kali使用Nmap扫描目标靶机服务版本信息,将 Telnet 版本信息字符串 作为 Flag 提交; flag:Microsoft Windows XP telnetd 2. 通过本地PC中渗透测试平台Kali对服务器场景Windows进行渗透测…...
mars3d加载arcgis发布的服务,⽀持4523坐标
问题 1.从这个服务地址加载,具体在哪⾥去转坐标呢? 加个 usePreCachedTilesIfAvailable:false 参数即可 坐标系为4490的arcgis影像服务图层,配置后瓦片加载不出来,没报错 甚至可以跳转 没有看出问题,或者测…...
『K8S 入门』二:深入 Pod
『K8S 入门』二:深入 Pod 一、基础命令 获取所有 Pod kubectl get pods2. 获取 deploy kubectl get deploy3. 删除 deploy,这时候相应的 pod 就没了 kubectl delete deploy nginx4. 虽然删掉了 Pod,但是这是时候还有 service,…...
十七、如何将MapReduce程序提交到YARN运行
1、启动某个节点的某一个用户 hadoopnode1:~$ jps 13025 Jps hadoopnode1:~$ yarn --daemon start resourcemanager hadoopnode1:~$ jps 13170 ResourceManager 13253 Jps hadoopnode1:~$ yarn --daemon start nodemanager hadoopnode1:~$ jps 13170 ResourceManager 15062 Jp…...
华为云CodeArts Deploy常见问答汇总
1.【Deploy】部署java项目,为什么通过springboot启动步骤启动失败了? 答:用户所部署的jar包源码并不是springboot框架,所以无法用springboot启动步骤启动,该步骤并不等同于java -jar 命令,需要使用shell脚…...
前后端交互—开发一个完整的服务器
代码下载 初始化 新建 apiServer 文件夹作为项目根目录,并在项目根目录中运行如下的命令,初始化包管理配置文件: npm init -y运行如下的命令,安装 express、cors: npm i express cors在项目根目录中新建 app.js 作为整个项目的入口文件&a…...
前端框架的虚拟DOM(Virtual DOM)
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
什么是http状态码?
什么是http状态码? 当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含 HTTP 状态码的信息头(server header)用以响应浏览器的请求。 ht…...
linux/CentOS 7安装Nginx
Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境。 Nginx一般使用非root账号安装,如果还没有非root账号,先创建账号 创建账号 创建组…...
软件工程期末复习+数据仓库ETL
一、软件工程 请用基本路径测试方法为下列程序设计测试用例,并写明中间过程: 第1步:画出流程图 1.菱形用于条件判断。用在有分支的地方。 2.矩形表示一个基本操作。 3.圆形是连接点 第2步:计算程序环路复杂性 流图G的环路复杂…...
学习C语言——体会计算机中的0和1
/* 把hello隐写入一个整型数组,这个小程序可以考察是否清楚数据在内存中存储的具体细节。 具体的说,int类型在小端机器上的存储方式是高位在高地址,低位在低地址,从视觉习惯上和我们的日常书写习惯相反; char类型占用…...
PyTorch官网demo解读——第一个神经网络(1)
神经网络如此神奇,feel the magic 今天分享一下学习PyTorch官网demo的心得,原来实现一个神经网络可以如此简单/简洁/高效,同时也感慨PyTorch如此强大。 这个demo的目的是训练一个识别手写数字的模型! 先上源码: fr…...
升华 RabbitMQ:解锁一致性哈希交换机的奥秘【RabbitMQ 十】
欢迎来到我的博客,代码的世界里,每一行都是一个故事 升华 RabbitMQ:解锁一致性哈希交换机的奥秘【RabbitMQ 十】 前言第一:该插件需求为什么需要一种更智能的消息路由方式?一致性哈希的基本概念: 第二&…...
vue3 element-plus 日期选择器 el-date-picker 汉化
vue3 项目中,element-plus 的日期选择器 el-date-picker 默认是英文版的,如下: 页面引入: //引入汉化语言包 import locale from "element-plus/lib/locale/lang/zh-cn" import { ElDatePicker, ElButton, ElConfigP…...
剑指 Offer(第2版)面试题 35:复杂链表的复制
剑指 Offer(第2版)面试题 35:复杂链表的复制 剑指 Offer(第2版)面试题 35:复杂链表的复制解法1:模拟 剑指 Offer(第2版)面试题 35:复杂链表的复制 题目来源&…...
自定义指令Custom Directives
<script setup langts> import { ref } from "vue"const state ref(false)/*** Implement the custom directive* Make sure the input element focuses/blurs when the state is toggled* */ // 以v开头的驼峰式命名的变量都可以作为一个自定义指令 const VF…...
预测性维护对制造企业设备管理的作用
制造企业设备管理和维护对于生产效率和成本控制至关重要。然而,传统的维护方法往往无法准确预测设备故障,导致生产中断和高额维修费用。为了应对这一挑战,越来越多的制造企业开始采用预测性维护技术。 预测性维护是通过传感器数据、机器学习和…...
华为、新华三、锐捷常用命令总结
华为、新华三、锐捷常用命令总结 一、华为交换机基础配置命令二、H3C交换机的基本配置三、锐捷交换机基础命令配置 一、华为交换机基础配置命令 1、创建vlan: <Quidway> //用户视图,也就是在Quidway模式下运行命令。 <Quidway>system-view…...
链路追踪详解(四):分布式链路追踪的事实标准 OpenTelemetry 概述
目录 OpenTelemetry 是什么? OpenTelemetry 的起源和目标 OpenTelemetry 主要特点和功能 OpenTelemetry 的核心组件 OpenTelemetry 的工作原理 OpenTelemetry 的特点 OpenTelemetry 的应用场景 小结 OpenTelemetry 是什么? OpenTelemetry 是一个…...
Node.js 工作线程与子进程:应该使用哪一个
Node.js 工作线程与子进程:应该使用哪一个 并行处理在计算密集型应用程序中起着至关重要的作用。例如,考虑一个确定给定数字是否为素数的应用程序。如果我们熟悉素数,我们就会知道必须从 1 遍历到该数的平方根才能确定它是否是素数ÿ…...
python matplotlib 三维图形添加文字且不随图形变动而变动
要在三维图形中添加文字并使其不随图形变动而变动,可以使用 annotate() 方法。这个方法可以在三维图形中添加文字,并且可以指定文字的位置、对齐方式和字体大小等属性。 下面是一个示例代码,演示如何在三维图形中添加文字: impo…...
网站跳出率/app推广是做什么的
SNV下载历史版本的某个文件 目的:从SVN上下载历史版本,不是整个工程的某个历史版本,而是某个文件的历史版本。 首先找到想要下载的文件右键Show log,找到想要的某个版本点击右键选择save resivion to 这样就保存了想要的&#…...
德阳网站制作/seo建设招商
圈套模式之投资 在触发器篇,我们讨论了内部触发器的重要性。通过外部触发器产品设计者可以引诱用户做出下一步行动。在行动篇,我们知道用户的一小步行动都是想立即实现潜在的期望。在奖励篇,我们知道不确定的奖励会使用户不断来使用产品。 投…...
昌乐哪里有做网站的/互联网营销师考试
其它信息 下载: http://cdn.cocos2d-x.org/cocos2d-x-3.0alpha1.zip 完整的更新日志: https://github.com/cocos2d/cocos2d-x/blob/cocos2d-x-3.0alph1/CHANGELOG API 参考:http://www.cocos2d-x.org/reference/native-cpp/V3.0alpha1/index.html 要求 运行环境…...
天津高端网站建设/seo专员岗位要求
今天学习: 在Ubuntu下软件源的文件是/etc/apt/sources.list,那么sourdces.list.d目录下的文件又是什么作用呢? 该文件夹下的文件是第三方软件的源,可以分别存放不同的第三源地址,只需“扩展名”为list即可,…...
潮州市住房和城乡建设局网站/市场营销方案
在有些情况下,我们不希望自己的shell脚本在运行时刻被中断,比如说我们写得shell脚 本设为某一用户的默认shell,使这一用户进入系统后只能作某一项工作,如数据库备份, 我 们可不希望用户使用ctrlC之类便进入到shell状…...
企业网站可以免费做吗/汕头网站建设平台
通过虚拟地址访问内存有以下优势: 1 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。 2 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常…...