Go 语言生态中,GUI 一直是短板,更别说跨平台的 GUI 了。fyne
向前迈了一大步。fyne
是 Go 语言编写的跨平台的 UI 库,它可以很方便地移植到手机设备上。fyne
使用上非常简单,同时它还提供fyne
命令打包静态资源和应用程序。我们先简单介绍基本控件和布局,然后介绍如何发布一个fyne
应用程序。
本文代码使用 Go Modules。
先初始化:
$ mkdir fyne && cd fyne
$ go mod init github.com/darjun/go-daily-lib/fyne
由于fyne
包含一些 C/C++ 的代码,所以需要gcc
编译工具。在 Linux/Mac OSX 上,gcc
基本是标配,在 windows 上我们有 3 种方式安装gcc
工具链:
MSYS2 + MingW-w64:https://www.msys2.org/;
TDM-GCC:https://jmeubank.github.io/tdm-gcc/download/;
Cygwin:https://www.cygwin.com/。
本文选择TDM-GCC
的方式安装。到https://jmeubank.github.io/tdm-gcc/download/下载安装程序并安装。正常情况下安装程序会自动设置PATH
路径。打开命令行,键入gcc -v
。如果正常输出版本信息,说明安装成功且环境变量设置正确。
安装fyne
:
$ go get -u fyne.io/fyne
到此准备工作已经完成,我们开始编码。按照惯例,先以Hello, World程序开始:
package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/widget"
)
func main() {
myApp := app.New()
myWin := myApp.NewWindow("Hello")
myWin.SetContent(widget.NewLabel("Hello Fyne!"))
myWin.Resize(fyne.NewSize(200, 200))
myWin.ShowAndRun()
}
运行结果如下:
fyne
的使用很简单。每个fyne
程序都包括两个部分,一个是应用程序对象myApp
,通过app.New()
创建。另一个是窗口对象,通过应用程序对象myApp
来创建myApp.NewWindow("Hello")
。myApp.NewWindow()
方法中传入的字符串就是窗口标题。
fyne
提供了很多常用的组件,通过widget.NewXXX()
创建(XXX
为组件名)。上面示例中,我们创建了一个Label
控件,然后设置到窗口中。最后,调用myWin.ShowAndRun()
开始运行程序。实际上myWin.ShowAndRun()
等价于
myWin.Show()
myApp.Run()
myWin.Show()
显示窗口,myApp.Run()
开启事件循环。
注意一点,fyne
默认窗口大小是根据内容的宽高来设置的。上面我们调用myWin.Resize()
手动设置了大小。否则窗口只能放下字符串Hello Fyne!
。
fyne
包结构划分fyne
将功能划分到多个子包中:
fyne.io/fyne
:提供所有fyne
应用程序代码共用的基础定义,包括数据类型和接口;
fyne.io/fyne/app
:提供创建应用程序的 API;
fyne.io/fyne/canvas
:提供Fyne
使用的绘制 API;
fyne.io/fyne/dialog
:提供对话框组件;
fyne.io/fyne/layout
:提供多种界面布局;
fyne.io/fyne/widget
:提供多种组件,fyne
所有的窗体控件和交互元素都在这个子包中。
在fyne
应用程序中,所有显示元素都是绘制在画布(Canvas
)上的。这些元素都是画布对象(CanvasObject
)。调用Canvas.SetContent()
方法可设置画布内容。Canvas
一般和布局(Layout
)容器(Container
)一起使用。canvas
子包中提供了一些基础的画布对象:
package main
import (
"image/color"
"math/rand"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
func main() {
a := app.New()
w := a.NewWindow("Canvas")
rect := canvas.NewRectangle(color.White)
text := canvas.NewText("Hello Text", color.White)
text.Alignment = fyne.TextAlignTrailing
text.TextStyle = fyne.TextStyle{Italic: true}
line := canvas.NewLine(color.White)
line.StrokeWidth = 5
circle := canvas.NewCircle(color.White)
circle.StrokeColor = color.Gray{0x99}
circle.StrokeWidth = 5
image := canvas.NewImageFromResource(theme.FyneLogo())
image.FillMode = canvas.ImageFillOriginal
raster := canvas.NewRasterWithPixels(
func(_, _, w, h int) color.Color {
return color.RGBA{uint8(rand.Intn(255)),
uint8(rand.Intn(255)),
uint8(rand.Intn(255)), 0xff}
},
)
gradient := canvas.NewHorizontalGradient(color.White, color.Transparent)
container := fyne.NewContainerWithLayout(
layout.NewGridWrapLayout(fyne.NewSize(150, 150)),
rect, text, line, circle, image, raster, gradient))
w.SetContent(container)
w.ShowAndRun()
}
程序运行结果如下:
canvas.Rectangle
是最简单的画布对象了,通过canvas.NewRectangle()
创建,传入填充颜色。
canvas.Text
是显示文本的画布对象,通过canvas.NewText()
创建,传入文本字符串和颜色。该对象可设置对齐方式和字体样式。对齐方式通过设置Text
对象的Alignment
字段值,取值有:
TextAlignLeading
:左对齐;
TextAlignCenter
:中间对齐;
TextAlignTrailing
:右对齐。
字体样式通过设置Text
对象的TextStyle
字段值,TextStyle
是一个结构体:
type TextStyle struct {
Bold bool
Italic bool
Monospace bool
}
对应字段设置为true
将显示对应的样式:
Bold
:粗体;
Italic
:斜体;
Monospace
:系统等宽字体。
我们还可以通过设置环境变量FYNE_FONT
为一个.ttf
文件从而使用外部字体。
canvas.Line
是线段,通过canvas.NewLine()
创建,传入颜色。可以通过line.StrokeWidth
设置线段宽度。默认情况下,线段是从父控件或画布的左上角到右下角的。可通过line.Move()
和line.Resize()
修改位置。
canvas.Circle
是圆形,通过canvas.NewCircle()
创建,传入颜色。另外通过StrokeColor
和StrokeWidth
设置圆形边框的颜色和宽度。
canvas.Image
是图像,可以通过已加载的程序资源创建(canvas.NewImageFromResource()
),传入资源对象。或通过文件路径创建(canvas.NewImageFromFile()
),传入文件路径。或通过已构造的image.Image
对象创建(canvas.NewImageFromImage()
)。可以通过FillMode
设置图像的填充模式:
ImageFillStretch
:拉伸,填满空间;
ImageFillContain
:保持宽高比;
ImageFillOriginal
:保持原始大小,不缩放。
下面程序演示了这 3 种创建图像的方式:
package main
import (
"image"
"image/color"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
func main() {
a := app.New()
w := a.NewWindow("Hello")
img1 := canvas.NewImageFromResource(theme.FyneLogo())
img1.FillMode = canvas.ImageFillOriginal
img2 := canvas.NewImageFromFile("./luffy.jpg")
img2.FillMode = canvas.ImageFillOriginal
image := image.NewAlpha(image.Rectangle{image.Point{0, 0}, image.Point{100, 100}})
for i := 0; i < 100; i++ {
for j := 0; j < 100; j++ {
image.Set(i, j, color.Alpha{uint8(i % 256)})
}
}
img3 := canvas.NewImageFromImage(image)
img3.FillMode = canvas.ImageFillOriginal
container := fyne.NewContainerWithLayout(
layout.NewGridWrapLayout(fyne.NewSize(150, 150)),
img1, img2, img3)
w.SetContent(container)
w.ShowAndRun()
}
theme.FyneLogo()
是 Fyne 图标资源,luffy.jpg
是磁盘中的文件,最后创建一个image.Image
,从中生成canvas.Image
。
最后一种是梯度渐变效果,有两种类型canvas.LinearGradient
(线性渐变)和canvas.RadialGradient
(放射渐变),指从一种颜色渐变到另一种颜色。线性渐变又分为两种水平线性渐变和垂直线性渐变,分别通过canvas.NewHorizontalGradient()