关于 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 }
运行效果:
一个更复杂的示例:
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 }
运行效果:
中文支持
首先,下载一个 TTF 格式的中文字库,可以下载思源字体的字库。需要注意的是,字库的格式必须是 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 }
运行效果如下: