Flutter 事件传递简单概述、事件冒泡、事件穿透

前言

当前案例 Flutter SDK版本:3.13.2

本文对 事件传递只做 简单概述,主要讲解,事件传递过程中可能遇到的问题解决,比如 事件冒泡事件穿透;

不是我偷懒,是自认为没有这几位写的详细、仔细,非常建议先看完这几篇参考文档,不然下面讲解一些对象或者函数会不理解。

深入进阶-从一次点击探寻Flutter事件分发原理 - 掘金

Flutter分享:Flutter事件分发原理 - 掘金

8.3 Flutter事件机制 | 《Flutter实战·第二版》

8.4 手势原理与手势冲突 | 《Flutter实战·第二版》

Flutter事件传递简单概述

重要对象介绍

HitTestEntry:可以把它看成视图中的 手势监听组件,主要信息都在 target 属性中。

HitTestResult:翻译为 命中测试结果,重点是它的 _path 集合保存着 HitTestEntry 对象;

重要函数介绍

hitTest(result,position) 翻译为 命中测试手势监听组件 内部会调用的方法,如果返回true,会将当前 手势监听组件 也就是 HitTestEntry 加入 HitTestResult._path 集合中,这只是默认规则,可以手动添加

核心代码:result.add(BoxHitTestEntry(this, position)),将 HitTestEntry (手势监听组件) 加入 HitTestResult._path 集合中;

核心代码:`result.add(BoxHitTestEntry(this, position))`:将 `HitTestEntry` **(手势监听组件)** 加入 `HitTestResult._path` 集合中;

还有查找 监听组件的顺序,是由深到浅的查找,比如 父子结构查找顺序:子孙手势组件、子手势组件、父手势组件,其他传统布局查找顺序:兄弟手势组件03、兄弟手势组件02、兄弟手势组件01。

那这个 hitTest函数的 布尔值是不是没用了?当然有用,后面会讲解,先忽略

最开始执行的是 renderView.hitTest(result, position: position)renderView 表示 渲染树的根节点;

