fyne - 谁说用Go不能开发应用界面

fyne项目介绍

fyne 是一个纯 Golang 的跨平台 GUI 库,跨平台库说实话,是有很多选择的,Flutter、Electron、QT等。fyne 绝对不是一个很大众的选择。但是在我,一名后端程序员尝试使用 Electron实现一个简单的番茄时钟,痛苦地在使用 js 如何在渲染进程和主进程之间传递信息,如何在客户端退到后台的时候继续进行倒计时,vue 和哪个 electron 的库版本兼容的问题中进行挣扎的时候,我真的很希望能有一个纯 Go 实现的 GUI 库能开发 mac 的 app,而且想到这并不是不能做到的,都是使用底层的系统接口渲染,没有那个语言更高贵对吧。于是,就找到了它,这就是 fyne。

再进一步了解 fyne 的时候,感觉进入了另外一个世界。fyne,它已经不仅仅是一个开源项目,它正在逐渐形成一个自己的生态。

fyne 的 github 开源地址是 https://github.com/fyne-io/fyne ,它的官网地址是 https://fyne.io/。 fyne 有一大堆的开源簇拥者,开源簇拥者会上传他们自己开发的 app 到 https://apps.fyne.io/,簇拥者也为 fyne 开发一系列的扩展 https://github.com/fyne-io/fyne-x ,并且 fyne 官方会定期将一些好的扩展并入标准库。不仅如此,fyne 在 2019 年开始举办 FyneConf 大会 https://conf.fyne.io/ ,到 2023 年已经5 届了。

分析源码,fyne 是一个近 9w 行代码的项目,属于一个中等偏上的项目量了,有 1w 多行注释,注释量也足够多了。

------------------------------------------------------------------------------------------------------------
File                                                                     blank        comment           code
------------------------------------------------------------------------------------------------------------
SUM:                                                                     17568          10438          89688
------------------------------------------------------------------------------------------------------------

好的项目和好的产品一样,内部复杂,但是外部暴露简单,这 9w 行代码我们不一定都能理解,但是只要能理解下 fyne 宇宙中最核心的几个结构及它们的方法,就能很快使用它,就能成为一名合格的使用者了。

fyne 核心数据结构

App

App 在 fyne 库中fyne.App, 它是一个 interface,定义了一些图形化应用程序的基本结构和功能。App是整个应用的最原始的存在。对,就是宇宙万物中的一生二,二生四,四生万物中的一,有了它,你就能拥有全世界。

我们使用下列两种库方法可以从 fyne 库直接创建 fyne.App

func NewWithID(id string) fyne.App

func New() fyne.App

New 的底层实现我们暂且不论,这里的 id 参数是什么?它是代表当前 app 的一个唯一字符串,我们一般填写能唯一指定当前 app 的名字,诸如 "com.hade.toolbox" 的字符串。如果不填写,就会使用时间戳来生成一个 fake 的 id。这个 id 在 app 内部叫做 UniqueID,在创建缓存,临时文件存储等目录的时候,会使用这个 id 来创建子目录。这里的 id 必须是全局唯一的。

我们再看下App定义的接口有如下,这里直接将注释给出,

NewWindow(title string) Window: 创建一个新的窗口,并将其作为应用程序的一部分。第一个打开的窗口被视为"主窗口",当关闭时应用程序将退出。
OpenURL(url *url.URL) error: 在默认浏览器中打开指定的 URL。
Icon() Resource: 获取应用程序的图标,用于各种操作系统特定的显示。这也是新窗口的默认图标。
SetIcon(Resource): 设置应用程序使用的图标资源。
Run(): 启动应用程序的事件循环,并一直阻塞直到调用 Quit() 或最后一个窗口关闭。
Quit(): 退出应用程序,关闭所有打开的窗口。在移动设备上不执行任何操作,因为应用程序生命周期由操作系统管理。
Driver() Driver: 返回渲染该应用程序的驱动程序。通常不需要在日常工作中使用,主要用于内部功能。
UniqueID() string: 返回应用程序的唯一标识符(如果已设置)。这必须在使用 Preferences() 函数时设置。
SendNotification(*Notification): 发送一个系统通知,将在操作系统的通知区域显示。
Settings() Settings: 返回全局设置,确定主题等。
Preferences() Preferences: 返回应用程序首选项,用于存储配置和状态。
Storage() Storage: 返回特定于此应用程序的存储处理程序。
Lifecycle() Lifecycle: 返回一个类型,允许应用程序钩入生命周期事件。
Metadata() AppMetadata: 返回在编译时设置的应用程序元数据。
CloudProvider() CloudProvider: 返回当前应用程序的云提供商(如果已由开发人员注册或用户选择)。
SetCloudProvider(CloudProvider): 允许开发人员指定应用程序应如何与云服务集成。

