写一个自定义组件 #
Fyne中包含的标准小部件旨在支持标准用户交互和需求。由于GUI通常必须提供自定义功能,因此可能需要编写自定义小部件。本文概述了如何。
一个小部件分为两个区域-每个区域都实现一个标准界面-fyne.widget和fyne.widget渲染器。小部件定义行为和状态,渲染器用于定义如何将其绘制到屏幕上。
fyne.Widget #
Fyne中的小部件只是一个有状态的画布对象,其渲染定义与主逻辑分离。正如您从fyne.Widget接口中看到的那样,需要实现的东西并不多。
type Widget interface {
CanvasObject
CreateRenderer() WidgetRenderer
}
2
3
4
5
因为小部件需要像我们从同一界面继承的任何其他画布项一样使用。为了节省编写所需的所有函数,我们可以使用处理基本功能的widget.BaseWidget类型。
每个小部件定义将包含比接口所需的多得多的内容。在Fyne小部件中,导出定义行为的字段是标准的(就像画布包中定义的原语一样)。
例如,查看小部件。按钮类型:
type Button struct {
BaseWidget
Text string
Style ButtonStyle
Icon fyne.Resource
OnTapped func()
}
2
3
4
5
6
7
8
您可以看到这些项中的每一项是如何存储有关小部件行为的状态的,但却看不到它是如何呈现的。
fyne.WidgetRenderer #
小部件渲染器负责管理fyne.CanvasObject原语的列表,这些原语一起创建小部件的设计。它很像带有自定义布局和一些附加主题处理的fyne.Container。
每个小部件都必须提供渲染器,但完全可以从另一个小部件重用渲染器,特别是如果您的小部件是另一个标准控件的轻量级包装器。
type WidgetRenderer interface {
Layout(Size)
MinSize() Size
Refresh()
Objects() []CanvasObject
Destroy()
}
2
3
4
5
6
7
8
如您所见,Layout(Size)和MinSize()函数类似于fyne.Layout接口,没有[]fyne.CanvasObject参数-这是因为小部件确实需要布局,但它控制将包含哪些对象。
当此渲染器绘制的小部件发生更改或主题发生更改时,将触发Refresh()方法。在任何一种情况下,我们都可能需要调整它的外观。最后,当不再需要此渲染器时,将调用Destroy()方法,以便清除否则会泄漏的任何资源。
再次与按钮小部件进行比较-它是fyne.WidgetRenderer实现基于以下类型:
type buttonRenderer struct {
icon *canvas.Image
label *canvas.Text
shadow *fyne.CanvasObject
objects []fyne.CanvasObject
button *Button
}
2
3
4
5
6
7
8
正如您所看到的,它有字段来缓存实际的图像、文本和阴影画布对象以供绘制。为了方便起见,它跟踪fyne.WidgetRenderer所需的对象切片。
最后,它保留对widget.Button的所有状态信息的引用。在Refresh()方法中,它将根据基础小部件中的任何更改更新图形状态
Bring it together #
一个基本的小部件将扩展小部件.BaseWidget类型,并声明小部件保持的任何状态。CreateRenderer()函数必须存在并返回新的fyne.WidgetRenderer实例。Fyne中的小部件和驱动程序代码将确保相应地缓存它-此方法可能被多次调用(例如,如果小部件被隐藏,然后显示)。如果再次调用CreateRenderer(),则应返回一个新的渲染器实例,因为旧的实例可能已被销毁。
注意不要在渲染器中保留任何重要状态-动画字幕非常适合该位置,但用户状态不适合。隐藏的小部件可能会破坏其渲染器,如果再次显示,新的渲染器必须能够反映相同的小部件状态。