07_Flutter使用NestedScrollView+TabBarView滚动位置共享问题修复
07_Flutter使用NestedScrollView+TabBarView滚动位置共享问题修复
一.案发现场

可以看到,上图中三个列表的滑动位置共享了,滑动其中一个列表,会影响到另外两个,这显然不符合要求,先来看下布局,再说明产生这个问题的原因:
-
布局整体使用NestedScrollView,顶部banner和TabBar通过headerSliverBuilder创建,body为TabBarView,TabBarView中有三个列表,通过TabController与TabBar实现联动,同时每一个列表通过继承StatefulWidget构建并混入AutomaticKeepAliveClientMixin,重写wantKeepAlive的getter方法返回true,这样可以保证每次切换Tab的时候,ListView不会重新创建,实现懒加载。
NestedScrollView(headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {return [SliverToBoxAdapter(child: Container(height: 200,color: Colors.red,alignment: Alignment.center,child: const Text("banner",style: TextStyle(color: Colors.white,fontSize: 16),),),),SliverOverlapAbsorber(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),sliver: StickySliverToBoxAdapter(child: Container(color: Colors.white,child: TabBar(tabs: List.generate(_tabs.length, (index) {return Padding(padding: const EdgeInsets.symmetric(vertical: 15),child: Text(_tabs[index]),);}),unselectedLabelColor: const Color(0xFF7B7B7B),labelColor: const Color(0xFF5E80FF),isScrollable: false,indicatorSize: TabBarIndicatorSize.label,indicator: UnderlineTabIndicator(borderRadius: BorderRadius.circular(3),borderSide: const BorderSide(color: Color(0xFF5E80FF), width: 3),insets: const EdgeInsets.symmetric(horizontal: 3, vertical: 9)),controller: _tabController,),),),),];},body: LayoutBuilder(builder: (context, _) {return Container(padding: EdgeInsets.only(top: NestedScrollView.sliverOverlapAbsorberHandleFor(context).layoutExtent ?? 0),child: NestedTabBarView(controller: _tabController,children: List.generate(_tabs.length, (index) {return _TabInnerListView(tabName: _tabs[index],);})));}) )class _TabInnerListViewState extends State<_TabInnerListView> with AutomaticKeepAliveClientMixin {final int length = 20;Widget build(BuildContext context) {return CustomScrollView(physics: const ClampingScrollPhysics(),slivers: [...(List.generate(length, (index) {return SliverToBoxAdapter(child: Container(height: 100,margin: const EdgeInsets.only(top: 16, left: 16, right: 16),color: Colors.orange,alignment: Alignment.center,child: Text("${widget.tabName} item $index",style: const TextStyle(color: Colors.white),),),);})),const SliverToBoxAdapter(child: SizedBox(height: 16,),)],);}bool get wantKeepAlive => true;} -
上述问题产生的原因,需要追踪NestedScrollView的源码,NestedScrollView整体的布局结构如下:

