当前位置: 首页 > news >正文

flutter学习-day12-可滚动组件和监听

📚 目录

  1. 简介
  2. 可滚动组件
    1. SingleChildScrollView
    2. ListView
      1. separated分割线
      2. 无限加载列表
      3. 带标题列表
  3. 滚动监听和控制
    1. ScrollController滚动监听
    2. NotificationListener滚动监听
  4. AnimatedList动画列表
  5. 滚动网格布局GridView
    1. 横轴子元素为固定数量
    2. 横轴子元素为固定最大长度
    3. 动态创建
  6. PageView页面切换和缓存
    1. PageState缓存
  7. TabBarView
  8. CustomScrollView和Slivers
    1. 常用的Slivers

本文学习和引用自《Flutter实战·第二版》:作者:杜文

1. 简介

通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大;如果要一次性将子组件全部构建出将会非常昂贵!为此,Flutter中提出一个Sliver(中文为“薄片”的意思)概念,Sliver 可以包含一个或多个子组件。Sliver 的主要作用是配合:加载子组件并确定每一个子组件的布局和绘制信息,如果 Sliver 可以包含多个子组件时,通常会实现按需加载模型。只有当 Sliver 出现在视口中时才会去构建它,这种模型也称为“基于Sliver的列表按需加载模型”。可滚动组件中有很多都支持基于Sliver的按需加载模型,如ListView、GridView,但是也有不支持该模型的,如SingleChildScrollView。

Flutter 中的可滚动组件主要由三个角色组成:

  • Scrollable :用于处理滑动手势,确定滑动偏移,滑动偏移变化时构建 Viewport。
方法描述
axisDirection-滚动方向
physicsClampingScrollPhysics、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互斥
shrinkWrapfalse是否根据子组件的总长度来设置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容器
SliverPaddingPadding
SliverVisibility、SliverOpacityVisibility、Opacity
SliverFadeTransitionFadeTransition
SliverLayoutBuilderLayoutBuilder
  • 其他常用的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-可滚动组件和监听

&#x1f4da; 目录 简介可滚动组件 SingleChildScrollViewListView separated分割线无限加载列表带标题列表 滚动监听和控制 ScrollController滚动监听NotificationListener滚动监听 AnimatedList动画列表滚动网格布局GridView 横轴子元素为固定数量横轴子元素为固定最大长度…...

LeetCode:967连续查相同的数字(DFS)

题目 返回所有长度为 n 且满足其每两个连续位上的数字之间的差的绝对值为 k 的 非负整数 。 请注意&#xff0c;除了 数字 0 本身之外&#xff0c;答案中的每个数字都 不能 有前导零。例如&#xff0c;01 有一个前导零&#xff0c;所以是无效的&#xff1b;但 0 是有效的。 …...

深入剖析NPM: Node包管理器的介绍和使用指南

导言&#xff1a;NPM&#xff08;Node Package Manager&#xff09;是JavaScript世界中最受欢迎的包管理器之一。它的出现大大简化了JavaScript开发过程中的依赖管理和模块化。本文将向您介绍NPM的基本概念、功能和常见用法&#xff0c;并为您提供一份详尽的NPM使用指南。 一、…...

AI视频-stable-video-diffusio介绍

介绍 stbilityai/stable-video-diffusion-img2vid-xt模型&#xff0c;由Stability AI开发和训练的基于散度的图像到视频生成模型。该模型可以接受一张静态图像作为条件,并生成出一个短视频。 该模型通过在SVD Image-to-Video [14帧]的基础上进行微调而来,可以生成576x1024分辨…...

day01-报表技术POI

前言 报表[forms for reporting to the higher organizations]&#xff0c;就是向上级报告情况的表格。简单的说&#xff1a;报表就是用表格、图表等格式来动态显示数据&#xff0c;可以用公式表示为&#xff1a;“报表 多样的格式 动态的数据”。 1、开发环境搭建 功能说…...

如何预防最新的.locked、.locked1勒索病毒感染您的计算机?

尊敬的读者&#xff1a; 近期&#xff0c;网络安全领域迎来一股新潮——.locked、.locked1勒索病毒的威胁&#xff0c;其先进的加密技术令人生畏。本文将深入剖析.locked、.locked1勒索病毒的阴谋&#xff0c;提供特色数据恢复策略&#xff0c;并揭示锁定恶劣行径的先锋预防手…...

实现两张图片的接缝线拼接

使用ORB算法检测特征点&#xff0c;并通过BFMatcher进行特征点匹配。然后&#xff0c;根据Lowes ratio test选择好的匹配点&#xff0c;并使用findHomography计算单应性矩阵。最后&#xff0c;使用warpPerspective将图像进行透视变换&#xff0c;然后将第二张图像粘贴到变换后的…...