这个接口引出了 App 的几个下级结构:

  • Window
  • Resource
  • Driver
  • Settings
  • Preferences
  • Storage
  • Lifecycle
  • AppMetadata
  • CloudProvider

这几个下级结构就是玩转 fyne 最需要了解的。

App -> Window

我们的应用 App 一般都会有好几个窗口组成,当点击某个按钮的时候,弹出一个窗口,这里的窗口,在 fyne 库中就叫做 Window 数据结构。

当然这些若干窗口的地位也不尽相同,第一个创建出来的窗口称为主窗口,其他的窗口称为子窗口。当主窗口关闭的时候,整个 app 也就关闭了。

fyne.Window 其实也是个接口,这个 Window 接口提供了创建、管理和控制应用程序窗口的基本功能,包括设置标题、全屏模式、大小调整、聚焦、内边距、图标、菜单、生命周期回调、拖放支持以及与画布和剪贴板的交互。

下面是对相关接口的详细说明:

Title() string: 返回当前窗口的标题。这通常显示在窗口装饰中。
SetTitle(string): 更新窗口的标题。
FullScreen() bool: 返回窗口是否处于全屏模式。
SetFullScreen(bool): 设置窗口是否应处于全屏模式。
Resize(Size): 根据指定的内容大小调整窗口大小。由于各种桌面或平台约束,实际大小可能与请求的不完全一致。
RequestFocus(): 尝试提升并聚焦该窗口。这只应在确定用户希望此窗口从任何当前聚焦的窗口窃取焦点时调用。
FixedSize() bool: 返回窗口是否应禁用调整大小。
SetFixedSize(bool): 设置是否应将窗口大小固定。
CenterOnScreen(): 将窗口放置在当前窗口所在的显示器中心。
Padded() bool: 返回窗口是否应有内边距,以使组件不会触及窗口边缘。
SetPadded(bool): 设置窗口是否应有内边距。用于全屏或基于图形的应用程序可能很有用。
Icon() Resource: 返回窗口图标,根据操作系统的不同会以各种方式使用,通常显示在窗口边框或任务切换器上。
SetIcon(Resource): 设置此窗口使用的图标资源。如果未设置,则应返回应用程序图标。
SetMaster(): 指示关闭此窗口应退出应用程序。
MainMenu() *MainMenu: 获取窗口的顶级菜单。
SetMainMenu(*MainMenu): 为此窗口添加顶级菜单。渲染方式取决于加载的驱动程序。
SetOnClosed(func()): 设置在窗口关闭时运行的函数。
SetCloseIntercept(func()): 设置一个函数,在关闭窗口时运行,而不是关闭窗口。在拦截器中应显式调用 Close() 来关闭窗口。
SetOnDropped(func(Position, []URI)): 设置一个窗口范围的回调函数,用于接收拖放的项目。
Show(): 显示窗口。
Hide(): 隐藏窗口,但不会销毁窗口或导致应用程序退出。
Close(): 关闭窗口。如果它是"主窗口",应用程序将退出。
ShowAndRun(): 显示窗口并运行应用程序。这应该在 main() 函数的末尾调用,因为它会阻塞。
Content() CanvasObject: 返回此窗口的内容。
SetContent(CanvasObject): 设置窗口的内容。
Canvas() Canvas: 返回用于在窗口中呈现的画布上下文。这可能有助于为窗口设置键处理程序。
Clipboard() Clipboard: 返回系统剪贴板。

大部分接口都很容易理解,其中我们关注到 Window 的几个下级数据结构:

  • MainMenu
  • CanvasObject
  • Canvas
  • Clipboard

同样的,我们需要钻入到这几个下级数据结构了解。

MainMenu 层级往下,有 MainMenu,Menu,MenuItem 三个层级结构。

