各主流的编程语言都有其特有的 UI 库,Go 语言也不甘落后。如果你不习惯用 Dart 或 Javascript 来开发跨平台的 GUI 应用,不妨来看看 Fyne,这个背靠强大易用的Go语言的UI工具库,兼具性能和开发效率,在桌面和移动端都能构建用户友好的应用。

Go语言UI库

简介

Fyne 是 fyne-io 组织在 Github 上开源的 Go 语言 UI 工具包,项目位于 https://github.com/fyne-io/fyne,目前版本为 v1.3.2。

Fyne 提供了简单的方式来开发美观、轻量和用户友好的原生应用,拥有大量性能高且方便使用的控件和布局。Fyne 专注于设计和可用性,控件能够充分满足用户对于交互的期待,并为开发者提供清晰的功能和接口。Fyne 易于安装,无须为各种复杂的依赖烦恼。而 Go 语言作为一个日渐流行的服务端语言,其较低的学习门槛和优秀的性能,使得 Fyne 具有更大的推广空间。目前,Fyne 已经拥有大量的使用案例,广受欢迎。

Fyne库 - 为桌面和移动端构建美观应用

安装

Fyne 使用 Go 语言开发,需要 Go 1.12 或以上,使用 go get 安装:

go get fyne.io/fyne

同时,你还需要一个 C语言编译器来编译平台相关的图形驱动。对于 Windows 平台,Cygwin、TDM-GCC 和 MSYS2(MingW-w64)都可以使用。在 Linux 平台,则需安装对应平台的图形驱动,如在 Ubuntu / Debian 中可以使用命令

sudo apt-get install golang gcc libgl1-mesa-dev xorg-dev

想要开发 Android 应用,则需要安装 Android SDK 和 Android NDK。而想要挨罚 macOS 或 iOS 应用,则需要 Xcode 进行开发。

示例

Fyne 提供了使用函数。或是使用结构体这两套不同的接口风格,可以根据个人的编码喜好选择。我们来看 Fyne 的 Hello World 例子:

package main

import (
    "fyne.io/fyne/app"
    "fyne.io/fyne/widget"
)

func main() {
    a := app.New()
    w := a.NewWindow("Hello")

    hello := widget.NewLabel("Hello Fyne!")
    w.SetContent(widget.NewVBox(
        hello 
        widget.NewButton("Hi!"  func() {
            hello.SetText("Welcome :)")
        }) 
    ))

    w.ShowAndRun()
}

代码十分简洁明了。首先,通过 app.New() 创建一个应用实例,并新建一个标题为 Hello 的窗口。然后,使用 widget.NewLabel 来创建一个便签,并在窗口设置 垂直方向排列的vbox 布局,首先放置标签,然后再放置一个新建的按钮。按钮注册了一个点击事件的回调函数,在点击按钮时会把标签的文本更改为 Welcome。最后,运行并显示窗口。

直接运行代码:

go run hello.go

就会出现一个新的窗口,并显示带有一个标签和一个按钮的界面:

Fyne的Hello例子

默认的主题是暗色调风格的,如果想要配置为明亮色调的主题,可以设置环境变量

FYNE_THEME=light

就能得到一个明亮色调主题的界面:

Fyne明亮色调例子

在上面的代码中,我们可以看到,控件的创建都是使用 New 之类的函数实现。Fyne 也提供了使用结构体的方式进行控件的创建,

package main

import (
	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/widget"
)

func main() {
	a := app.New()

	w := a.NewWindow("Hello")
	w.SetContent(&widget.Box{Children: []fyne.canvasObject{
		&widget.Label{Text: "Hello Fyne!"} 
		&widget.Button{Text: "Quit"  OnTapped: func() {
			a.Quit()
		}} 
	}})

	w.ShowAndRun()
}

在一些情况下,结构体方式的风格可能更为清晰,可自行选择。

Fyne 控件丰富,包括基本的按钮、输入框、表单、图标、进度条、选择框等。

package main

import (
    "log"

    "fyne.io/fyne/app"
    "fyne.io/fyne/widget"
)

func main() {
    myApp := app.New()
    myWindow := myApp.NewWindow("Form Widget")

    entry := widget.NewEntry()
    textArea := widget.NewMultiLineEntry()

    form := &widget.Form{
        Items: []*widget.FormItem{
            {"Entry"  entry}} 
        OnSubmit: func() {
            log.Println("Form submitted:"  entry.Text)
            log.Println("multiline:"  textArea.Text)
            myWindow.Close()
        } 
    }

    form.Append("Text"  textArea)

    myWindow.SetContent(form)
    myWindow.ShowAndRun()
}

