关于 Fyne
Fyne 是使用 Go 语言编写的易于使用的 UI 工具包和应用程序 API。它旨在构建使用单个代码库在桌面和移动设备上运行的应用程序。

当前的版本是 1.2,该版本增加了对 iOS 和 Android 设备的支持,并提供了编写自定义窗口小部件的更简单方法。我们现在正在朝 1.3 迈进,它将添加数据绑定和一些更高级的小部件,例如表和列表。

使用条件
要使用 Fyne 开发应用,您将需要 Go 1.12 或更高版本。

设置好goproxy=https://goproxy.cn,采用go mod模式开发,可以自动下载依赖包。

编译注意事项

如果不带任何参数编译,fyne应用会先打开控制台窗口,然后才从控制台窗口打开应用。要取消启动时的控制台窗口,需要在编译时加入如下参数:-ldflags -H=windowsgui。如下:

go build -ldflags -H=windowsgui main.go

Helloworld示例:

 1     package main
 2 
 3     import "fyne.io/fyne/widget"
 4     import "fyne.io/fyne/app"
 5 
 6     func main() {
 7         app := app.New()
 8 
 9         w := app.NewWindow("Hello")
10         w.SetContent(widget.NewVBox(
11             widget.NewLabel("Hello Fyne!"),
12             widget.NewButton("Quit", func() {
13                 app.Quit()
14             }),
15         ))
16 
17         w.ShowAndRun()
18     }

运行效果:

Golang的GUI开发包fyne基本教程-风君雪科技博客

 一个更复杂的示例:

 1 // Package main provides various examples of Fyne API capabilities
 2 package main
 3 
 4 import (
 5     "fmt"
 6     "net/url"
 7 
 8     "fyne.io/fyne"
 9     "fyne.io/fyne/app"
10     "fyne.io/fyne/canvas"
11     "fyne.io/fyne/cmd/fyne_demo/data"
12     "fyne.io/fyne/cmd/fyne_demo/screens"
13     "fyne.io/fyne/layout"
14     "fyne.io/fyne/theme"
15     "fyne.io/fyne/widget"
16 )
17 
18 const preferenceCurrentTab = "currentTab"
19 
20 func parseURL(urlStr string) *url.URL {
21     link, err := url.Parse(urlStr)
22     if err != nil {
23         fyne.LogError("Could not parse URL", err)
24     }
25 
26     return link
27 }
28 
29 func welcomeScreen(a fyne.App) fyne.CanvasObject {
30     logo := canvas.NewImageFromResource(data.FyneScene)
31     logo.SetMinSize(fyne.NewSize(228, 167))
32 
33     return widget.NewVBox(
34         widget.NewLabelWithStyle("Welcome to the Fyne toolkit demo app", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
35         layout.NewSpacer(),
36         widget.NewHBox(layout.NewSpacer(), logo, layout.NewSpacer()),
37 
38         widget.NewHBox(layout.NewSpacer(),
39             widget.NewHyperlink("fyne.io", parseURL("https://fyne.io/")),
40             widget.NewLabel("-"),
41             widget.NewHyperlink("documentation", parseURL("https://fyne.io/develop/")),
42             widget.NewLabel("-"),
43             widget.NewHyperlink("sponsor", parseURL("https://github.com/sponsors/fyne-io")),
44             layout.NewSpacer(),
45         ),
46         layout.NewSpacer(),
47 
48         widget.NewGroup("Theme",
49             fyne.NewContainerWithLayout(layout.NewGridLayout(2),
50                 widget.NewButton("Dark", func() {
51                     a.Settings().SetTheme(theme.DarkTheme())
52                 }),
53                 widget.NewButton("Light", func() {
54                     a.Settings().SetTheme(theme.LightTheme())
55                 }),
56             ),
57         ),
58     )
59 }
60 
61 func main() {
62     a := app.NewWithID("io.fyne.demo")
63     a.SetIcon(theme.FyneLogo())
64 
65     w := a.NewWindow("Fyne Demo")
66     w.SetMainMenu(fyne.NewMainMenu(fyne.NewMenu("File",
67         fyne.NewMenuItem("New", func() { fmt.Println("Menu New") }),
68         // a quit item will be appended to our first menu
69     ), fyne.NewMenu("Edit",
70         fyne.NewMenuItem("Cut", func() { fmt.Println("Menu Cut") }),
71         fyne.NewMenuItem("Copy", func() { fmt.Println("Menu Copy") }),
72         fyne.NewMenuItem("Paste", func() { fmt.Println("Menu Paste") }),
73     )))
74     w.SetMaster()
75 
76     tabs := widget.NewTabContainer(
77         widget.NewTabItemWithIcon("Welcome", theme.HomeIcon(), welcomeScreen(a)),
78         widget.NewTabItemWithIcon("Widgets", theme.ContentCopyIcon(), screens.WidgetScreen()),
79         widget.NewTabItemWithIcon("Graphics", theme.DocumentCreateIcon(), screens.GraphicsScreen()),
80         widget.NewTabItemWithIcon("Windows", theme.ViewFullScreenIcon(), screens.DialogScreen(w)),
81         widget.NewTabItemWithIcon("Advanced", theme.SettingsIcon(), screens.AdvancedScreen(w)))
82     tabs.SetTabLocation(widget.TabLocationLeading)
83     tabs.SelectTabIndex(a.Preferences().Int(preferenceCurrentTab))
84     w.SetContent(tabs)
85 
86     w.ShowAndRun()
87     a.Preferences().SetInt(preferenceCurrentTab, tabs.CurrentTabIndex())
88 }

运行效果:

 Golang的GUI开发包fyne基本教程-风君雪科技博客

 中文支持

首先,下载一个 TTF 格式的中文字库,可以下载思源字体的字库。需要注意的是,字库的格式必须是 TTF 的,否则会报错。

然后添加一个环境变量 FYNE_FONT,指定下载好的字库文件:

Golang的GUI开发包fyne基本教程-风君雪科技博客

也可以用YaHeiConsolasHybrid.ttf字体,只要设置好FYNE_FONT变量就可以。
 
下面是一个查询网络IP属主的示例:

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "fyne.io/fyne"
 6     "fyne.io/fyne/app"
 7     "fyne.io/fyne/theme"
 8     "fyne.io/fyne/widget"
 9     "fyne.io/fyne/layout"
10     "net/http"
11     "io/ioutil"
12     "encoding/json"
13 )
14 
15 var ip = widget.NewLabel("")
16 var position = widget.NewLabel("")
17 var isp = widget.NewLabel("")
18 
19 type IpInfo struct {
20     Code int `json:"code"`
21     Message string `json:"msg"`
22     Data `json:"data"`
23 }
24 
25 type Data struct {
26     IP string `json:"ip"`
27     Position string `json:"pos"`
28     Isp string `json:"isp"`
29 }
30 
31 func main() {
32     a := app.New()
33     a.Settings().SetTheme(theme.LightTheme())
34     w := a.NewWindow("Demo")
35     w.Resize(fyne.NewSize(600, 500))
36     w.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayoutWithColumns(2), info(GetIpInfo("")), query()))
37     w.ShowAndRun()
38 }
39 
40 func GetIpInfo(ip string) string {
41     if len(ip) == 0 {
42         return ""
43     }
44 
45     url := fmt.Sprintf("http://v1.alapi.cn/api/ip?ip=%s&format=json", ip)
46 
47     resp, err :=   http.Get(url)
48     if err != nil {
49         // handle error
50     }
51 
52     defer resp.Body.Close()
53     body, err := ioutil.ReadAll(resp.Body)
54     if err != nil {
55         // handle error
56     }
57 
58     return string(body)
59 }
60 
61 func query() fyne.CanvasObject {
62     ip := widget.NewEntry()
63     ip.SetPlaceHolder("Please input IP address")
64 
65     form := &widget.Form{
66         OnSubmit: func() {
67             info(GetIpInfo(ip.Text))
68         },
69     }
70 
71     form.Append("IP", ip)
72     query := widget.NewGroup("Query", form)
73     return widget.NewScrollContainer(query)
74 }
75 
76 func info(response string) fyne.CanvasObject {
77     var i IpInfo
78     json.Unmarshal([]byte(response),&i)
79 
80     screen := widget.NewForm(
81         &widget.FormItem{Text: "IP地址:", Widget: ip},
82         &widget.FormItem{Text: "所属地:", Widget: position},
83         &widget.FormItem{Text: "供应商:", Widget: isp},
84     )
85 
86     ip.SetText(i.IP)
87     position.SetText(i.Position)
88     isp.SetText(i.Isp)
89 
90     info := widget.NewGroup("Info", screen)
91     return widget.NewScrollContainer(info)
92 }

运行效果如下:

Golang的GUI开发包fyne基本教程-风君雪科技博客