MainMenu 结构定义了激活 Windows 窗口后,菜单栏(桌面)显示的内容。

Menu 结构定义了应用程序中标准菜单的信息。它可以用作主菜单(从 MainMenu 下拉)或弹出式菜单。

MenuItem 结构定义了菜单中的单个项目。

简要来说,MainMenu 就是整个菜单栏,Menu 是下拉的一列,MenuItem 是一列中的单行。

  1. MainMenu 包含一个 *Menu 类型的切片 Items,代表顶级菜单。
  2. Menu 包含一个 *MenuItem 类型的切片 Items,代表菜单项。
  3. MenuItem包含以下字段:
    • ChildMenu: 如果此菜单项有子菜单,则为该子菜单的指针。
    • IsQuit: 如果设置为 true,则表示此菜单项用于退出应用程序。
    • IsSeparator: 如果设置为 true,则表示此菜单项应用作分隔符。
    • Label: 菜单项的显示标签。
    • Action: 当菜单项被点击时要执行的函数。
    • Disabled: 如果设置为 true,则表示此菜单项应该被禁用。
    • Checked: 如果设置为 true,则表示此菜单项应该被显示为已选中状态。
    • Icon: 菜单项的图标资源(如果有)。
    • Shortcut: 与此菜单项关联的快捷键(如果有)。

这三者的关系我们用如下类图来表示:
classDiagram class MainMenu { +[]*Menu Items +Refresh() } class Menu { +string Label +[]*MenuItem Items +Refresh() } class MenuItem { +*Menu ChildMenu +bool IsQuit +bool IsSeparator +string Label +func() Action +bool Disabled +bool Checked +Resource Icon +Shortcut Shortcut } MainMenu "1" *-- "*" Menu : contains Menu "1" *-- "*" MenuItem : contains

以下是这三个数据结构每个接口的说明,略微枯燥,可具体使用再细看。

MainMenu 包含以下内容:

  1. Items: 一个 *Menu 类型的切片,代表顶级菜单项。每个 Menu 代表一个下拉菜单。

MainMenu 提供了以下方法:

  1. NewMainMenu(items ...*Menu) *MainMenu: 创建一个新的 MainMenu 实例,并传入顶级菜单项。
  2. Refresh(): 通知任何使用此结构的渲染菜单更新它们的显示。这对于在应用程序运行时动态更新菜单很有用。

这个结构是 Fyne 库中用于管理应用程序主菜单的核心部分。它允许开发人员定义应用程序的顶级菜单结构,并在运行时动态更新菜单。这对于创建功能丰富的桌面应用程序非常有用。

Menu 结构定义了应用程序中标准菜单的信息。它可以用作主菜单(从 MainMenu 下拉)或弹出式菜单。

Menu 结构包含以下字段:

  1. Label: 菜单的标签,在主菜单中显示。
  2. Items: 一个 *MenuItem 类型的切片,代表菜单中的项目。

Menu 提供了以下方法:

  1. NewMenu(label string, items ...*MenuItem) *Menu: 创建一个新的 Menu 实例,并传入标签和菜单项。
  2. Refresh(): 通知任何使用此菜单的窗口或系统托盘更新其显示。这对于在应用程序运行时动态更新菜单很有用。

Menu 结构是 Fyne 库中用于管理应用程序菜单的核心部分。它允许开发人员定义应用程序中的各种菜单,包括主菜单和弹出式菜单。这些菜单可以包含各种菜单项,如普通菜单项、分隔线、子菜单等。Refresh() 方法使开发人员能够在应用程序运行时动态更新菜单,从而提高用户体验。

MenuItem 结构定义了菜单中的单个项目。它包含以下字段:

  1. ChildMenu: 如果此菜单项有子菜单,则为该子菜单的指针。
  2. IsQuit: 如果设置为 true,则表示此菜单项用于退出应用程序。
  3. IsSeparator: 如果设置为 true,则表示此菜单项应用作分隔符。
  4. Label: 菜单项的显示标签。
  5. Action: 当菜单项被点击时要执行的函数。
  6. Disabled: 如果设置为 true,则表示此菜单项应该被禁用。
  7. Checked: 如果设置为 true,则表示此菜单项应该被显示为已选中状态。
  8. Icon: 菜单项的图标资源(如果有)。
  9. Shortcut: 与此菜单项关联的快捷键(如果有)。