如果没有_NestedScrollCoordinator的加持,那么外层的CustomScrollView和内层的CustomScrollView就会各划各的。_NestedScrollCoordinator处理嵌套滑动是在applyUserOffset方法中完成的:
class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldController {void applyUserOffset(double delta) {updateUserScrollDirection(delta > 0.0 ? ScrollDirection.forward : ScrollDirection.reverse,);assert(delta != 0.0);if (_innerPositions.isEmpty) {_outerPosition!.applyFullDragUpdate(delta);} else if (delta < 0.0) {...} else {double innerDelta = delta;if (_floatHeaderSlivers) {innerDelta = _outerPosition!.applyClampedDragUpdate(delta);}if (innerDelta != 0.0) {double outerDelta = 0.0;final List<double> overscrolls = <double>[];final List<_NestedScrollPosition> innerPositions = _innerPositions.toList();for (final _NestedScrollPosition position in innerPositions) {final double overscroll = position.applyClampedDragUpdate(innerDelta);outerDelta = math.max(outerDelta, overscroll);overscrolls.add(overscroll);}if (outerDelta != 0.0) {outerDelta -= _outerPosition!.applyClampedDragUpdate(outerDelta);}for (int i = 0; i < innerPositions.length; ++i) {final double remainingDelta = overscrolls[i] - outerDelta;if (remainingDelta > 0.0) {innerPositions[i].applyFullDragUpdate(remainingDelta);}}}}} }可以看到在applyUserOffset中,是通过_NestedScrollPosition的applyFullDragUpdate响应滑动事件的,如果调用_outerPosition!.applyFullDragUpdate,则外层的CustomScrollView滑动。同理,内层CustomScrollView滑动,只不过applyUserOffset在处理内层滑动时,是遍历innerPositions把所有内层CustomScrollView的_NestedScrollPosition滚动相同的位移。
_NestedScrollPosition? get _outerPosition {if (!_outerController.hasClients) {return null;}return _outerController.nestedPositions.single; }Iterable<_NestedScrollPosition> get _innerPositions {return _innerController.nestedPositions; }这也就解释了上图中,为什么滚动其中一个列表,其他列表也会跟着滑动相同的位置?。
二.解决方案
综上所述,_NestedScrollCoordinator的_innerPositions的返回结果是所有内层CustomScrollView的_NestedScrollPosition,要解决这个问题,我们只需要想办法将_NestedScrollCoordinator的_innerPositions的返回结果改变成只包含当前选中的内层CustomScrollView的_NestedScrollPosition即可,而_innerPositions的取值是来源于_innerController的nestedPositions。_innerController是一个_NestedScrollController对象,接着看_NestedScrollController的源码:
class _NestedScrollController extends ScrollController {...ScrollPosition createScrollPosition(ScrollPhysics physics,ScrollContext context,ScrollPosition? oldPosition,) {return _NestedScrollPosition(coordinator: coordinator,physics: physics,context: context,initialPixels: initialScrollOffset,oldPosition: oldPosition,debugLabel: debugLabel,);}void attach(ScrollPosition position) {assert(position is _NestedScrollPosition);super.attach(position);coordinator.updateParent();coordinator.updateCanDrag();position.addListener(_scheduleUpdateShadow);_scheduleUpdateShadow();}void detach(ScrollPosition position) {assert(position is _NestedScrollPosition);(position as _NestedScrollPosition).setParent(null);position.removeListener(_scheduleUpdateShadow);super.detach(position);_scheduleUpdateShadow();}...Iterable<_NestedScrollPosition> get nestedPositions {// TODO(vegorov): use instance method version of castFrom when it is available.return Iterable.castFrom<ScrollPosition, _NestedScrollPosition>(positions);}
}
可以看到_NestedScrollController是私有类,并且NestedScrollView从头到尾都没有暴露任何可以修改或替换_innerController的方法给我们,因此,想在外部直接修改是不可能的。怎么办呢?
首先,内层的每一个CustomScrollView都是我们在外部人为编写的,我们可以在外部给内层的每一个CustomScrollView重新指定ScrollController,虽然暂时没什么卵用😄,但是别着急。
class _TabInnerListViewState extends State<_TabInnerListView> with AutomaticKeepAliveClientMixin {final int length = 20;late ScrollController _scrollController;void initState() {super.initState();_scrollController = ScrollController();}void dispose() {_scrollController.dispose();super.dispose();}Widget build(BuildContext context) {return CustomScrollView(controller: _scrollController,physics: const ClampingScrollPhysics(),slivers: [...(List.generate(length, (index) {return SliverToBoxAdapter(child: Container(height: 100,margin: const EdgeInsets.only(top: 16, left: 16, right: 16),color: Colors.orange,alignment: Alignment.center,child: Text("${widget.tabName} item $index",style: const TextStyle(color: Colors.white),),),);})),const SliverToBoxAdapter(child: SizedBox(height: 16,),)],);}bool get wantKeepAlive => true;}

可以看到,此时嵌套滑动失效了,这是因为我们为内层的每一个CustomScrollView单独指定ScrollController后,CustomScrollView的滑动全部交给了这个这个ScrollController处理,跟NestedScrollView的_innerController已经没有半毛钱关系了。既然没有关系,那我们就建立关系,怎么建立:
-
创建NestedInnerScrollController类继承ScrollController
-
重写createScrollPosition方法,通过PrimaryScrollController.maybeOf(context)获取NestedScrollView的_innerController,将createScrollPosition转交给_innerController完成
-
重写attach方法,将attach转交给_innerController完成
-
重写detach方法,将detach转交给_innerController完成
-
为每一个内层的CustomScrollView指定controller为NestedInnerScrollController的实例
class NestedInnerScrollController extends ScrollController {ScrollController? _inner;NestedInnerScrollController();ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) {ScrollPosition scrollPosition;ScrollableState? scrollableState = context as ScrollableState;if(scrollableState != null) {_inner = PrimaryScrollController.maybeOf(scrollableState.context);}if(_inner == null) {scrollPosition = super.createScrollPosition(physics, context, oldPosition);} else {scrollPosition = _inner!.createScrollPosition(physics, context, oldPosition);}return scrollPosition;}void attach(ScrollPosition position) {super.attach(position);_inner?.attach(position);}void detach(ScrollPosition position) {_inner?.detach(position);super.detach(position);}}class _TabInnerListViewState extends State<_TabInnerListView> with AutomaticKeepAliveClientMixin {final int length = 20;late ScrollController _scrollController;void initState() {super.initState();_scrollController = NestedInnerScrollController();}void dispose() {_scrollController.dispose();super.dispose();}Widget build(BuildContext context) {return CustomScrollView(controller: _scrollController,physics: const ClampingScrollPhysics(),slivers: [...(List.generate(length, (index) {return SliverToBoxAdapter(child: Container(height: 100,margin: const EdgeInsets.only(top: 16, left: 16, right: 16),color: Colors.orange,alignment: Alignment.center,child: Text("${widget.tabName} item $index",style: const TextStyle(color: Colors.white),),),);})),const SliverToBoxAdapter(child: SizedBox(height: 16,),)],);}bool get wantKeepAlive => true;}
可以看到,嵌套滑动它又回来了😄。那么接下来…,就只剩下解决共享滑动了:
-
将TabBarView单独定义成StatefulWidget,这样我们就可以很方便的为每一个内层的CustomScrollView维护上面定义好的NestedInnerScrollController,同时通过TabController监听TabBar的选中状态。
class NestedTabBarView extends StatefulWidget {final TabController? controller;final List<Widget> children;final ScrollPhysics? physics;final DragStartBehavior dragStartBehavior;final double viewportFraction;final Clip clipBehavior;const NestedTabBarView({super.key,required this.children,this.controller,this.physics,this.dragStartBehavior = DragStartBehavior.start,this.viewportFraction = 1.0,this.clipBehavior = Clip.hardEdge,});State<StatefulWidget> createState() => _NestedTabBarViewState(); }class _NestedTabBarViewState extends State<NestedTabBarView> {List<NestedInnerScrollController> _nestedInnerControllers = [];void initState() {super.initState();_initNestedInnerControllers();widget.controller?.addListener(_onTabChange);}void didUpdateWidget(covariant NestedTabBarView oldWidget) {super.didUpdateWidget(oldWidget);if(oldWidget.children.length != widget.children.length) {_initNestedInnerControllers();}}void dispose() {widget.controller?.removeListener(_onTabChange);_disposeNestedInnerControllers();super.dispose();}void _onTabChange() {}void _initNestedInnerControllers() {_disposeNestedInnerControllers();List<NestedInnerScrollController> controllers = List.generate(widget.children.length, (index) {return NestedInnerScrollController();});if(mounted) {setState(() {_nestedInnerControllers = controllers;});} else {_nestedInnerControllers = controllers;}}void _disposeNestedInnerControllers() {_nestedInnerControllers.forEach((element) {element.dispose();});}Widget build(BuildContext context) {return TabBarView(controller: widget.controller,physics: widget.physics,dragStartBehavior: widget.dragStartBehavior,viewportFraction: widget.viewportFraction,clipBehavior: widget.clipBehavior,children: widget.children);} } -
使用InheritedWidget,将NestedInnerScrollController暴露给对应的内层CustomScrollView使用
class _InheritedInnerScrollController extends InheritedWidget {final ScrollController controller;const _InheritedInnerScrollController({required super.child,required this.controller});bool updateShouldNotify(covariant _InheritedInnerScrollController oldWidget) => controller != oldWidget.controller;}class _NestedTabBarViewState extends State<NestedTabBarView> {...Widget build(BuildContext context) {return TabBarView(controller: widget.controller,physics: widget.physics,dragStartBehavior: widget.dragStartBehavior,viewportFraction: widget.viewportFraction,clipBehavior: widget.clipBehavior,children: List<Widget>.generate(widget.children.length, (index) {return _InheritedInnerScrollController(controller: _nestedInnerControllers[index],child: widget.children[index],);}));} }class NestedInnerScrollController extends ScrollController {...static ScrollController of(BuildContext context) {final _InheritedInnerScrollController? target = context.dependOnInheritedWidgetOfExactType<_InheritedInnerScrollController>();assert(target != null,'NestedInnerScrollController.of must be called with a context that contains a NestedTabBarView\'s children.',);return target!.controller;}static ScrollController? maybeOf(BuildContext context) {final _InheritedInnerScrollController? target = context.dependOnInheritedWidgetOfExactType<_InheritedInnerScrollController>();return target?.controller;}}class _TabInnerListViewState extends State<_TabInnerListView> with AutomaticKeepAliveClientMixin {final int length = 20;Widget build(BuildContext context) {return CustomScrollView(controller: NestedInnerScrollController.maybeOf(context),physics: const ClampingScrollPhysics(),slivers: [...],);}bool get wantKeepAlive => true;}使用的时候
NestedScrollView(headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {return [...];},body: LayoutBuilder(builder: (context, _) {return Container(padding: EdgeInsets.only(top: NestedScrollView.sliverOverlapAbsorberHandleFor(context).layoutExtent ?? 0),child: NestedTabBarView(controller: _tabController,children: List.generate(_tabs.length, (index) {return _TabInnerListView(tabName: _tabs[index],);})));}) ) -
监听TabBar的选中状态,然后通过NestedInnerScrollController将NestedScrollView的_innerController中所有的ScrollPosition detach,然后再attach与当前选中的NestedInnerScrollController对应的ScrollPosition。
class NestedInnerScrollController extends ScrollController {...void attachCurrent() {if(_inner != null) {while(_inner!.positions.isNotEmpty) {_inner!.detach(_inner!.positions.first);}_inner!.attach(position);}}}class _NestedTabBarViewState extends State<NestedTabBarView> {...void _onTabChange() {int index = widget.controller!.index;if (index == widget.controller!.animation?.value) {_nestedInnerControllers[index].attachCurrent();}}... }
搞定。
三.完整代码
class _NestedScrollPageState extends State<NestedScrollPage> with TickerProviderStateMixin {final List<String> _tabs = ["tab1", "tab2", "tab3"];late TabController _tabController;void initState() {super.initState();_tabController = TabController(length: _tabs.length, vsync: this);}void dispose() {_tabController.dispose();super.dispose();}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("nested scroll"),),body: SafeArea(child: NestedScrollView(headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {return [SliverToBoxAdapter(child: Container(height: 200,color: Colors.red,alignment: Alignment.center,child: const Text("banner",style: TextStyle(color: Colors.white,fontSize: 16),),),),SliverOverlapAbsorber(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),sliver: StickySliverToBoxAdapter(child: Container(color: Colors.white,child: TabBar(tabs: List.generate(_tabs.length, (index) {return Padding(padding: const EdgeInsets.symmetric(vertical: 15),child: Text(_tabs[index]),);}),unselectedLabelColor: const Color(0xFF7B7B7B),labelColor: const Color(0xFF5E80FF),isScrollable: false,indicatorSize: TabBarIndicatorSize.label,indicator: UnderlineTabIndicator(borderRadius: BorderRadius.circular(3),borderSide: const BorderSide(color: Color(0xFF5E80FF), width: 3),insets: const EdgeInsets.symmetric(horizontal: 3, vertical: 9)),controller: _tabController,),),),),];},body: LayoutBuilder(builder: (context, _) {return Container(padding: EdgeInsets.only(top: NestedScrollView.sliverOverlapAbsorberHandleFor(context).layoutExtent ?? 0),child: NestedTabBarView(controller: _tabController,children: List.generate(_tabs.length, (index) {return _TabInnerListView(tabName: _tabs[index],);})));}))),);}}class _TabInnerListView extends StatefulWidget {final String? tabName;const _TabInnerListView({this.tabName});State<StatefulWidget> createState() => _TabInnerListViewState();}class _TabInnerListViewState extends State<_TabInnerListView> with AutomaticKeepAliveClientMixin {final int length = 20;Widget build(BuildContext context) {return CustomScrollView(controller: NestedInnerScrollController.maybeOf(context),physics: const ClampingScrollPhysics(),slivers: [...(List.generate(length, (index) {return SliverToBoxAdapter(child: Container(height: 100,margin: const EdgeInsets.only(top: 16, left: 16, right: 16),color: Colors.orange,alignment: Alignment.center,child: Text("${widget.tabName} item $index",style: const TextStyle(color: Colors.white),),),);})),const SliverToBoxAdapter(child: SizedBox(height: 16,),)],);}bool get wantKeepAlive => true;}
class NestedTabBarView extends StatefulWidget {final TabController? controller;final List<Widget> children;final ScrollPhysics? physics;final DragStartBehavior dragStartBehavior;final double viewportFraction;final Clip clipBehavior;const NestedTabBarView({super.key,required this.children,this.controller,this.physics,this.dragStartBehavior = DragStartBehavior.start,this.viewportFraction = 1.0,this.clipBehavior = Clip.hardEdge,});State<StatefulWidget> createState() => _NestedTabBarViewState();
}class _NestedTabBarViewState extends State<NestedTabBarView> {List<NestedInnerScrollController> _nestedInnerControllers = [];void initState() {super.initState();_initNestedInnerControllers();widget.controller?.addListener(_onTabChange);}void didUpdateWidget(covariant NestedTabBarView oldWidget) {super.didUpdateWidget(oldWidget);if(oldWidget.children.length != widget.children.length) {_initNestedInnerControllers();}}void dispose() {widget.controller?.removeListener(_onTabChange);_disposeNestedInnerControllers();super.dispose();}void _onTabChange() {int index = widget.controller!.index;if (index == widget.controller!.animation?.value) {_nestedInnerControllers[index].attachCurrent();}}void _initNestedInnerControllers() {_disposeNestedInnerControllers();List<NestedInnerScrollController> controllers = List.generate(widget.children.length, (index) {return NestedInnerScrollController();});if(mounted) {setState(() {_nestedInnerControllers = controllers;});} else {_nestedInnerControllers = controllers;}}void _disposeNestedInnerControllers() {_nestedInnerControllers.forEach((element) {element.dispose();});}Widget build(BuildContext context) {return TabBarView(controller: widget.controller,physics: widget.physics,dragStartBehavior: widget.dragStartBehavior,viewportFraction: widget.viewportFraction,clipBehavior: widget.clipBehavior,children: List<Widget>.generate(widget.children.length, (index) {return _InheritedInnerScrollController(controller: _nestedInnerControllers[index],child: widget.children[index],);}));}
}class _InheritedInnerScrollController extends InheritedWidget {final ScrollController controller;const _InheritedInnerScrollController({required super.child,required this.controller});bool updateShouldNotify(covariant _InheritedInnerScrollController oldWidget) => controller != oldWidget.controller;}class NestedInnerScrollController extends ScrollController {ScrollController? _inner;NestedInnerScrollController();ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) {ScrollPosition scrollPosition;ScrollableState? scrollableState = context as ScrollableState;if(scrollableState != null) {_inner = PrimaryScrollController.maybeOf(scrollableState.context);}if(_inner == null) {scrollPosition = super.createScrollPosition(physics, context, oldPosition);} else {scrollPosition = _inner!.createScrollPosition(physics, context, oldPosition);}return scrollPosition;}void attach(ScrollPosition position) {super.attach(position);_inner?.attach(position);}void detach(ScrollPosition position) {_inner?.detach(position);super.detach(position);}void attachCurrent() {if(_inner != null) {while(_inner!.positions.isNotEmpty) {_inner!.detach(_inner!.positions.first);}_inner!.attach(position);}}static ScrollController of(BuildContext context) {final _InheritedInnerScrollController? target = context.dependOnInheritedWidgetOfExactType<_InheritedInnerScrollController>();assert(target != null,'NestedInnerScrollController.of must be called with a context that contains a NestedTabBarView\'s children.',);return target!.controller;}static ScrollController? maybeOf(BuildContext context) {final _InheritedInnerScrollController? target = context.dependOnInheritedWidgetOfExactType<_InheritedInnerScrollController>();return target?.controller;}}
相关文章:
07_Flutter使用NestedScrollView+TabBarView滚动位置共享问题修复
07_Flutter使用NestedScrollViewTabBarView滚动位置共享问题修复 一.案发现场 可以看到,上图中三个列表的滑动位置共享了,滑动其中一个列表,会影响到另外两个,这显然不符合要求,先来看下布局,再说明产生这个…...
Java解决垂直鉴权问题(对垂直权限进行校验)
Java解决垂直鉴权问题(对垂直权限进行校验) 文章目录 Java解决垂直鉴权问题(对垂直权限进行校验)前言一、垂直鉴权是什么?二、实现过程1.新建接口权限菜单映射表2.项目初始化时加载接口菜单映射关系3.自定义过滤器拦截…...
【MySQL工具】pt-heartbeat
功能 pt-heartbeat - 监控 MySQL 复制延迟。 用法 pt-heartbeat [OPTIONS] [DSN] --update|--monitor|--check|--stop pt-heartbeat 用于测量 MySQL 或 PostgreSQL 服务器上的复制延迟。您可以使用它来更新主服务器或监控从服务器。如果可能,MySQL 连接选项将从您…...
实现vant的年月日时分秒组件
方法:van-datetime-picker(type:datetime)和 van-picker结合实现。 <template><div class"datetimesec-picker"><van-datetime-pickerref"timePickerRef"type"datetime" //年月日时…...
typescript 命名空间、装饰器
1、命名空间 命名空间:在代码量较大的情况下,为了避免各种变量命名的冲突,可将相似功能的函数、类、接口等放置到命名空间内。同Java的包.Net的命名空间一样,typescript 的命名空间可以将代码包裹起来,只对外暴露需要在…...
GPT问答SAP BW
以下回答由GPT-3.5回答,仅供参考. 这个AI工具超好用,每天都有免费额度,写文章、总结长视频、画图等,都几秒搞定!快去下载Sider Chrome或Edge插件,薅羊毛! https://sider.ai/invited?c43b289bf2616575daecf…...
使用zdppy_amauth开发激活用户接口
服务端代码: 1、创建数据库连接对象2、初始化数据库3、声明一个上下文4、挂载用户相关的路由,这里主要由 用户登录接口用户注册注册获取用户列表接口激活指定用户接口 5、启动服务 import mcrud import api import amauth import env import contextli…...
c++ memset 指针示例
目录 C 传一个float指针,在函数内部修改指针的值 c memset 指针示例 C 传一个float指针,在函数内部修改指针的值 #include <iostream>// 定义一个函数,它接受一个指向float的指针 void modifyValue(float* ptr) {// 通过解引用指针来…...
24考研双非上岸武汉理工大学电子信息专硕,855考研经验
目录 一、考研择校经验 二、武理考研初试经验 三、武理考研复试经验 一、考研择校经验 我建议学弟学妹们确定院校时没必要一上来就说我一定要考某个院校。其实考哪个学校是要在考研备考的过程中慢慢探索,慢慢研究的,不过最晚9月初一定要确定院校了&a…...
使用KubeKey 快速交付k8s v1.28.8集群
文章目录 服务器配置使用kubekey部署k8s1. 操作系统基础配置2. 安装部署 K8s2.1 下载 KubeKey2.2 创建 K8s 集群部署配置文件 3. 验证 K8s 集群3.1 验证集群状态 4. 部署测试资源5.验证服务 服务器配置 主机名IPCPU内存系统盘数据盘用途vm-16-11-ubuntu192.168.9.131128256Gi5…...
nginx--压缩https证书favicon.iconginx隐藏版本号 去掉nginxopenSSL
压缩功能 简介 Nginx⽀持对指定类型的⽂件进行压缩然后再传输给客户端,而且压缩还可以设置压缩比例,压缩后的文件大小将比源文件显著变小,这样有助于降低出口带宽的利用率,降低企业的IT支出,不过会占用相应的CPU资源…...
通俗的理解网关的概念的用途(四):什么是网关设备?(网络层面)
任何一台Windows XP操作系统之后的个人电脑、Linux操作系统电脑都可以简单的设置,就可以成为一台具备“网关”性质的设备,因为它们都直接内置了其中的实现程序。MacOS有没有就不知道,因为没用过。 简单的理解,就是运行了具备第二…...
Spring JdbcTemplate实现自定义动态sql拼接功能
需求描述: sql 需要能满足支持动态拼接,包含 查询字段、查询表、关联表、查询条件、关联表的查询条件、排序、分组、去重等 实现步骤: 1,创建表及导入测试数据 CREATE TABLE YES_DEV.T11 (ID BINARY_BIGINT NOT NULL,NAME VARCH…...
第十一篇:操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲
操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲 1 引言 在数字化的浪潮中,操作系统如同一位智慧的舵手,引领着信息技术的航船穿越波涛汹涌的海洋。随着人工智能、物联网、量子计算等前沿技术的蓬勃发展,操作系统正站在一个…...
【大数据】学习笔记
文章目录 [toc]NAT配置IP配置SecureCRT配置PropertiesTerminal Java安装环境变量配置 Hadoop安装修改配置文件hadoop-env.shyarn-env.shslavescore-site.xmlhdfs-site.xmlmapred-site.xmlyarn-site.xml 环境变量配置 IP与主机名映射关系配置hostname配置映射关系配置 关闭防火墙…...
PHP 框架安全:ThinkPHP 序列 漏洞测试.
什么是 ThinkPHP 框架. ThinkPHP 是一个流行的国内 PHP 框架,它提供了一套完整的安全措施来帮助开发者构建安全可靠的 web 应用程序。ThinkPHP 本身不断更新和改进,以应对新的安全威胁和漏洞。 目录: 什么是 ThinkPHP 框架. ThinkPHP 框架…...
厂家自定义 Android Ant编译流程源码分析
0、Ant安装 Windows下安装Ant: ant 官网可下载 http://ant.apache.org ant 环境配置: 解压ant的包到本地目录。 在环境变量中设置ANT_HOME,值为你的安装目录。 把ANT_HOME/bin加到你系统环境的path。 Ubuntu下安装Ant: sudo apt…...
基于springboot+vue+Mysql的体质测试数据分析及可视化设计
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
uniapp的app端推送功能,不使用unipush
1:推送功能使用htmlPlus实现:地址HTML5 API Reference (html5plus.org) 效果图: 代码实现: <template><view class"content"><view class"text-area"><button click"createMsg&q…...
数据结构(四)————二叉树和堆(中)
制作不易,三连支持一下呗!!! 文章目录 前言一、堆的概念及结构二、堆的实现三.堆的应用 总结 前言 CSDN 这篇博客介绍了二叉树中的基本概念和存储结构,接下来我们将运用这些结构来实现二叉树 一、堆的概念及结构 1…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