Fyne表单

Fyne 使用布局来控制控件的排列方式,包括 HBox(水平方向单行排列)、VBox(垂直方向单列排列)、Center(居中)、Grid(网格)、GridWrap(自适应的网格)、Border(各边外的剩余空间)和 Max(最大填充)。

Grid布局

Fyne 的窗口控制十分方便,充分利用了 Go 语言的协程特性,完成了窗口的异步渲染和控制:

package main

import (
	"time"

	"fyne.io/fyne"
	"fyne.io/fyne/app"
	"fyne.io/fyne/widget"
)

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("Hello")
	myWindow.SetContent(widget.NewLabel("Hello"))

	go showAnother(myApp)
	myWindow.ShowAndRun()
}

func showAnother(a fyne.App) {
	time.Sleep(time.Second * 5)

	win := a.NewWindow("Shown later")
	win.SetContent(widget.NewLabel("5 seconds later"))
	win.Resize(fyne.NewSize(200  200))
	win.Show()

	time.Sleep(time.Second * 2)
	win.Hide()
}

在本例中,我们在一个窗口中新建了另一个窗口并进行渲染显示,同时展示了包括控制大小、隐藏关闭等功能。

Fyne 还支持画布功能,可以在画布上进行自由渲染,实现更为复杂的图形应用:

package main

import (
    "fyne.io/fyne"
    "fyne.io/fyne/app"
    "fyne.io/fyne/Canvas"
    "fyne.io/fyne/theme"
    "image/color"
    "time"
)

func main() {
    myApp := app.New()
    myWindow := myApp.NewWindow("Canvas")
    myCanvas := myWindow.Canvas()

    text := canvas.NewText("Text"  color.White)
    text.TextStyle.Bold = true
    myCanvas.SetContent(text)
    go changeContent(myCanvas)

    myWindow.Resize(fyne.NewSize(100  100))
    myWindow.ShowAndRun()
}

func changeContent(c fyne.Canvas) {
    time.Sleep(time.Second * 2)

    c.SetContent(canvas.NewRectangle(color.Black))

    time.Sleep(time.Second * 2)
    c.SetContent(canvas.NewLine(color.Gray{0x66}))

    time.Sleep(time.Second * 2)
    circle := canvas.NewCircle(color.White)
    circle.StrokeWidth = 4
    circle.StrokeColor = color.RGBA{0xff  0x33  0x33  0xff}
    c.SetContent(circle)

    time.Sleep(time.Second * 2)
    c.SetContent(canvas.NewImageFromResource(theme.FyneLogo()))
}

Fyne应用例子

更多

Fyne 的打包构建十分简单,安装 Fyne 配套的命令行工具

go get fyne.io/fyne/cmd/fyne

对应桌面端的应用,在确定好目标平台后就可以直接打包:

fyne package -os darwin -icon myapp.png
fyne package -os linux -icon myapp.png
fyne package -os windows -icon myapp.png

上述命令分别对应 macOS、Linux 和 Windows平台的构建,而 myapp.png 是应用的图标文件。对于 macOS 平台,生成 app 应用文件;对于 Linux 平台,生成一个 tag.gz 文件,解包后可以放到 use/local/ 中使用;对于 Windows 平台,直接生成 exe 文件,可以直接运行。

而对于移动端平台,同样十分简单。在配置好了相应的环境后,运行

fyne package -os android -appID com.example.myapp -icon mobileIcon.png
fyne package -os ios - appID com.example.myapp -icon mobileIcon.png

则可以分别构建出 Android 平台的 apk 文件和 iOS 平台的 app 文件。

Fyne特性

总结

Fyne 作为一个 UI 工具包,依靠 Go 语言的优秀性能,尤其是 goroutine 协程的异步特性,实现了高性能、易开发的图形界面应用。Fyne 的使用和设计十分现代化,通过简单的命令行就能实现跨平台的开发和构建,安装方便,平台兼容性好。

Fyne 并未基于现有的 UI 库进行开发,而是直接使用不同平台的图形驱动实现,完成了对跨平台 UI 的抽象,底层代码值得研究。同时,Fyne 仍处于活跃开发阶段,不少功能仍待实现,有兴趣的开发者可持续关注项目。