基于JNI 实现 嵌套 List 类型参数解析

基于JNI 实现 嵌套 List 类型参数解析 背景分析解决 背景 在前面两篇文章中&#xff0c;我们总结了Java 调用 C/C SDK 的几种方案&#xff0c;分享了JNI在实践过程中的一些踩坑点&#xff0c;而在这篇文章将继续分享针对Java List类型及其嵌套类型&#xff0c;我们的JNI如何接…...

探索灵活性与可维护性的利器:策略(Strategy)模式详解

目录 ​编辑 1. 策略模式概述&#xff1a; 2. 主要角色&#xff1a; 3. 实例场景&#xff1a; 4. 具体实现步骤&#xff1a; 步骤一&#xff1a;定义策略接口 5. 使用策略模式的客户端代码&#xff1a; 总结&#xff1a; 我的其他博客 1. 策略模式概述&#xff1a; 策…...

压缩包文件暴力破解 -Server2005(解析)

任务五十一: 压缩包文件暴力破解 任务环境说明:Server2005 1. 通过本地PC中渗透测试平台Kali使用Nmap扫描目标靶机服务版本信息,将 Telnet 版本信息字符串 作为 Flag 提交; flag:Microsoft Windows XP telnetd 2. 通过本地PC中渗透测试平台Kali对服务器场景Windows进行渗透测…...

mars3d加载arcgis发布的服务,⽀持4523坐标

问题 1.从这个服务地址加载&#xff0c;具体在哪⾥去转坐标呢&#xff1f; 加个 usePreCachedTilesIfAvailable&#xff1a;false 参数即可 坐标系为4490的arcgis影像服务图层&#xff0c;配置后瓦片加载不出来&#xff0c;没报错 甚至可以跳转 没有看出问题&#xff0c;或者测…...

『K8S 入门』二:深入 Pod

『K8S 入门』二&#xff1a;深入 Pod 一、基础命令 获取所有 Pod kubectl get pods2. 获取 deploy kubectl get deploy3. 删除 deploy&#xff0c;这时候相应的 pod 就没了 kubectl delete deploy nginx4. 虽然删掉了 Pod&#xff0c;但是这是时候还有 service&#xff0c…...

十七、如何将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项目&#xff0c;为什么通过springboot启动步骤启动失败了&#xff1f; 答&#xff1a;用户所部署的jar包源码并不是springboot框架&#xff0c;所以无法用springboot启动步骤启动&#xff0c;该步骤并不等同于java -jar 命令&#xff0c;需要使用shell脚…...

前后端交互—开发一个完整的服务器

代码下载 初始化 新建 apiServer 文件夹作为项目根目录&#xff0c;并在项目根目录中运行如下的命令&#xff0c;初始化包管理配置文件: npm init -y运行如下的命令&#xff0c;安装 express、cors: npm i express cors在项目根目录中新建 app.js 作为整个项目的入口文件&a…...

前端框架的虚拟DOM(Virtual DOM)

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

什么是http状态码?

什么是http状态码&#xff1f; 当浏览者访问一个网页时&#xff0c;浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前&#xff0c;此网页所在的服务器会返回一个包含 HTTP 状态码的信息头&#xff08;server header&#xff09;用以响应浏览器的请求。 ht…...

linux/CentOS 7安装Nginx

Nginx 是 C语言 开发&#xff0c;建议在 Linux 上运行&#xff0c;当然&#xff0c;也可以安装 Windows 版本&#xff0c;本篇则使用 CentOS 7 作为安装环境。 Nginx一般使用非root账号安装&#xff0c;如果还没有非root账号&#xff0c;先创建账号 创建账号 创建组&#xf…...

软件工程期末复习+数据仓库ETL

一、软件工程 请用基本路径测试方法为下列程序设计测试用例&#xff0c;并写明中间过程&#xff1a; 第1步&#xff1a;画出流程图 1.菱形用于条件判断。用在有分支的地方。 2.矩形表示一个基本操作。 3.圆形是连接点 第2步&#xff1a;计算程序环路复杂性 流图G的环路复杂…...

学习C语言——体会计算机中的0和1

/* 把hello隐写入一个整型数组,这个小程序可以考察是否清楚数据在内存中存储的具体细节。 具体的说&#xff0c;int类型在小端机器上的存储方式是高位在高地址&#xff0c;低位在低地址&#xff0c;从视觉习惯上和我们的日常书写习惯相反&#xff1b; char类型占用…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...