Skip to content
On this page

写一个自定义组件

Fyne中包含的标准小部件旨在支持标准用户交互和需求。由于GUI通常必须提供自定义功能,因此可能需要编写自定义小部件。本文概述了如何。

一个小部件分为两个区域-每个区域都实现一个标准界面-fyne.widget和fyne.widget渲染器。小部件定义行为和状态,渲染器用于定义如何将其绘制到屏幕上。

fyne.Widget

Fyne中的小部件只是一个有状态的画布对象,其渲染定义与主逻辑分离。正如您从fyne.Widget接口中看到的那样,需要实现的东西并不多。

go
type Widget interface {
	CanvasObject

	CreateRenderer() WidgetRenderer
}

因为小部件需要像我们从同一界面继承的任何其他画布项一样使用。为了节省编写所需的所有函数,我们可以使用处理基本功能的widget.BaseWidget类型。

每个小部件定义将包含比接口所需的多得多的内容。在Fyne小部件中,导出定义行为的字段是标准的(就像画布包中定义的原语一样)。

例如,查看小部件。按钮类型:

go
type Button struct {
	BaseWidget
	Text  string
	Style ButtonStyle
	Icon  fyne.Resource

	OnTapped func()
}

您可以看到这些项中的每一项是如何存储有关小部件行为的状态的,但却看不到它是如何呈现的。

fyne.WidgetRenderer

小部件渲染器负责管理fyne.CanvasObject原语的列表,这些原语一起创建小部件的设计。它很像带有自定义布局和一些附加主题处理的fyne.Container。

每个小部件都必须提供渲染器,但完全可以从另一个小部件重用渲染器,特别是如果您的小部件是另一个标准控件的轻量级包装器。

go
type WidgetRenderer interface {
	Layout(Size)
	MinSize() Size

	Refresh()
	Objects() []CanvasObject
	Destroy()
}

如您所见,Layout(Size)和MinSize()函数类似于fyne.Layout接口,没有[]fyne.CanvasObject参数-这是因为小部件确实需要布局,但它控制将包含哪些对象。

当此渲染器绘制的小部件发生更改或主题发生更改时,将触发Refresh()方法。在任何一种情况下,我们都可能需要调整它的外观。最后,当不再需要此渲染器时,将调用Destroy()方法,以便清除否则会泄漏的任何资源。

再次与按钮小部件进行比较-它是fyne.WidgetRenderer实现基于以下类型:

go
type buttonRenderer struct {
	icon   *canvas.Image
	label  *canvas.Text
	shadow *fyne.CanvasObject

	objects []fyne.CanvasObject
	button  *Button
}

正如您所看到的,它有字段来缓存实际的图像、文本和阴影画布对象以供绘制。为了方便起见,它跟踪fyne.WidgetRenderer所需的对象切片。

最后,它保留对widget.Button的所有状态信息的引用。在Refresh()方法中,它将根据基础小部件中的任何更改更新图形状态

Bring it together

一个基本的小部件将扩展小部件.BaseWidget类型,并声明小部件保持的任何状态。CreateRenderer()函数必须存在并返回新的fyne.WidgetRenderer实例。Fyne中的小部件和驱动程序代码将确保相应地缓存它-此方法可能被多次调用(例如,如果小部件被隐藏,然后显示)。如果再次调用CreateRenderer(),则应返回一个新的渲染器实例,因为旧的实例可能已被销毁。

注意不要在渲染器中保留任何重要状态-动画字幕非常适合该位置,但用户状态不适合。隐藏的小部件可能会破坏其渲染器,如果再次显示,新的渲染器必须能够反映相同的小部件状态。