MenuItem 提供了以下方法:

  1. NewMenuItem(label string, action func()) *MenuItem: 创建一个新的 MenuItem 实例,并传入标签和点击时执行的操作。
  2. NewMenuItemSeparator() *MenuItem: 创建一个新的菜单分隔线项目。

MenuItem 结构是 Fyne 库中用于定义应用程序菜单项的核心部分。它提供了丰富的功能,如子菜单、快捷键、图标和选中状态等,使开发人员能够创建功能强大的应用程序菜单。这些菜单项可以被添加到 Menu 结构中,以构建应用程序的菜单层次结构。

其中 MenuItem 中的 Action 函数是我们最经常使用到的,点击某个按钮的具体行为。

Window -> CanvasObject

CanvasObject 是 Fyne 库中定义的一个接口,用于描述可以添加到画布上的任何图形对象。是的,任何图形对象,画布上的任何元素都是由一个 CanvasObject,或者由多个 CanvasObject 组成的。所有画布上的元素都实现了 CanvasObject 接口。所以CanvasObject 定义的方法,是可以作用在任何元素上的。

CanvasObject 接口定义了以下方法:

  1. Geometry :
    • MinSize() Size: 返回该对象需要被绘制的最小尺寸。
    • Move(Position): 将该对象移动到相对于其父对象的给定位置。这只应在对象不在使用布局管理器的容器中时调用。
    • Position() Position: 返回该对象相对于其父对象的当前位置。
    • Resize(Size): 将该对象调整到给定大小。这只应在对象不在使用布局管理器的容器中时调用。
    • Size() Size: 返回该对象的当前大小。
  2. Visibility :
    • Hide(): 隐藏该对象。
    • Visible() bool: 返回该对象是否可见。
    • Show(): 显示该对象。
  3. Refresh :
    • Refresh(): 如果该对象的内部状态发生变化而需要重新绘制,则必须调用此方法。

以上是 CanvasObject 接口的定义,而对于一些更复杂的额外的对象,fyne 又定义了另外一些基础接口,这些接口和 CanvasObject 结合起来,就能代表更为复杂的对象元素。

即按照 Golang 的接口鸭子模型,如果我们有具体实现像多个接口,那么这个实现就有这两个接口的能力。比如一个实现了 CanvasObject 和 Scrollable 两个接口的元素,那么它在画布上就是可以被滚动的元素。fyne 真是把接口玩到飞起。

  • Disableable: 描述可以被禁用的 CanvasObject
  • DoubleTappable: 描述可以被双击的 CanvasObject
  • Draggable: 描述可以被拖动的 CanvasObject
  • Focusable: 描述可以响应焦点的 CanvasObject
  • Scrollable: 描述可以被滚动的 CanvasObject
  • SecondaryTappable: 描述可以响应二次点击(右击或长按)的 CanvasObject
  • Shortcutable: 描述可以响应快捷键命令(退出、剪切、复制和粘贴)的 CanvasObject
  • Tabbable: 描述需要接受 Tab 键按下事件的 CanvasObject
  • Tappable: 描述可以响应点击事件的 CanvasObject

理解了接口,我们再聊到 CanvasObject 的具体实现,这些实现才是我们具体代码中会用到的各个元素,使用好这些元素,你的 app 就能有各种各样的展示效果。

  • Container 容器
  • Widget 系列
    • Accordion
    • Button
    • Card
    • Check
    • Entry
    • FileIcon
    • Form
    • Hyperlink
    • Icon
    • Label
    • Progress bar
    • RadioGroup
    • Select
    • SelectEntry
    • Separator
    • Slider
    • TextGrid
    • Toolbar
    • List
    • Table
    • Tree
    • AppTabs
    • Scroll
    • Split
  • Dialog 系列
    • Color
    • Confirm
    • FileOpen
    • Form
    • Information
    • Custom

这些元素对应的样式,可以在官网的 doc 中找到:https://docs.fyne.io/,用的时候再挑。

Window -> Canvas

Canvas 接口定义了一个图形画布,可以在其上添加 CanvasObjectContainer。每个画布都有一个缩放比例,在渲染过程中会自动应用。

