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

flutter学习-day16-自定义组件

📚 目录

  1. 介绍
  2. 组合多个组件
  3. 自绘组件
    1. Custompaint
    2. 绘制边界RepaintBoundary
    3. CustomPainter与Canvas
    4. 画笔Paint
    5. 绘制组件例子

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

1. 介绍

当Flutter提供的现有组件无法满足我们的需求,或者我们为了共享代码需要封装一些通用组件,这时我们就需要自定义组件。在Flutter中自定义组件有三种方式:通过组合其他组件、自绘和实现RenderObject。

  • 组合多个Widget:通过已有组件来拼装组合成一个新的组件。
  • 通过CustomPaint自绘:通过Flutter中提供的CustomPaint和Canvas来实现UI自绘。
  • 通过RenderObject自绘:RenderObject中最终也是通过Canvas API来绘制的。而CustomPaint只是为了方便开发者封装的一个代理类。

2. 组合多个组件

实现一个渐变色背景,支持圆角,按下有涟漪效果的按钮,并且实现一个带动画的可以旋转的容器,每次点击按钮,容器旋转一点。

import 'package:flutter/material.dart';/// 定义
class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}/// 实现
class HomePageState extends State<HomePage> {double myTurns = 0.0;Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Flutter Home'),),body: Container(alignment: Alignment.center,child: Column(children: [TurnBox(turns: myTurns,speed: 500,child: const Icon(Icons.refresh,size: 50,)),MyButton(colors: const [Colors.green, Colors.orangeAccent],height: 60.0,width: 300.0,borderRadius: const BorderRadius.all(Radius.circular(8)),onPressed: () {setState(() {myTurns += 0.2;});},child: const Text('放手一搏'),)],),));}
}
/// 自定义按钮
class MyButton extends StatelessWidget {const MyButton({Key? key,this.colors,this.width,this.height,this.onPressed,this.borderRadius,required this.child}): super(key: key);// 渐变色数组final List<Color>? colors;// 按钮属性final double? width;final double? height;final BorderRadius? borderRadius;// 点击回调事件final GestureTapCallback? onPressed;final Widget child;Widget build(BuildContext context) {ThemeData theme = Theme.of(context);List<Color> myColors =colors ?? [theme.primaryColor, theme.primaryColorDark];return DecoratedBox(decoration: BoxDecoration(gradient: LinearGradient(colors: myColors),borderRadius: borderRadius),child: Material(type: MaterialType.transparency,child: InkWell(splashColor: Colors.white70,highlightColor: Colors.transparent,borderRadius: borderRadius ?? BorderRadius.circular(8),onTap: onPressed,child: ConstrainedBox(constraints: BoxConstraints.tightFor(height: height, width: width),child: Center(child: Padding(padding: const EdgeInsets.all(8.0),child: DefaultTextStyle(style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18.0),child: child,),),),),),),);}
}/// 自定义旋转
class TurnBox extends StatefulWidget {const TurnBox({Key? key,this.turns = .0, // 旋转的“圈”数,一圈为360度,如0.25圈即90度this.speed = 200, // 过渡动画执行的总时长required this.child}) :super(key: key);final double turns;final int speed;final Widget child;TurnBoxState createState() => TurnBoxState();
}class TurnBoxState extends State<TurnBox> with SingleTickerProviderStateMixin {AnimationController? myController;void initState() {super.initState();myController = AnimationController(vsync: this,lowerBound: -double.infinity,upperBound: double.infinity);myController!.value = widget.turns;}void dispose() {myController!.dispose();super.dispose();}Widget build(BuildContext context) {return RotationTransition(turns: myController!,child: widget.child,);}void didUpdateWidget(TurnBox oldWidget) {super.didUpdateWidget(oldWidget);// 旋转角度发生变化时执行过渡动画if (oldWidget.turns != widget.turns) {myController!.animateTo(widget.turns,duration: Duration(milliseconds: widget.speed??200),curve: Curves.easeOut,);}}
}

3. 自绘组件

对于一些复杂或不规则的UI,我们可能无法通过组合其他组件的方式来实现。比如一个正六边形、一个渐变的圆形进度条、一个棋盘等。在Flutter中,提供了一个CustomPaint 组件,它可以结合画笔CustomPainter来实现自定义图形绘制。

3-1. CustomPaint

画笔CustomPainter绘制时我们需要提供前景或背景画笔,两者也可以同时提供。我们的画笔需要继承CustomPainter类,我们在画笔类中实现真正的绘制逻辑。

属性描述
painter背景画笔,会显示在子节点后面
foregroundPainter前景画笔,会显示在子节点前面
size当child为null时,代表默认绘制区域大小,如果有child则忽略此参数,画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小,可以使用SizeBox包裹CustomPaint实现。
isComplex是否复杂的绘制,如果是,Flutter会应用一些缓存策略来减少重复渲染的开销。
willChange和isComplex配合使用,当启用缓存时,该属性代表在下一帧中绘制是否会改变。

3-2. 绘制边界RepaintBoundary

如果CustomPaint有子节点,为了避免子节点不必要的重绘并提高性能,通常情况下都会将子节点包裹在RepaintBoundary组件中,这样会在绘制时就会创建一个新的绘制层(Layer),其子组件将在新的Layer上绘制,而父组件将在原来Layer上绘制,也就是说RepaintBoundary 子组件的绘制将独立于父组件的绘制,RepaintBoundary会隔离其子节点和CustomPaint本身的绘制边界。

CustomPaint(// 指定画布大小size: Size(300, 300),painter: MyPainter(),child: RepaintBoundary(child:...), 
)

3-3. CustomPainter与Canvas

CustomPainter中提定义了一个虚函数paint。它有两个参数,Canvas和Size。Canvas是Flutter中绘制UI的底层组件,它是一个画布,包括各种绘制方法。Size是当前绘制区域大小。Canvas常用API如下:

API描述
drawLine画线
drawPoint画点
drawPath画路径
drawImage画图像
drawRect画矩形
drawCircle画圆
drawOval画椭圆
drawArc画圆弧

3-4. 画笔Paint

Flutter提供了Paint类来实现画笔。在Paint中,我们可以配置画笔的各种属性如粗细、颜色、样式等。如下例子:

// 创建一个画笔并配置其属性
var paint = Paint()..isAntiAlias = true // 是否抗锯齿..style = PaintingStyle.fill // 画笔样式:填充..color = Color(0x77cdb175); // 画笔颜色

3-5. 绘制组件例子

如下,是一个自定义绘制的饼状图。

  • 饼图完整代码
import 'dart:math';
import 'package:flutter/material.dart';typedef PieChartViewTap = Function(int index);
typedef OutsideText = Text Function(PieChartModel model, String scale);class PieChartView extends ImplicitlyAnimatedWidget {final List<PieChartModel> models;/// 是否显示内部圆final bool isShowHole;/// 内部圆的半径final double holeRadius;/// 内部圆的颜色final Color holeColor;/// 扇形分割线宽度final double spaceWidth;/// 溢出上方文字final OutsideText? outsideTopText;/// 溢出下方文字final OutsideText? outsideBottomText;/// 扇形点击事件final PieChartViewTap? onTap;const PieChartView(this.models, {Key? key,this.holeRadius = 55.0,this.isShowHole = true,this.holeColor = Colors.white,this.spaceWidth = 2.0,this.outsideTopText,this.outsideBottomText,this.onTap,Curve curve = Curves.linear,Duration duration = const Duration(milliseconds: 150),}) : super(key: key,curve: curve,duration: duration,);CustomPieViewState createState() => CustomPieViewState();
}class CustomPieViewState extends AnimatedWidgetBaseState<PieChartView> {CustomPieTween? customPieTween;List<PieChartModel> get end => widget.models.map((e) => PieChartModel(value: e.value, color: e.color, name: e.name, radius: e.radius)).toList();Widget build(BuildContext context) {return CustomPaint(size: Size.infinite,painter: PieChartPainter(context,customPieTween!.evaluate(animation),holeRadius: widget.holeRadius,isShowHole: widget.isShowHole,holeColor: widget.holeColor,spaceWidth: widget.spaceWidth,outsideTopText: widget.outsideTopText,outsideBottomText: widget.outsideBottomText,onTap: widget.onTap,),);}void forEachTween(TweenVisitor<dynamic> visitor) {customPieTween = visitor(customPieTween, end, (dynamic value) {return CustomPieTween(begin: value, end: end);}) as CustomPieTween;}
}class CustomPieTween extends Tween<List<PieChartModel>> {CustomPieTween({List<PieChartModel>? begin, List<PieChartModel>? end}): super(begin: begin, end: end);List<PieChartModel> lerp(double t) {List<PieChartModel> list = [];begin?.asMap().forEach((index, model) {list.add(model..radius = lerpDouble(model.radius, end?[index].radius ?? 100.0, t));});return list;}double lerpDouble(double radius, double radius2, double t) {if (radius == radius2) {return radius;}var d = (radius2 - radius) * t;var value = radius + d;return value;}
}class PieChartPaint extends CustomPaint {const PieChartPaint({Key? key}) : super(key: key);
}class PieChartPainter extends CustomPainter {final BuildContext context;final List<PieChartModel> models;final bool isShowHole;final double holeRadius;final Color holeColor;final double spaceWidth;final OutsideText? outsideTopText;final OutsideText? outsideBottomText;final PieChartViewTap? onTap;final List<Path> paths = [];final Path holePath = Path();Offset oldTapOffset = Offset.zero;PieChartPainter(this.context,this.models, {this.holeRadius = 60.0,this.isShowHole = true,this.holeColor = Colors.white,this.spaceWidth = 2.0,this.outsideTopText,this.outsideBottomText,this.onTap,});void paint(Canvas canvas, Size size) {//移动到中心点canvas.translate(size.width / 2, size.height / 2);//绘制饼状图_drawPie(canvas, size);//绘制分割线_drawSpaceLine(canvas);// 绘制中心圆_drawHole(canvas, size);// drawLineAndText(canvas);}bool shouldRepaint(CustomPainter oldDelegate) => oldDelegate != this;bool? hitTest(Offset position) {return _interceptTouchEvent(position);}bool _interceptTouchEvent(Offset offset) {if (oldTapOffset.dx == offset.dx && oldTapOffset.dy == offset.dy) {return false;}oldTapOffset = offset;for (int i = 0; i < paths.length; i++) {if (paths[i].contains(offset) && !holePath.contains(offset)) {onTap?.call(i);oldTapOffset = offset;return true;}}onTap?.call(-1);return false;}/// 绘制分割线void _drawSpaceLine(Canvas canvas) {var sumValue = models.fold<double>(0.0, (sum, model) => sum + model.value);var startAngle = 0.0;for (var model in models) {_drawLine(canvas, startAngle, model.radius);startAngle += model.value / sumValue * 360;_drawLine(canvas, startAngle, model.radius);}}void _drawLine(Canvas canvas, double angle, double radius) {var endX = cos(angle * pi / 180) * radius;var endY = sin(angle * pi / 180) * radius;Paint paint = Paint()..style = PaintingStyle.fill..color = Colors.white..strokeWidth = spaceWidth;canvas.drawLine(Offset.zero, Offset(endX, endY), paint);}/// 绘制饼状图void _drawPie(Canvas canvas, Size size) {var startAngle = 0.0;var sumValue = models.fold<double>(0.0, (sum, model) => sum + model.value);for (var model in models) {Paint paint = Paint()..style = PaintingStyle.fill..color = model.color;var sweepAngle = model.value / sumValue * 360;canvas.drawArc(Rect.fromCircle(radius: model.radius, center: Offset.zero),startAngle * pi / 180, sweepAngle * pi / 180, true, paint);Path path = Path();var centerX = size.width / 2;var centerY = size.height / 2;path.addArc(Rect.fromCircle(radius: model.radius, center: Offset(centerX, centerY)),startAngle * pi / 180,sweepAngle * pi / 180);path.moveTo(centerX, centerY);path.lineTo(centerX + cos(startAngle * pi / 180) * model.radius,centerY + sin(startAngle * pi / 180) * model.radius);path.lineTo(centerX + cos((sweepAngle + startAngle) * pi / 180) * model.radius,centerY + sin((sweepAngle + startAngle) * pi / 180) * model.radius);paths.add(path);// 为每一个区域绘制延长线和文字_drawLineAndText(canvas, size, model.radius, startAngle, sweepAngle, model);startAngle += sweepAngle;}}/// 绘制延长线和文字void _drawLineAndText(Canvas canvas, Size size, double radius,double startAngle, double sweepAngle, PieChartModel model) {var ratio = (sweepAngle / 360.0 * 100).toStringAsFixed(2);var top = outsideTopText?.call(model, ratio) ??Text(model.name,style: const TextStyle(color: Colors.black38),);var topTextPainter = getTextPainter(top);var bottom = outsideBottomText?.call(model, ratio) ??Text("$ratio%",style: const TextStyle(color: Colors.black38),);var bottomTextPainter = getTextPainter(bottom);// 绘制横线// 计算开始坐标以及转折点的坐标var startX = radius * (cos((startAngle + (sweepAngle / 2)) * (pi / 180)));var startY = radius * (sin((startAngle + (sweepAngle / 2)) * (pi / 180)));var firstLine = radius / 5;var secondLine =max(bottomTextPainter.width, topTextPainter.width) + radius / 4;var pointX = (radius + firstLine) *(cos((startAngle + (sweepAngle / 2)) * (pi / 180)));var pointY = (radius + firstLine) *(sin((startAngle + (sweepAngle / 2)) * (pi / 180)));// 计算坐标在左边还是在右边// 并计算横线结束坐标// 如果结束坐标超过了绘制区域,则改变结束坐标的值var endX = 0.0;// 距离绘制边界的偏移量var marginOffset = 20.0;if (pointX - startX > 0) {endX = min(pointX + secondLine, size.width / 2 - marginOffset);secondLine = endX - pointX;} else {endX = max(pointX - secondLine, -size.width / 2 + marginOffset);secondLine = pointX - endX;}Paint paint = Paint()..style = PaintingStyle.fill..strokeWidth = 1..color = Colors.grey;// 绘制延长线canvas.drawLine(Offset(startX, startY), Offset(pointX, pointY), paint);canvas.drawLine(Offset(pointX, pointY), Offset(endX, pointY), paint);// 文字距离中间横线上下间距偏移量var offset = 4;var textWidth = bottomTextPainter.width;var textStartX = 0.0;textStartX = _calculateTextStartX(pointX, startX, textWidth, secondLine, textStartX, offset);bottomTextPainter.paint(canvas, Offset(textStartX, pointY + offset));textWidth = topTextPainter.width;var textHeight = topTextPainter.height;textStartX = _calculateTextStartX(pointX, startX, textWidth, secondLine, textStartX, offset);topTextPainter.paint(canvas, Offset(textStartX, pointY - offset - textHeight));// 绘制文字前面的小圆点paint.color = model.color;canvas.drawCircle(Offset(textStartX - 8, pointY - 4 - topTextPainter.height / 2),4,paint);}double _calculateTextStartX(double stopX, double startX, double w,double line2, double textStartX, int offset) {if (stopX - startX > 0) {if (w > line2) {textStartX = (stopX + offset);} else {textStartX = (stopX + (line2 - w));}} else {if (w > line2) {textStartX = (stopX - offset - w);} else {textStartX = (stopX - (line2 - w) - w);}}return textStartX;}TextPainter getTextPainter(Text text) {TextPainter painter = TextPainter(locale: Localizations.localeOf(context),maxLines: text.maxLines,textDirection: TextDirection.ltr,text: TextSpan(text: text.data,style: text.style,),);painter.layout();return painter;}/// 绘制中心圆void _drawHole(Canvas canvas, Size size) {if (isShowHole) {holePath.reset();Paint paint = Paint()..style = PaintingStyle.fill..color = Colors.white;canvas.drawCircle(Offset.zero, holeRadius, paint);var centerX = size.width / 2;var centerY = size.height / 2;holePath.addArc(Rect.fromCircle(radius: holeRadius, center: Offset(centerX, centerY)),0,360 * pi / 180);}}
}/// 数据
class PieChartModel {double value;Color color;String name;double radius;PieChartModel({required this.value,required this.color,required this.name,this.radius = 100,});
}
  • 使用
import 'package:flutter/material.dart';
import 'package:demo1/widget/MyPieChart.dart';/// 定义
class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}/// 实现
class HomePageState extends State<HomePage> {double myTurns = 0.0;Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Flutter Home'),),body: Container(alignment: Alignment.center,// 饼图child: PieChartView([PieChartModel(value: 35,name: 'A',color: Colors.blue,radius: 100,),PieChartModel(value: 15,name: 'B',color: Colors.red,radius: 100,),PieChartModel(value: 22,name: 'C',color: Colors.yellow,radius: 100,),PieChartModel(value: 18,name: 'D',color: Colors.orange,radius: 100,),PieChartModel(value: 39,name: 'F',color: Colors.green,radius: 100,),],),));}
}

本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

往期文章

  • 手把手教你搭建规范的团队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学习-day16-自定义组件

&#x1f4da; 目录 介绍组合多个组件自绘组件 Custompaint绘制边界RepaintBoundaryCustomPainter与Canvas画笔Paint绘制组件例子 本文学习和引用自《Flutter实战第二版》&#xff1a;作者&#xff1a;杜文 1. 介绍 当Flutter提供的现有组件无法满足我们的需求&#xff0c;或…...

XML简介 (EXtensible Markup Language)

XML简介 (EXtensible Markup Language) 可扩展标记语言 特点 XML与操作系统、编程语言的开发平台无关实现不同系统之间的数据交换 作用 数据交互配置应用程序和网站Ajax基石 XML标签 XML文档内容由一系列标签元素组成 <元素名 属性名"属性值">元素内容&l…...

基于Spring自动注入快速实现策略模式+工厂模式优化过多的if..else

一、策略模式 1.1策略模式定义 在策略模式&#xff08;Strategy Pattern&#xff09;中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式定义了一系列算法或策略&#xff0c;并将每个算法封装在独立的类中&#xff0c;使得它们可以互相…...

安装vcpkg管理opencv的安装+MFC缺失的解决

第一步&#xff0c;出现#include没有办法找到opencv头文件的问题&#xff0c;无法解决 在VC的提示下&#xff0c;安装了vcpkg&#xff0c;然后用vcpkg命令来帮助安装opencv&#xff0c;过程十分顺利。 1. cmd 到命令行窗口&#xff1b; 2. 建立src文件夹&#xff0c;并进入…...

了解树和学习二叉树

1.树 1.1 概念 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看 起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的 。 注意&#xff1a;树形结构中…...

Spring Boot学习随笔- 拦截器实现和配置(HandlerInterceptor、addInterceptors)、jar包部署和war包部署

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十三章、拦截器 拦截器 &#xff1a;Interceptor 拦截 中断 类似于javaweb中的Filter&#xff0c;不过没有Filter那么强大 作用 Spring MVC的拦截器是一种用于在请求处理过程中进行预处理和后处理的机制。拦…...

Pipelined-ADC设计二——结构指标及非理想因素(Part2)

接上文&#xff0c;本章将两个比较重要的非理想因素&#xff0c;因此各项指标制定。后续会对常见的非理想因素给出常见的解决方法&#xff0c;以及设计所采用的方法。 2.2.7. 比较器失调 在流水线 ADC 中&#xff0c;比较器的主要误差来源就是比较器失调&#xff0c;称为失调误…...

Ubuntu 常用命令之 clear 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 clear命令在Ubuntu系统下用于清除终端屏幕的内容。这个命令没有任何参数&#xff0c;它的主要作用就是清理终端屏幕上的所有信息&#xff0c;使得屏幕看起来像是新打开的一样。 使用clear命令非常简单&#xff0c;只需要在终端中…...

【JAVA面试题】什么是对象锁?什么是类锁?

&#x1f34e; 个人博客 &#xff1a;个 人 主 页 &#x1f3c6;个人专栏&#xff1a;多线程JAVA ⛳️ 功 不 唐 捐 &#xff0c;玉 汝 于 成 目录 前言 回答 对象锁&#xff08;Object Lock&#xff09;&#xff1a; 类锁&#xff08;Class Lock&#xff09;&#xff1…...

飞天使-k8s知识点5-kubernetes基础名词扫盲

文章目录 deploymentspodNodeserviceskubectl 实现应用伸缩kubectl 实现滚动更新kubernetes架构 deployments 中文文档 http://docs.kubernetes.org.cn/251.htmldeployment是用来创建和更新应用的&#xff0c;master 会负责将创建好的应用实例调度到集群中的各个节点 应用实例…...

【视觉实践】使用Mediapipe进行目标检测:杯子检测和椅子检测实践

目录 1 Mediapipe 2 Solutions 3 安装mediapipe 4 实践 1 Mediapipe Mediapipe是google的一个开源项目,可以提供开源的、跨平台的常用机器学习(machine learning,ML)方案。MediaPipe是一个用于构建机器学习管道</...

C++之深拷贝进阶

目录 拷贝构造函数的深拷贝进阶版本 赋值运算符重载的深拷贝进阶 总结 上期我们学习了C中深拷贝的传统版本&#xff0c;今天我们将学习更为高效的版本。 拷贝构造函数的深拷贝进阶版本 传统版本代码如下&#xff1a; string(string& s):_str(new char[strlen(s._str)…...

导行电磁波从纵向场分量求其他方向分量的矩阵表示

导行电磁波从纵向场分量求解其他方向分量的矩阵表示 导行电磁波传播的特点 电磁波在均匀、线性、各向同性的空间中沿着 z z z轴传播&#xff0c;可用分离变量法将时间轴、 z z z轴与 x , y x,y x,y轴分离&#xff0c;电磁波的形式可表示为&#xff1a; E ⃗ E ⃗ ( x , y )…...

融资项目——swagger2的注解

1. ApiModel与ApiModelProperty(在实体类中使用) 如上图&#xff0c;ApiModel加在实体类上方&#xff0c;用于整体描述实体类。ApiModelProperty(value"xxx",example"xxx")放于每个属性上方&#xff0c;用于对属性进行描述。swagger2网页上的效果如下图&am…...

【性能优化】MySql数据库查询优化方案

阅读本文你的收获 了解系统运行效率提升的整体解决思路和方向学会MySQl中进行数据库查询优化的步骤学会看慢查询、执行计划、进行性能分析、调优 一、问题&#xff1a;如果你的系统运行很慢&#xff0c;你有什么解决方案&#xff1f; ​关于这个问题&#xff0c;我们通常首先…...

Chrome浏览器http自动跳https问题

现象&#xff1a; Chrome浏览器访问http页面时有时会自动跳转https&#xff0c;导致一些问题。比如&#xff1a; 开发阶段访问dev环境网址跳https&#xff0c;后端还是http&#xff0c;导致接口跨域。 复现&#xff1a; 先访问http网址&#xff0c;再改成https访问&#xf…...

【C++进阶02】多态

一、多态的概念及定义 1.1 多态的概念 多态简单来说就是多种形态 同一个行为&#xff0c;不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如&#xff1a;函数重载 动态多态指的是运行时 在程序运行…...

PHP开发日志——循环和条件语句嵌套不同,效率不同(循环内加入条件语句,条件语句判断后加入循环,array_map函数中加入条件语句)

十多年前开发框架时&#xff0c;为了效率不断试过各种代码写法&#xff0c;今天又遇到了&#xff0c;想想php8时代会不会有所变化&#xff0c;结果其实也还是和当年一样&#xff0c;但当年没写博客&#xff0c;但现在可以把数据记录下来了。 PHP_loop_ireflies_dark_forest 项目…...

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制&#xff0c;在应用上下文启动时&#xff0c;会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…...

DBA-MySql面试问题及答案-上

文章目录 1.什么是数据库?2.如何查看某个操作的语法?3.MySql的存储引擎有哪些?4.常用的2种存储引擎&#xff1f;6.可以针对表设置引擎吗&#xff1f;如何设置&#xff1f;6.选择合适的存储引擎&#xff1f;7.选择合适的数据类型8.char & varchar9.Mysql字符集10.如何选择…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...