热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:在Flutter中编写自定义小部件(第1部分)ーEllipsizedText

篇首语:本文由编程笔记#小编为大家整理,主要介绍了在Flutter中编写自定义小部件(第1部分)ーEllipsizedText相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了在 Flutter 中编写自定义小部件(第1部分)ー EllipsizedText相关的知识,希望对你有一定的参考价值。






[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXrTwl3Y-1622423140772)(https://ducafecat.tech/2021/05/31/translation/writing-custom-widgets-in-flutter-part-1-ellipsizedtext/2021-05-31-08-55-16.png)]


老铁记得 转发 ,猫哥会呈现更多 Flutter 好文~~~~


微信 flutter 研修群 ducafecat


原文



https://rlesovyi.medium.com/writing-custom-widgets-in-flutter-part-1-ellipsizedtext-a0efdc1368a8



代码

https://github.com/MatrixDev/Flutter-CustomWidgets


正文

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jvwZQA1y-1622423140774)(https://ducafecat.tech/2021/05/31/translation/writing-custom-widgets-in-flutter-part-1-ellipsizedtext/2021-05-31-08-35-42.png)]

声明式用户界面在 Flutter 是相当不错,易于使用,它是非常诱人的使用尽可能。但是很多时候,开发人员只是做得太过火了ーー用声明的方式编写所有东西,即使有时候任务可以以更强制性的方式更有效、更容易理解。

每个人都应该明白的---- 在陈述和命令式编程之间必须有一个平衡。每种方法都有自己的用途,每种方法在某些任务上都比其他方法更加出色。

在本系列文章中,我将描述如何通过从头创建自定义小部件来解决不同的问题。每一个都比前一个稍微复杂一点。


思考

在查看代码之前,我们需要知道一些基本的事情。

Widget ー只是一个不可变的(最好是 const)类,它包含 Elements 和 RenderObjects 的配置属性。它还负责创建上述元素和渲染对象。需要理解的重要事情ー小部件从不包含状态或任何业务逻辑,只是传递它们。

元素ー是负责实际 UI 树的实体。它包含对所有子元素的引用,以及(不像 Widget)对其父元素的引用。元素在大多数情况下都会被重用,除非键或小部件被更改。因此,如果 onlyWidget 属性被更改,即使分配了新的 Widget,Element 也将保持不变。

State ー只不过是 Element 内部的一个用户定义类,它还公开了一些来自它的回调。

RenderObject ーー负责实际尺寸的计算、子元素的放置、绘制、触摸事件的处理等。这些对象与 android 或其他框架的经典视图非常相似。

为什么我们同时拥有元素和渲染对象?因为效率高。每个小部件都有各自的元素,但只有一些有渲染对象。由于这一点,很多布局,触摸和其他层次遍历调用可以省略。


代码

第一个例子是一个非常简单的小部件,它在文本不适合时用省略号缩放文本。为什么我们需要这样一个小部件时,内置的文本已经省略号支持你可能会问?答案很简单---- 到目前为止,它只是通过文字而不是字符来表达 https://github.com/flutter/flutter/issues/18761。所以如果你有一个非常长的单词在结尾ー大多数时候你只能看到这个单词的前几个字母,即使有足够的空间去填充。

那么,我们开始吧。Flutter 有许多内置的基类和 mixin,它们将帮助构建完全自定义的小部件。以下是其中的一些:


  • LeafRenderObjectWidget 没有 child
  • SingleChildRenderObjectWidget 一个 child
  • MultiChildRenderObjectWidget 多个 child

在我们的例子中,我们将使用 LeafRenderObjectWidget,因为我们只需要渲染文本,并且不会有子节点:

enum Ellipsis { start, middle, end }
class EllipsizedText extends LeafRenderObjectWidget {
final String text;
final TextStyle? style;
final Ellipsis ellipsis;
const EllipsizedText(
this.text, {
Key? key,
this.style,
this.ellipsis = Ellipsis.end,
}) : super(key: key);
@override
RenderObject createRenderObject(BuildContext context) {
return RenderEllipsizedText()..widget = this;
}
@override
void updateRenderObject(BuildContext context, RenderEllipsizedText renderObject) {
renderObject.widget = this;
}
}

我们创建了我们的 Widget,唯一不同寻常的是有两种方法:


  • createRenderObject — 负责实际创建我们的 RenderObject
  • updateRenderObject — 当 Widget 的数据发生变化但 RenderObject 保持不变时,将调用 updateRenderObject ー。在这种情况下,我们需要更新 RenderObject 中的数据,否则它将呈现旧文本

我还需要注意,将每个值从小部件复制到 RenderObject 是首选的。但是我会通过整个 Widget,因为不管怎样它们都是不可变的(而且我懒得编写所有的样板代码)。

现在让我们从实际的渲染对象开始:

class RenderEllipsizedText extends RenderBox {
var _widgetChanged = false;
var _widget = const EllipsizedText('');
set widget(EllipsizedText widget) {
if (_widget.text == widget.text &&
_widget.style == widget.style &&
_widget.ellipsis == widget.ellipsis) {
return;
}
_widgetChanged = true;
_widget = widget;
markNeedsLayout();
}
}

在这里,我们定义了所有的变量,并编写了一个 setter 来实际更新它们。还有一个检查值是否实际发生了更改的防护措施ー如果没有更改,则没有必要重新计算省略号和重绘文本。

现在我们需要布局渲染对象。

class RenderEllipsizedText extends RenderBox {
// ...
var _constraints = const BoxConstraints();
@override
void performLayout() {
if (!_widgetChanged && _constraints == constraints && hasSize) {
return;
}
_widgetChanged = false;
_constraints = constraints;
size =_ellipsize(
minWidth: constraints.minWidth,
maxWidth: constraints.maxWidth,
);
}
}

布局的过程相当简单。所有我们需要做的ー根据提供给我们的约束计算渲染对象的大小。约束只描述我们必须遵守的最小和最大规模。另外,如果没有任何变化,并且在以前的布局传递过程中已经计算了大小,则添加额外的检查。

实际创建省略号文本的过程相当繁琐,而且肯定有更好的解决方案,但我选择使用二进制搜索来寻找最佳匹配。

class RenderEllipsizedText extends RenderBox {
// ...
final _textPainter = TextPainter(textDirection: TextDirection.ltr);
Size _ellipsize({required double minWidth, required double maxWidth}) {
final text = _widget.text;
if (_layoutText(length: text.length, minWidth: minWidth) > maxWidth) {
var left = 0;
var right = text.length - 1;
while (left final index = (left + right) ~/ 2;
if (_layoutText(length: index, minWidth: minWidth) > maxWidth) {
right = index;
} else {
left = index + 1;
}
}
_layoutText(length: right - 1, minWidth: minWidth);
}
return constraints.constrain(Size(_textPainter.width, _textPainter.height));
}
}

我不会讲完所有这些逻辑(如果你愿意,你可以通过它来阅读)。但是重要的是 TextPainter 是用来计算文本大小的。如果文本大小长于我们的约束,我会尽量使它越来越短,直到它符合我们的约束。

_layoutText 用来计算我们裁剪后的文本大小:

double _layoutText({required int length, required double minWidth}) {
final text = _widget.text;
final style = _widget.style;
final ellipsis = _widget.ellipsis;
String ellipsizedText = '';
switch (ellipsis) {
case Ellipsis.start:
if (length > 0) {
ellipsizedText = text.substring(text.length - length, text.length);
if (length != text.length) {
ellipsizedText = '...' + ellipsizedText;
}
}
break;
case Ellipsis.middle:
if (length > 0) {
ellipsizedText = text;
if (length != text.length) {
var start = text.substring(0, (length / 2).round());
var end = text.substring(text.length - start.length, text.length);
ellipsizedText = start + '...' + end;
}
}
break;
case Ellipsis.end:
if (length > 0) {
ellipsizedText = text.substring(0, length);
if (length != text.length) {
ellipsizedText = ellipsizedText + '...';
}
}
break;
}
_textPainter.text = TextSpan(text: ellipsizedText, style: style);
_textPainter.layout(minWidth: minWidth, maxWidth: double.infinity);
return _textPainter.width;
}

差不多就是这样了,我们剩下要做的就是——实际上画出我们的文本。

@override
void paint(PaintingContext context, Offset offset) {
_textPainter.paint(context.canvas, offset);
}


© 猫哥

https://ducafecat.tech/

https://github.com/ducafecat


往期


开源


GetX Quick Start

https://github.com/ducafecat/getx_quick_start


新闻客户端

https://github.com/ducafecat/flutter_learn_news


strapi 手册译文

https://getstrapi.cn


微信讨论群 ducafecat


系列集合


译文

https://ducafecat.tech/categories/%E8%AF%91%E6%96%87/


开源项目

https://ducafecat.tech/categories/%E5%BC%80%E6%BA%90/


Dart 编程语言基础

https://space.bilibili.com/404904528/channel/detail?cid=111585


Flutter 零基础入门

https://space.bilibili.com/404904528/channel/detail?cid=123470


Flutter 实战从零开始 新闻客户端

https://space.bilibili.com/404904528/channel/detail?cid=106755


Flutter 组件开发

https://space.bilibili.com/404904528/channel/detail?cid=144262


Flutter Bloc

https://space.bilibili.com/404904528/channel/detail?cid=177519


Flutter Getx4

https://space.bilibili.com/404904528/channel/detail?cid=177514


Docker Yapi

https://space.bilibili.com/404904528/channel/detail?cid=130578






推荐阅读
  • 在使用 Qt 进行 YUV420 图像渲染时,由于 Qt 本身不支持直接绘制 YUV 数据,因此需要借助 QOpenGLWidget 和 OpenGL 技术来实现。通过继承 QOpenGLWidget 类并重写其绘图方法,可以利用 GPU 的高效渲染能力,实现高质量的 YUV420 图像显示。此外,这种方法还能显著提高图像处理的性能和流畅性。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
  • 开发笔记:深入解析Android自定义控件——Button的72种变形技巧
    开发笔记:深入解析Android自定义控件——Button的72种变形技巧 ... [详细]
  • Spring框架中枚举参数的正确使用方法与技巧
    本文详细阐述了在Spring Boot框架中正确使用枚举参数的方法与技巧,旨在帮助开发者更高效地掌握和应用枚举类型的数据传递,适合对Spring Boot感兴趣的读者深入学习。 ... [详细]
  • 本指南介绍了如何在ASP.NET Web应用程序中利用C#和JavaScript实现基于指纹识别的登录系统。通过集成指纹识别技术,用户无需输入传统的登录ID即可完成身份验证,从而提升用户体验和安全性。我们将详细探讨如何配置和部署这一功能,确保系统的稳定性和可靠性。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 优化后的标题:深入探讨网关安全:将微服务升级为OAuth2资源服务器的最佳实践
    本文深入探讨了如何将微服务升级为OAuth2资源服务器,以订单服务为例,详细介绍了在POM文件中添加 `spring-cloud-starter-oauth2` 依赖,并配置Spring Security以实现对微服务的保护。通过这一过程,不仅增强了系统的安全性,还提高了资源访问的可控性和灵活性。文章还讨论了最佳实践,包括如何配置OAuth2客户端和资源服务器,以及如何处理常见的安全问题和错误。 ... [详细]
  • 当使用 `new` 表达式(即通过 `new` 动态创建对象)时,会发生两件事:首先,内存被分配用于存储新对象;其次,该对象的构造函数被调用以初始化对象。为了确保资源管理的一致性和避免内存泄漏,建议在使用 `new` 和 `delete` 时保持形式一致。例如,如果使用 `new[]` 分配数组,则应使用 `delete[]` 来释放内存;同样,如果使用 `new` 分配单个对象,则应使用 `delete` 来释放内存。这种一致性有助于防止常见的编程错误,提高代码的健壮性和可维护性。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
  • 深入解析 Android TextView 中 getImeActionLabel() 方法的使用与代码示例 ... [详细]
  • 技术分享:深入解析GestureDetector手势识别机制
    技术分享:深入解析GestureDetector手势识别机制 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 题目链接:https://www.luogu.com.cn/problem/P6453在解决 COCI 2008-2009 第四轮 PERIODNI 问题时,我们需要逐行分析。由于一行中的字符若被断开则不再视为同一行,因此每行的最大矩形区域需要单独计算。通过这种方法,可以确保每层都能找到其最大连续子矩形,从而有效解决问题。 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 掌握Android UI设计:利用ZoomControls实现图片缩放功能
    本文介绍了如何在Android应用中通过使用ZoomControls组件来实现图片的缩放功能。ZoomControls提供了一种简单且直观的方式,让用户可以通过点击放大和缩小按钮来调整图片的显示大小。文章详细讲解了ZoomControls的基本用法、布局设置以及与ImageView的结合使用方法,适合初学者快速掌握Android UI设计中的这一重要功能。 ... [详细]
author-avatar
林志群晴梦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有