为什么有了 Windows 还有一个 Canvas 呢?

Canvas 理解为画布,更像是 Windows 的宿主,我们可以有一个画布放在 Windows 中,也可以有一个画布放在 Image 图片中,在这个图片上进行绘制。

func TestPainter_paintImage(t *testing.T) {
	img := canvas.NewImageFromImage(makeTestImage(3, 3))

	c := test.NewCanvas()
	c.SetPadded(false)
	c.SetContent(img)
	c.Resize(fyne.NewSize(50, 50))
	p := software.NewPainter()

	target := p.Paint(c)
	test.AssertImageMatches(t, "draw_image_default.png", target)
}

Canvas 接口提供了管理画布内容、焦点、事件、缩放和覆盖层的功能,以及截屏和坐标转换等实用工具。它是 Fyne 库中构建图形用户界面的核心组件之一。

Canvas 接口定义了以下功能:

  1. 内容管理 :
    • Content() CanvasObject: 返回当前设置在画布上的顶层 CanvasObject
    • SetContent(CanvasObject): 设置画布的顶层 CanvasObject
    • Refresh(CanvasObject): 通知画布重新绘制指定的 CanvasObject
  2. 焦点管理 :
    • Focus(Focusable): 设置指定的 Focusable 对象为焦点。
    • FocusNext(): 聚焦下一个可聚焦的对象。
    • FocusPrevious(): 聚焦上一个可聚焦的对象。
    • Unfocus(): 取消当前焦点。
    • Focused() Focusable: 返回当前获得焦点的对象。
  3. 尺寸和缩放 :
    • Size() Size: 返回画布的当前大小。
    • Scale() float32: 返回画布当前使用的缩放比例。
  4. 覆盖层管理 :
    • Overlays() OverlayStack: 返回画布的覆盖层堆栈。
  5. 事件处理 :
    • OnTypedRune() func(rune), SetOnTypedRune(func(rune)): 设置和获取键入字符事件的处理函数。
    • OnTypedKey() func(*KeyEvent), SetOnTypedKey(func(*KeyEvent)): 设置和获取键盘事件的处理函数。
    • AddShortcut(shortcut Shortcut, handler func(shortcut Shortcut)): 添加一个快捷键及其处理函数。
    • RemoveShortcut(shortcut Shortcut): 移除一个快捷键。
  6. 截屏 :
    • Capture() image.Image: 捕获画布的当前内容并返回一个图像。
  7. 坐标转换 :
    • PixelCoordinateForPosition(Position) (int, int): 返回给定位置在画布上的像素坐标。
    • InteractiveArea() (Position, Size): 返回中央交互区域的位置和大小。

Window -> Clipboard

Clipboard 接口定义了系统剪贴板的接口。它提供了以下两个方法:

  1. Content() string: 返回当前剪贴板的内容。
  2. SetContent(content string): 设置剪贴板的内容。

这个接口为应用程序提供了与系统剪贴板进行交互的方法。通过这个接口,开发人员可以:

  1. 从剪贴板读取文本内容,例如在粘贴操作中使用。
  2. 将文本内容写入到剪贴板,例如在复制操作中使用。

Clipboard 接口是 Fyne 库中用于与系统剪贴板进行交互的核心组件。它为应用程序提供了一种标准的方式来访问和管理剪贴板内容,从而增强应用程序的复制粘贴功能。

App -> Resource

Resource 接口定义了一个单一的二进制资源,比如图像或字体。fyne 倾向于所有外部的资源,比如图片, 字体等都编译在代码中,被定义一个唯一的标识名称,拥有对应的字节数组内容,在使用的时候,就不用使用各种路径去加载,而是直接到内存中加载。

fyne 还提供了一个 bundle 命令,我们可以用 fyne 的工具对任何 png,freetype 进行打包成为一个变量,我们称这个变量为 StaticResource。

StaticResource 是 Resource 接口的具体实现,他们提供了一种标准的方式来管理应用程序中包含的二进制资源。开发人员可以使用这些接口从文件系统或 URL 加载资源,并在应用程序中使用这些资源,而无需关心底层的文件系统或网络操作。这有助于提高应用程序的可维护性和跨平台性。