class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {bool hitTest(HitTestResult result, { required Offset position }) {// 这部分逻辑是父子结构的组件,才走的if (child != null) { child!.hitTest(BoxHitTestResult.wrap(result), position: position);}// 你手指触摸位置的那个 手势监听组件,加入 HitTestResult._path 集合中result.add(HitTestEntry(this)); return true;}}abstract class RenderBox extends RenderObject {// 父子结构的组件,走到这bool hitTest(BoxHitTestResult result, { required Offset position }) {   ... ...if (_size!.contains(position)) {if (hitTestChildren(result, position: position) || hitTestSelf(position)) {result.add(BoxHitTestEntry(this, position));return true;}}return false;}}

常用的手势监听组件

Listener组件

只监听最原始的几种事件,down ==> move ==> ... ==> move ==> up ==> cancel;

比如 第一次将手指放在屏幕上 触发 Down 事件,手指没有离开屏幕前,手指位置发生改变 触发 Move 事件,每次位置改变都会触发一次 Move 事件,手指离开屏幕时触发 Up事件,紧接着 触发 Cancel事件;

常用的一些手势,比如 单击、双击、长按 等等,它都识别不了,也不负责处理事件冲突

Listener(onPointerDown: (event) {debugPrint('onPointerDown');},child: Container(width: 100,height: 100,color: Colors.primaries[10],),
)

GestureDetector

对Listener的封装后的产物,内部加了很多 GestureRecognizer (手势识别器),每个识别器都代表一种手势监听,比如监听 单击、双击、长按、缩放 等等手势,以及可以通过自定义手势识别器解决事件冲突,所以一般都用它

GestureDetector(onTap: () {debugPrint('onTap');},child: Container(width: 100,height: 100,color: Colors.primaries[10],),
)
class GestureDetector extends StatelessWidget {... ... @overrideWidget build(BuildContext context) {... ...// TapGestureRecognizer 单击手势识别器gestures[TapGestureRecognizer] = ... ...// DoubleTapGestureRecognizer 双击手势识别器gestures[DoubleTapGestureRecognizer] = ... ...... ...return RawGestureDetector(... ...);}
}class RawGestureDetector extends StatefulWidget { ... ...@overrideRawGestureDetectorState createState() => RawGestureDetectorState();
}class RawGestureDetectorState extends State<RawGestureDetector> {... ... @overrideWidget build(BuildContext context) {Widget result = Listener( // 原始手势监听器... ... );... ...return result;}... ...}

InkWell

对GestureDetector的封装,加了点击时出现水波纹效果,我项目里基本不用这东西。

注意:它这个水波纹效果,实现位置是在 Child 下面,所以Child 颜色要为透明,不然看不见;

一般是通过 Material 组件设置背景色,来解决这个问题。

Material(color: Colors.greenAccent, // 设置背景色child: InkWell(onTap: () {debugPrint('onTap');},child: Container(width: 100,height: 100,),),
),
class InkWell extends InkResponse {... ...}class InkResponse extends StatelessWidget {... ...@overrideWidget build(BuildContext context) {... ...return _InkResponseStateWidget(... ...);}... ...}class _InkResponseStateWidget extends StatefulWidget {... ... @override_InkResponseState createState() => _InkResponseState();... ...}class _InkResponseState extends State<_InkResponseStateWidget> with AutomaticKeepAliveClientMixin<_InkResponseStateWidget> implements _ParentInkResponseState {... ... @overrideWidget build(BuildContext context) {... ...return _ParentInkResponseProvider(... ...child: GestureDetector( // 手势监听器... ...),),);}... ...}

事件传递过程

这个过程是我根据断点调试顺序构思的,如有错误,还请评论区留言,共勉。

默认传递过程

使用HitTestBehavior的传递过程

HitTestBehavior

翻译 命中测试行为,它不是一个对象,只是一个概念,让我们自己写 命中测试 逻辑,通过以下两个对象 实现。

RenderProxyBox:它是RenderObject的子类,可以重写 hitTest 命中测试函数,从而修改事件传递过程,RenderObject 属于 渲染树无法直接Widget树 中使用,需要包一层 SingleChildRenderObjectWidget。

SingleChildRenderObjectWidget:用来将 RenderObject 类型的组件,转换成 RenderObjectWidget,让其 可以在 Widget树中 使用;

会涉及到两个知识点:

  1. 事件中断机制;
  2. 还有 hitTest 命中测试函数 返回布尔值 有什么用;

我都写在代码注释里

如果你想自定义手势,建议去研究 GestureDetector 里的 GestureRecognizer (手势识别器),因为它用的最多,有的组件 甚至提供了 GestureRecognizer类型参数。

class MyListener extends SingleChildRenderObjectWidget {MyListener({super.key,this.downEventListener,this.hitTestBehavior = MyHitTestBehavior.normal,super.child});PointerDownEventListener? downEventListener;MyHitTestBehavior hitTestBehavior;@overrideRenderObject createRenderObject(BuildContext context) {return MyRenderListener(downEventListener: downEventListener, hitTestBehavior: hitTestBehavior);}@overridevoid updateRenderObject(BuildContext context, covariant MyRenderListener renderObject) {renderObject.downEventListener = downEventListener;renderObject.hitTestBehavior = hitTestBehavior;}
}class MyRenderListener extends MyRenderHitTestBehavior {MyRenderListener({this.downEventListener, super.hitTestBehavior});PointerDownEventListener? downEventListener;@overridevoid handleEvent(PointerEvent event, covariant HitTestEntry<HitTestTarget> entry) {if (event is PointerDownEvent) {return downEventListener?.call(event);}}}abstract class MyRenderHitTestBehavior extends RenderProxyBox {MyRenderHitTestBehavior({this.hitTestBehavior = MyHitTestBehavior.normal});MyHitTestBehavior hitTestBehavior;@overridebool hitTest(BoxHitTestResult result, {required Offset position}) {if(hitTestBehavior == MyHitTestBehavior.normal) { // 默认return super.hitTest(result, position: position);}if(hitTestBehavior == MyHitTestBehavior.ignore) {return false; // 强制命中测试失败}// 下面两个判断,区别在于 返回布尔值不一样// 同一容器内的 兄弟级别事件监听组件,只要有一个返回true,// 其他的都会返回false,这叫 事件中断机制,触发了这个机制,// 这些返回false的,将不参与 事件命中测试,即使加入了 HitTestResult.path 集合 也没用// 因为这些 事件监听组件的 handleEvent 没有触发// 注意:是触发了 中断机制 之后,这些返回false的 事件监听组件 才不参与 事件命中测试// 不是因为返回值是false,就不参与 事件命中测试,跟 false 没啥关系// 不触发 中断机制 的方法// 全部返回 false,这样只要在 HitTestResult.path 里的事件监听组件,都会被 分发事件if(hitTestBehavior == MyHitTestBehavior.opaque) {if(size.contains(position)) { // 点击的坐标,是否在 事件监听组件 范围内result.add(BoxHitTestEntry(this, position));return true; // 强制命中测试成功,会触发中断机制}}if (hitTestBehavior == MyHitTestBehavior.avoidInterruptions) {// 注意:这里我没有使用这个 范围判断,触发范围会变成 它父级组件 范围// if(size.contains(position))result.add(BoxHitTestEntry(this, position));return false; // 强制命中测试失败,不会触发中断机制}return false;}@overridebool hitTestSelf(Offset position) => super.hitTestSelf(position);// hitTestSelf函数 是父子结构组件 的判断条件 之一,你点开 super.hitTest(result, position: position);源码// 父子结构组件
// return Listener( // 父组件
//   ... ...
//   child: Container(
//    ... ...
//     child: Listener( // 子组件
//       ... ...
//       child: Container(
//         ... ...
//       ),
//     ),
//   ),
// );// super.hitTest(result, position: position); 源码:// 重点代码:如果子组件全都 命中测试失败,那就判断 hitTestSelf函数的 返回值
// if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
//    ... ...
// }// bool hitTest(BoxHitTestResult result, { required Offset position }) {
//   ... ...
//   if (_size!.contains(position)) {
//     if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
//       result.add(BoxHitTestEntry(this, position));
//       return true;
//     }
//   }
//   return false;
// }}enum MyHitTestBehavior {ignore, // 不参与 命中测试opaque, // 强制命中测试成功avoidInterruptions, // 避免触发中断机制normal  // 默认
}

 使用 MyListener

  Widget box(int index, double size) {return MyListener(// hitTestBehavior: MyHitTestBehavior.ignore, // 事件拦截hitTestBehavior: MyHitTestBehavior.avoidInterruptions, // 所有兄弟节点都会被分发事件downEventListener: (event) {debugPrint('index:$index');},child: Container(width: size,height: size,color: Colors.primaries[index],),);}@overrideWidget build(BuildContext context) {return Scaffold(body: SizedBox(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Container(color: Colors.greenAccent,width: 150,height: 400,child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [box(1, 100),box(2, 100),box(3, 100),box(4, 100),],),),],)),);}

事件冒泡

事件冒泡的产生原因

在父子结构组件中,父组件会先调用 hitTestChildren 方法,最后调用自身的 hitTest方法;
父组件判断自身是否 命中测试 的条件:只要有一个子组件的 hitTest 方法 返回true,父组件 hitTest方法 也会返回true,导致它会执行handleEvent方法,递归这个过程,就会产生事件冒泡

hitTestChildren(result, position):执行子组件的 hitTest 方法;

// 事件冒泡代码
Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Listener(onPointerDown: (event) {debugPrint('Parent --- onPointerDown');},child: Container(width: 300,height: 300,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[10],alignment: Alignment.center,child: Listener(onPointerDown: (event) {debugPrint('Child01 --- onPointerDown');},child: Container(width: 200,height: 200,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[8],alignment: Alignment.center,child: Listener(onPointerDown: (event) {debugPrint('Child02 --- onPointerDown');},child: Container(width: 100,height: 100,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[11],),))),),),],
)

解决方式一:通过变量判断

// 解决方式一:通过变量判断
Builder(builder: (context) {bool childEvent = false;return Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Listener(onPointerDown: (event) {if(!childEvent) {debugPrint('Parent --- onPointerDown');}childEvent = false;},child: Container(width: 300,height: 300,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[10],alignment: Alignment.center,child: Listener(onPointerDown: (event) {if(!childEvent) {debugPrint('Child01 --- onPointerDown');childEvent = true;}},child: Container(width: 200,height: 200,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[8],alignment: Alignment.center,child: Listener(onPointerDown: (event) {debugPrint('Child02 --- onPointerDown');childEvent = true;},child: Container(width: 100,height: 100,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[11],),))),),),],);}
),

解决方式二:使用GestureDetector

// 使用GestureDetector解决
// 注意一:
// 有参数的事件回调,还是会触发冒泡,比如onTapDown(details),以此类推
// onTap():可以防止冒泡,onTapDown(details)不可以;
// onDoubleTap():可以防止冒泡,onDoubleTapDown(details)不可以;
//
// 注意二:而且它俩都是up事件,手指离开屏幕时才会触发
// ... ...
Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [GestureDetector(onTap: () {debugPrint('Parent --- onPointerDown');},child: Container(width: 300,height: 300,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[10],alignment: Alignment.center,child: GestureDetector(onTap: () {debugPrint('Child01 --- onPointerDown');},child: Container(width: 200,height: 200,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[8],alignment: Alignment.center,child: GestureDetector(onTap: () {debugPrint('Child02 --- onPointerDown');},child: Container(width: 100,height: 100,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[11],),))),),),],
),

事件穿透

在叠加布局中,两个组件是位置相同相互覆盖,且两个都有事件,如何忽略盖在上面的组件事件,只触发底层的组件事件,这种场景出现的很少;

事件穿透应用场景:在叠加布局中,两个组件是位置相同相互覆盖,且两个都注册了事件监听器,如何忽略盖在上面的组件事件,只触发底层组件的事件;

这里介绍一下 IgnorePointer 和 AbsorbPointer 组件,它们的原理就是让这些组件不参与命中测试,从而做到事件拦截

  • IgnorePointer组件:包裹的组件,以及子组件、子孙后代组件,都不参与命中测试;
  • AbsorbPointer组件:包裹组件的 子组件、子孙后代组件 不参与命中测试,但不包括自身,点击子组件区域,还是会触发自身事件;

它俩都有一个是否启用的布尔值参数,默认为true,表示启用,可以通过变量动态操控;

使用IgnorePointer,包裹的组件事件被完全拦截,可以做到事件穿透的效果,反之AbsorbPointer不可以

// 在叠加布局中使用
Stack(alignment: Alignment.center,children: [Listener(onPointerDown: (event) {debugPrint('Child01 --- onPointerDown');},child: Container(width: 300,height: 300,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[10],),),// Listener(//     onPointerDown: (event) {//       debugPrint('Child02 --- onPointerDown');//     },//     child: IgnorePointer(//       child: Container(//         width: 200,//         height: 200,//         margin: const EdgeInsets.only(bottom: 12),//         color: Colors.primaries[8],//       ),//     )// ),// 或者这样写 都可以// 拦截当前组件事件,但同一位置的底层组件,会被触发,相当于穿透了IgnorePointer(child: Listener(onPointerDown: (event) {debugPrint('Child02 --- onPointerDown');},child: Container(width: 200,height: 200,margin: const EdgeInsets.only(bottom: 12),color: Colors.primaries[8],)),),// 拦截当前组件事件,但同一位置的底层组件无法触发,无法穿透// AbsorbPointer(//   child: Listener(//       onPointerDown: (event) {//         debugPrint('Child02 --- onPointerDown');//       },//       child: Container(//         width: 200,//         height: 200,//         margin: const EdgeInsets.only(bottom: 12),//         color: Colors.primaries[8],//       )//   ),// ),],
),

事件竞争

  • 当用户触摸屏幕时,可能同时触发好几种事件,这时候需要处理 事件冲突,确定哪一种 手势操作,Flutter提供了GestureArenaManager(手势竞技场)对象,将每一个手势当作一个竞选者,进行了筛选;

GestureArenaManager:官方视频:https://www.youtube.com/watch?v=Q85LBtBdi0U&t=469s


每个手势都有自己的判定条件,且每次竞争,只能有一个胜利者,举例:

  • 短按:手指按下 200毫秒
  • 长按:手指按下 500毫秒
  • ... ...

API 过时

以后要是找不到 hitTest 函数 就找 hitTestInView 函数

官方文档

gestures library - Dart API

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

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

相关文章

防火墙在解决方案及典型项目中的应用

防火墙在解决方案及典型项目中的应用 防火墙作为基础安全防护产品&#xff0c;在各种解决方案、业务场景中配套应用&#xff0c;本节给出各类方案资料链接方便查阅。 防火墙在华为网络解决方案中的应用 解决方案 文档 主要应用 CloudFabric云数据中心网解决方案 资料专区…

如何查看局域网内所有的ip和对应的mac地址

1、windows下查看 方法一、 按快捷键“winr”打开运行界面&#xff0c;输入“CMD”回车: 输入以下命令&#xff1a; for /L %i IN (1,1,254) DO ping -w 1 -n 1 192.168.0.%i 其中 192.168.0.%i 部分要使用要查询的网段&#xff0c;比如 192.168.1.%i 192.168.137.%i 172.16.2…

Protege的推理机

最近一直在使用Protege软件&#xff0c;来构建本体和知识图谱。其中还涉及到了如何指定规则进行推理&#xff0c;需要SWRL(使用这个插件能够比较简单创建规则)这个插件&#xff0c;但我一直找不到这个tab在哪里&#xff0c;但是我确信的是一定存在这个插件&#xff0c;因为prot…

vue-quill-editor和vue-ueditor-wrap富文本编辑器应用

目录 一、vue-quill-editor 1.1、界面展示 1.2、代码介绍 1.2.1、安装 1.2.2、配置 1.2.3、代码应用 1.2.4、提取内容 二、vue-ueditor-wrap 2.1、界面展示 2.2、代码介绍 2.2.1、安装 2.2.2、配置 2.2.3、代码应用 一、vue-quill-editor 1.1、界面展示 文本输出…

Mysql学习--深入探究索引和事务的重点要点与考点

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

CMake笔记之将任意官方库作为third_party完整地包含在工程项目中使用的通用模板

CMake笔记之将任意官方库作为third_party完整地包含在工程项目中使用的通用模板 —— 杭州 2024-03-20 凌晨1:06 code review! 文章目录 CMake笔记之将任意官方库作为third_party完整地包含在工程项目中使用的通用模板1.通用CMakeLists.txt模板2.GPT4给出的改进建议3.git clon…

【项目实践Day06】异步请求与同步请求+Ajax+微信小程序上实现发送异步请求

什么是同步和异步 同步 在主线程上排队执行的任务&#xff0c;只有前一个任务执行完毕&#xff0c;才能继续执行下一个任务。也就是一旦调用开始&#xff0c;就必须等待其返回结果&#xff0c;程序的执行顺序和任务排列顺序一致。客户端必须等待服务器端的响应。在等待的期间客…

继承和多态(1)(继承部分)

继承 继承的概念 上文就是继承的概念。 必须记住父类也可以称为基类&#xff0c;超类。 子类也可以称为派生类。 继承的语法 在Java中如果要表示类之间的继承关系&#xff0c;需要借助extends关键字&#xff0c;具体如下&#xff1a; 修饰符 class 子类 extends 父类 {//…

一、SpringBoot基础搭建

本教程主要给初学SpringBoot的开发者&#xff0c;通过idea搭建单体服务提供手把手教学例程&#xff0c;主要目的在于理解环境的搭建&#xff0c;以及maven模块之间的整合与调用 源码&#xff1a;jun/learn-springboot 以商城项目为搭建例子&#xff0c;首先计划建1个父模块&…

用 二层口 实现三层口 IP 通信的一个实现方法

我们一般用 undo portswitch 来将二层口转为三层口&#xff0c;但如果设备不支持的话&#xff0c;那么。。。 一、拓朴图&#xff1a; 二、实现方法&#xff1a; 起一个 vlan x&#xff0c;配置 vlanif地址&#xff0c;然后二层口划分到 vlan x 下&#xff0c;对端做同样的配置…

一文带你弄懂JVM与JAVA体系结构

文章目录 1.JVM 与 Java 体系结构1.1. 前言1.2. 一些参考书目1.3. Java 及 JVM 简介1.4. Java 发展的重大事件1.5. 虚拟机与 Java 虚拟机1.6. JVM 的整体结构1.7. Java 代码执行流程1.8. JVM 的架构模型1.9. JVM 的生命周期 1.JVM 与 Java 体系结构 1.1. 前言 作为 Java 工程…

用Compute Shader处理图像数据后在安卓机上不能正常显示渲染纹理

1&#xff09;用Compute Shader处理图像数据后在安卓机上不能正常显示渲染纹理 2&#xff09;折叠屏适配问题 3&#xff09;Prefab对DLL中脚本的引用丢失 4&#xff09;如何优化Unity VolumeManager中的ReplaceData 这是第378篇UWA技术知识分享的推送&#xff0c;精选了UWA社区…

长安链智能合约标准协议第二草案——BNS与DID协议邀请社区用户评审

长安链智能合约标准协议 在智能合约编写过程中&#xff0c;不同的产品及开发人员对业务理解和编程习惯不同&#xff0c;即使同一业务所编写的合约在具体实现上也可能有很大差异&#xff0c;在运维或业务对接中面临较大的学习和理解成本&#xff0c;现有公链合约协议规范又不能完…

RabbitMQ问题

如何实现顺序消费&#xff1f; 消息放入到同一个队列中消费 如何解决消息不丢失&#xff1f; 方案&#xff1a; 如上图&#xff1a;消息丢失有三种情况&#xff0c;解决了以上三种情况就解决了丢失的问题 1、丢失1--->消息在到达交换机的时候&#xff1b;解决&#xff1…

记录解决问题--activiti8.2 流程图图片由png改为svg前端不显示图片问题

1.说明 如果是vue svg显示&#xff0c;请查阅其他标准资料&#xff0c;类似使用svg标签。我这里讲的另外一种情况&#xff0c;链接返回的是svg文件&#xff0c;需要用v-html显示图片。 2.activiti6流程图图片格式 ①png格式。可以查看链接返回&#xff0c;以png开头。 ②前端…

C语⾔内存函数

目录 1. memcpy使⽤和模拟实现 memcpy函数的模拟实现: 2. memmove使⽤和模拟实现 memmove的模拟实现&#xff1a; 3. memset函数的使⽤ 4. memcmp函数的使⽤ 1. memcpy使⽤和模拟实现 void * memcpy ( void * destination, const void * source, size_t num ); • 函数me…

混境之地5

混境之地5 分析&#xff1a; 有一个二维矩阵代表的是a(i,j)的高度&#xff0c;给出一个起始坐标(a,b),以及一个终点坐标(c,d)&#xff0c;问是否能到终点坐标。要求是&#xff1a;只能从高的a(i,j)走到低的位置&#xff0c;有一次从低位置跳到高位置的机会。 我们优先想到的就…

[Uni-app] 微信小程序的圆环进度条

效果图&#xff1a; 组件完整代码如下&#xff1a; <template><view class"base-style":style"position: relative;width: diameter px;height: diameter px;display: flex;flex-direction: row;background-color: bgColor ;"><!…

解决:visio导出公式为pdf图片乱码问题

今天需要将Visio编辑好的以后的图输出pdf&#xff0c;但是点击保存后公式部分一直乱码&#xff0c;如下图所示 保存为pdf后会变成&#xff1a; 解决方案&#xff1a;保存时点击文件下方的快速打印&#xff0c;存到桌面&#xff0c;不要直接点击保存

SG5032VAN差分晶振X1G004261001100专用于5G通讯设备

差分晶体振荡器(DXO)是目前行业中公认高技术&#xff0c;高要求的一款晶体振荡器&#xff0c;是指输出差分信号使用2种相位彼此完全相反的信号,从而消除了共模噪声,并产生一个更高性能的系统。差分晶振一般为六脚贴片晶振&#xff0c;输出类型分为好几种,LVDS&#xff0c;LV-PE…