这或许也是我喜欢 fyne 的原因之一,和 golang 的语言如出一辙,所有东西都是静态,避免任何的动态加载。不要再去拼凑各种动态文件路径,加载等逻辑,真是轻松的一匹。

照例对照源码列一下接口和结构的定义。

Resource 接口提供了以下方法:

  1. Name() string: 返回资源的唯一名称,通常与生成它的文件名匹配。
  2. Content() []byte: 返回资源的字节内容,不进行任何压缩,但保留资源本身的任何压缩。

StaticResource 结构包含以下字段:

  1. StaticName string: 资源的名称。
  2. StaticContent []byte: 资源的字节内容。

StaticResource 提供了以下方法:

  1. Name() string: 返回资源的名称。
  2. Content() []byte: 返回资源的字节内容。

App -> Driver

Driver 接口定义了 Fyne 渲染驱动程序的抽象概念。任何实现这个接口的驱动程序都必须提供以下方法:

  1. 窗口管理 :
    • CreateWindow(string) Window: 创建一个新的 UI 窗口。
    • AllWindows() []Window: 返回包含所有应用程序窗口的切片。
  2. 文本渲染 :
    • RenderedTextSize(text string, fontSize float32, style TextStyle) (size Size, baseline float32): 返回渲染给定文本所需的大小和基线高度。
  3. 画布关联 :
    • CanvasForObject(CanvasObject) Canvas: 返回与给定 CanvasObject 关联的画布。
    • AbsolutePositionForObject(CanvasObject) Position: 返回给定 CanvasObject 相对于画布左上角的绝对位置。
  4. 设备管理 :
    • Device() Device: 返回应用程序当前运行的设备。
  5. 事件循环 :
    • Run(): 启动驱动程序的主事件循环。
    • Quit(): 关闭驱动程序和打开的窗口,然后退出应用程序。
  6. 动画管理 :
    • StartAnimation(*Animation): 注册一个新的动画并请求启动它。
    • StopAnimation(*Animation): 停止一个动画并从驱动程序中注销。

Driver 接口是 Fyne 库中非常重要的组件,它定义了渲染引擎的核心功能。通过实现这个接口,开发人员可以为特定的操作系统或运行环境提供定制的渲染驱动程序,从而使 Fyne 应用程序能够在不同平台上运行。

在 fyne 中,默认的驱动是 OpenGL(gLDriver)。fyne 使用的 openGL 的驱动也是使用开源库:github.com/go-gl/glfw/v3.3/glfw。这个开源库是 cgo 实现的,调用操作系统底层的GLFW (OpenGL Friendly Windowing)。而GLFW (OpenGL Friendly Windowing) 是一个开源的、跨平台的、轻量级的 C 库,用于创建和管理 OpenGL、OpenGL ES 和 Vulkan 图形上下文以及窗口。它主要用于游戏和其他视觉应用程序的开发。

所以这也就是 fyne 能一次开发,mac,android,window 都可运行的底气所在。

App -> Settings

Settings 接口提供了一种标准的方式来配置 Fyne 应用程序的外观和行为。开发人员可以通过实现该接口来定义应用程序的默认设置,并允许用户通过 UI 界面或其他方式进行更改。

这些设置对于确保应用程序在不同平台和用户偏好下都能提供一致的体验非常重要。例如,主题和缩放设置可以确保应用程序在不同屏幕尺寸和分辨率下保持良好的视觉效果。

Settings 接口是 Fyne 应用程序配置管理的核心,确保应用程序能够适应不同的用户需求和环境。

Settings 接口它包含以下功能:

  1. 主题管理 :
    • Theme() Theme: 获取当前使用的主题。
    • SetTheme(Theme): 设置使用的主题。
    • ThemeVariant() ThemeVariant: 获取主题的首选变体(如浅色或深色)。
  2. 缩放设置 :
    • Scale() float32: 获取应用程序的缩放比例。
  3. 主色设置 :
    • PrimaryColor() string: 获取用户首选的主色。
  4. 变更监听 :
    • AddChangeListener(chan Settings): 添加一个监听器,当设置发生变化时会被通知。
  5. 构建类型 :
    • BuildType() BuildType: 获取应用程序的构建类型(标准、调试或发布)。
  6. 动画设置 :
    • ShowAnimations() bool: 获取是否应该显示动画的设置。

App -> Preferences

Preferences 接口定义了一组用于保存和加载应用程序用户首选项的方法。首选项就是用户进入这个 app 后的一些设置值。这些首选项值在一个终端是会进行持久化存储的,并且在 App 关闭又重新打开的时候,是还会持续生效。它也支持了多种的数据类型。

它提供了以下功能:

  1. 基本数据类型读写 :
    • Bool(key string) bool: 读取指定键的布尔值。
    • BoolWithFallback(key string, fallback bool) bool: 读取指定键的布尔值,如果不存在则返回回退值。
    • SetBool(key string, value bool): 保存指定键的布尔值。
    • 类似的方法还有 FloatIntString
  2. 列表数据类型读写 :
    • BoolList(key string) []bool: 读取指定键的布尔值列表。
    • BoolListWithFallback(key string, fallback []bool) []bool: 读取指定键的布尔值列表,如果不存在则返回回退列表。
    • SetBoolList(key string, value []bool): 保存指定键的布尔值列表。
    • 类似的方法还有 FloatListIntListStringList
  3. 删除值 :
    • RemoveValue(key string): 删除指定键的值。
  4. 变更监听 :
    • AddChangeListener(func()): 添加一个回调函数,当值发生变化时会被调用。
    • ChangeListeners() []func(): 获取所有已注册的变更监听器。

App -> Storage

Storage 接口定义了 Fyne 应用程序内部文件存储的相关功能。

Storage 接口提供了一个抽象层,用于管理应用程序自身的文件存储。它确保每个 Fyne 应用程序都有一个独立的沙箱存储区域,用于存储应用程序相关的文件,而不会与其他应用程序的文件产生冲突。

这个接口的实现会根据不同的操作系统平台采用不同的存储机制,比如在 Windows 上可能使用应用程序数据目录,在 Android 上可能使用内部存储空间等。但无论采用何种存储方式,开发者都可以使用统一的 Storage 接口进行文件操作。

通过使用 Storage 接口,Fyne 应用程序可以保证在不同平台上都能以一致的方式访问和管理应用程序文件,提高了跨平台的可移植性。同时,这也确保了应用程序的文件存储受到沙箱机制的保护,提高了安全性。

它提供了以下主要功能:

  1. 根 URI 访问 :
    • RootURI() URI: 获取应用程序的根文件存储 URI。这个 URI 代表了应用程序独有的文件存储沙箱。
  2. 文件创建 :
    • Create(name string) (URIWriteCloser, error): 创建一个新的文件,并返回一个可写入的 URIWriteCloser 对象。
  3. 文件打开 :
    • Open(name string) (URIReadCloser, error): 打开一个现有的文件,并返回一个可读取的 URIReadCloser 对象。
  4. 文件保存 :
    • Save(name string) (URIWriteCloser, error): 打开或创建一个文件并返回一个可写入的 URIWriteCloser 对象。
  5. 文件删除 :
    • Remove(name string) error: 删除指定名称的文件。
  6. 列出文件 :
    • List() []string: 返回当前存储中所有文件的名称列表。

总之, Storage 接口是 Fyne 框架中用于管理应用程序内部文件存储的核心组件。它抽象了底层的存储细节,为上层应用程序提供了一致的文件操作 API。

App -> Lifecycle

通过 Lifecycle 接口可以在不同阶段为应用程序设置行为。

  1. 前后台切换钩子 :
    • SetOnEnteredForeground(func()): 用于设置一个回调函数,在应用程序从后台切换到前台并获得焦点时会被调用。
    • SetOnExitedForeground(func()): 用于设置一个回调函数,在应用程序失去输入焦点并切换到后台时会被调用。
  2. 启动/停止钩子 :
    • SetOnStarted(func()): 用于设置一个回调函数,在应用程序启动并开始运行时会被调用。
    • SetOnStopped(func()): 用于设置一个回调函数,在应用程序停止运行时会被调用。

通过这些钩子函数,开发者可以在应用程序的生命周期关键时刻执行相应的逻辑,比如:

  • 当应用程序进入前台时,可以恢复动画、重新加载数据等。
  • 当应用程序进入后台时,可以暂停耗电的操作、保存当前状态等。
  • 当应用程序启动时,可以进行一些初始化操作。
  • 当应用程序停止时,可以执行清理工作、保存用户数据等。

App -> AppMetadata

AppMetadata 是 Fyne 框架中用于描述应用程序元信息的数据结构。它包含了以下几个重要的属性:

  1. 应用程序 ID :
    • ID string: 应用程序的唯一标识符,通常用于各种应用程序分发平台。
  2. 应用程序名称 :
    • Name string: 应用程序的人类可读名称。
  3. 应用程序版本 :
    • Version string: 应用程序的版本号,通常遵循语义化版本控制规范。
    • Build int: 应用程序的构建编号,有时会附加在版本号后面。
  4. 应用程序图标 :
    • Icon Resource: 如果存在的话,包含了应用程序在构建时打包的图标资源。
  5. 发布模式 :
    • Release bool: 表示该二进制文件是否是在发布模式下构建的。
  6. 自定义元数据 :
    • Custom map[string]string: 包含了开发者在 FyneApp.toml 文件中或编译命令行上定义的自定义元数据。

AppMetadata 结构体的作用是为 Fyne 应用程序提供一种标准化的方式来描述自身的元信息。这些信息通常会被用于以下场景:

  1. 应用程序分发 :
    应用程序 ID、名称、版本号等信息通常会被应用程序分发平台使用,例如 App Store、Google Play 等。
  2. 应用程序徽标 :
    应用程序图标信息会被用于在操作系统界面和应用商店中显示应用程序的图标。
  3. 应用程序配置 :
    自定义的元数据可以被应用程序使用,用于存储一些额外的配置信息。
  4. 应用程序更新 :
    版本号和构建编号信息可以帮助用户跟踪应用程序的更新情况。

总之, AppMetadata 是 Fyne 框架中用于描述应用程序元信息的核心数据结构,它为开发者提供了一种标准化的方式来定义应用程序的各种属性,从而更好地支持应用程序的分发、展示和配置等需求。

App -> CloudProvider

CloudProvider 是 Fyne 框架中用于定义和管理云服务提供商的接口。它主要提供了以下功能:

  1. 提供者信息获取 :
    • ProviderDescription() string: 获取云服务提供商的详细描述信息。
    • ProviderIcon() Resource: 获取与该云服务关联的图标资源。
    • ProviderName() string: 获取云服务提供商的名称。
  2. 生命周期管理 :
    • Cleanup(App): 当云服务提供商不再被使用时,会被调用来执行清理工作。
    • Setup(App) error: 当云服务提供商第一次被使用时,会被调用进行初始化设置。如果初始化失败,可以返回一个错误,以退出云服务设置流程。

通过 CloudProvider 接口,Fyne 应用程序可以与不同的云服务提供商进行集成,并提供统一的用户体验。具体的集成方式包括:

  1. 用户偏好同步 :
    • CloudProviderPreferences 接口定义了同步用户偏好设置到云端的功能。
  2. 文档同步 :
    • CloudProviderStorage 接口定义了同步应用程序文档到云端的功能。

当应用程序开发者选择使用云服务提供商时,只需要实现 CloudProvider 接口以及可选的 CloudProviderPreferencesCloudProviderStorage 接口,就可以为应用程序提供云同步功能。

Fyne 框架会负责管理不同云服务提供商的生命周期,并提供统一的 API 供应用程序使用。这使得应用程序开发者可以专注于业务逻辑的实现,而不需要过多地关注云服务的集成细节。

总之, CloudProvider 是 Fyne 框架中用于集成和管理云服务提供商的核心组件,它为应用程序提供了跨云服务的统一抽象,简化了云服务集成的开发工作。

fyne 总结

fyne 是一个对于后端 Golang 开发者极其友好的 GUI 库,对于后端开发人员,功能开发并不是难事,而前端页面绘制可能会难倒大部分开发者,fyne 提供了一种使用代码就能很好绘制界面的方法,这对于这个人群来说,是一个福音。

这里主要列了 fyne 的核心数据结构,当然每个结构里面的方法和具体实现都需要花时间琢磨。我自己用 fyne 开发了一个番茄始终,当你自己用 go 绘制了界面,用 go 开启协程进行倒计时,用go 在 mac 的 tab 栏增加倒计时显示的时候,你才会感叹到 fyne 的强大。