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
所有的窗体控件和交互元素都在这个子包中。package main
import (
"fyne.io/fyne/app"
"fyne.io/fyne/container"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Hello")
hello := widget.NewLabel("Hello Fyne!")
w.SetContent(container.NewVBox(
hello,
widget.NewButton("Hi!", func() {
hello.SetText("Welcome:")
}),
))
w.ShowAndRun()
}
func main() {
test()
}
效果,当你点击
Hi!
点击按钮时上面的文字变为了Welcome:
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 test() {
a := app.New()
w := a.NewWindow("Canvas")
rect := canvas.NewRectangle(color.White)
text := canvas.NewText("Hello Text", color.Black)
text.Alignment = fyne.TextAlignTrailing
text.TextStyle = fyne.TextStyle{Italic: true}
line := canvas.NewLine(color.Black)
line.StrokeWidth = 4
// 绘制LoGo图
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, image, raster, gradient,
)
w.SetContent(container)
w.ShowAndRun()
}
func main() {
test()
}
效果图
窗体控件是一个Fyne应用程序的主要组成部分。它们能适配当前的主题,并且处理与用户的交互。
Label(标签)是一个简单的空间,用于字符串的展示。
package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Label")
l1 := widget.NewLabel("Name")
// 可以识别转义字符
l2 := widget.NewLabel("Yu\nhandsome")
container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), l1, l2)
w.SetContent(container)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
package main
import (
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Button")
btn1 := widget.NewButton("text button", func() {
fmt.Println("btn1 button clicked")
})
btn2 := widget.NewButtonWithIcon("text button", theme.HomeIcon(), func() {
fmt.Println("Home")
})
container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), btn1, btn2)
w.SetContent(container)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
在这个案例中我们创建两个按钮,两种共同之处是都需要传入按钮名称和按钮点击触发的执行函数。值得一提的是我们还可以加载外部的图标进行装饰。
效果
package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Button")
content := widget.NewVBox(
widget.NewLabel("The top row of VBox"),
widget.NewHBox(
widget.NewLabel("Label 1"),
widget.NewLabel("Label 2"),
),
)
content.Append(widget.NewButton("Append", func() {
content.Append(widget.NewLabel("Append"))
}))
w.SetContent(content)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
盒子控件Box,其实就是一个容器类似于html中的div。不同的是在盒子创建时我们可选择widget.NewHBox()或widget.NewVBox()创建内部不同布局的盒子。在创建好Box盒子后,我们
效果
输入框(Entry)控件用于给用户输入简单的文本内容。我们可以调用widget.NewEntry()即可创建一个输入框控件。同时我们可以通过访问其Text字段来获得输入框中的内容。注册OnChange回调函数。每当内容有修改时,OnChange就会被调用。SetPlaceHolder()用来设置占位字符,设置字段MultilIne让输入框接受多行文本。此外,NewPasswordEntry()可创建一个密码输入框,输入的文本不会以明文显示。
package main
import (
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Entry")
input := widget.NewEntry()
input.SetPlaceHolder("please enter name")
input.OnChanged = func(content string) {
fmt.Println("name", content)
}
passInput := widget.NewPasswordEntry()
passInput.SetPlaceHolder("please enter password")
line1 := widget.NewVBox(widget.NewLabel("Name"), layout.NewSpacer(), input)
line2 := widget.NewVBox(widget.NewLabel("Password"), layout.NewSpacer(), passInput)
loginBtn := widget.NewButton("Login", func() {
fmt.Printf("name: %s, password: %s\n Login in", input.Text, passInput.Text)
})
content := widget.NewVBox(line1, line2, loginBtn)
w.SetContent(content)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
这里的话主要是关于三种选择框的使用:
package main
import (
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Entry")
input := widget.NewEntry()
input.SetPlaceHolder("please enter name")
input.OnChanged = func(content string) {
fmt.Println("name", content)
}
passInput := widget.NewPasswordEntry()
passInput.SetPlaceHolder("please enter password")
line1 := widget.NewVBox(widget.NewLabel("Name"), layout.NewSpacer(), input)
line2 := widget.NewVBox(widget.NewLabel("Password"), layout.NewSpacer(), passInput)
loginBtn := widget.NewButton("Login", func() {
fmt.Printf("name: %s, password: %s\n Login in", input.Text, passInput.Text)
})
sexRadio := widget.NewRadio([]string{"male", "female"}, func(value string) {
fmt.Printf("sex %s\n", value)
})
sexBox := widget.NewHBox(widget.NewLabel("Sex"), sexRadio)
provinceSelect := widget.NewSelect([]string{"beijing", "zhejiang", "shanghai"}, func(value string) {
fmt.Println("province:", value)
})
provinceBox := widget.NewHBox(widget.NewLabel("Province"), layout.NewSpacer(), provinceSelect)
content := widget.NewVBox(line1, line2, sexBox, provinceBox, loginBtn)
w.SetContent(content)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
表单控件Form用于对很多Label和输入控件进行布局。如果指定了OnSubmit或OnCancek函数,表单控件
package main
import (
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Entry")
input := widget.NewEntry()
passWord := widget.NewPasswordEntry()
form := widget.NewForm(
&widget.FormItem{Text: "Name", Widget: input},
&widget.FormItem{Text: "password", Widget: passWord},
)
form.OnSubmit = func() {
fmt.Printf("Name: %s\nPassword: %s\n", input.Text, passWord.Text)
}
form.OnCancel = func() {
fmt.Printf("Login end")
}
w.SetContent(form)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
进度条(ProgressBar)用来表示任务的进度。例如文件下载的进度。创建当大widget.NewProgressBar(),默认最小值0.0,最大值为1.1,可通过Min/Max字段,调用SetValue()方法来控制进度,还有一种进度条是循环动画,表示任务在进行中。
package main
import (
"time"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Entry")
bar1 := widget.NewProgressBar()
bar1.Min = 0
bar1.Max = 100
bar2 := widget.NewProgressBarInfinite()
go func() {
for i := 0; i <= 100; i++ {
time.Sleep(time.Millisecond * 500)
bar1.SetValue(float64(i))
}
}()
content := widget.NewVBox(bar1, bar2)
w.SetContent(content)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
标签容器(TabContainer)允许用户在不同的内容面板之间切换。标签可以是文本或图标。我们通过不同的方法创建不同位置的标签:
TabLocationBottom
:显示在底部;TabLocationLeading
:显示在顶部左边;TabLocationTrailing
:显示在顶部右边。package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("TabContainer")
label1 := widget.NewLabel("label1")
label11 := widget.NewLabel("label11")
label11.Hide()
label2 := widget.NewLabel("label2")
showCheck := widget.NewCheck("Hidden?", func(val bool) {
if val {
label11.Hide()
} else {
label11.Show()
}
})
b1 := widget.NewHBox(
label1,
label11,
showCheck,
)
b2 := widget.NewHBox(
label2,
)
tabs := widget.NewTabContainer(
widget.NewTabItem("11", b1),
widget.NewTabItem("22", b2),
)
w.SetContent(tabs)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
package main
import (
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Toolbar")
toolbar := widget.NewToolbar(
widget.NewToolbarAction(theme.DocumentCreateIcon(), func() {
fmt.Println("create new Doc")
}),
widget.NewToolbarSeparator(),
widget.NewToolbarAction(theme.ContentCutIcon(), func() {
fmt.Println("Cut")
}),
widget.NewToolbarAction(theme.ContentCopyIcon(), func() {
fmt.Println("Copy")
}),
widget.NewToolbarAction(theme.ContentPasteIcon(), func() {
fmt.Println("Paste")
}),
widget.NewToolbarSpacer(),
widget.NewToolbarAction(theme.HelpIcon(), func() {
fmt.Println("Display help")
}),
)
content := fyne.NewContainerWithLayout(
layout.NewBorderLayout(toolbar, nil, nil, nil),
toolbar, widget.NewLabel("show Toolbar"),
)
w.SetContent(content)
w.Resize(fyne.NewSize(150, 150))
w.ShowAndRun()
}
func main() {
test()
}
效果
下面我们通过扩展控件来让widget.Icon能够相应鼠标左键。
package main
import (
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/theme"
"fyne.io/fyne/widget"
)
type tappableIcon struct {
widget.Icon
}
func newTappableIcon(res fyne.Resource) *tappableIcon {
icon := &tappableIcon{}
icon.ExtendBaseWidget(icon)
icon.SetResource(res)
return icon
}
type Tappable interface {
Tapped(*fyne.PointEvent)
}
// 鼠标左键单机事件
func (t *tappableIcon) Tapped(e *fyne.PointEvent) {
// 输出鼠标相对于串口的绝对位置(窗口的左上角),相对位置(容器的左上角)
fmt.Println("tapped at", e)
}
func test() {
a := app.New()
w := a.NewWindow("Toolbar")
w.SetContent(newTappableIcon(theme.FyneLogo()))
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
布局(Layout)可以帮助我们在会使用控件的基础上,对控件的位置进行排版使之更加美观。
package main
import (
"image/color"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
)
func test() {
a := app.New()
w := a.NewWindow("Box Layout")
h1 := fyne.NewContainerWithLayout(
layout.NewHBoxLayout(),
canvas.NewText("left", color.Black),
layout.NewSpacer(),
canvas.NewText("right", color.Black),
)
h2 := fyne.NewContainerWithLayout(
layout.NewHBoxLayout(),
layout.NewSpacer(),
canvas.NewText("left", color.Black),
canvas.NewText("right", color.Black),
)
w.SetContent(fyne.NewContainerWithLayout(layout.NewVBoxLayout(), h1, h2))
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
网格布局,我们通过layout.NewGridLayout(cols)
,传入每行的列数来创建。使用该布局时,当我们缩放界面时控件的大小会自动调整
package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
func test() {
a := app.New()
w := a.NewWindow("Grid Layout")
img1 := canvas.NewImageFromResource(theme.FyneLogo())
img2 := canvas.NewImageFromResource(theme.FyneLogo())
img3 := canvas.NewImageFromResource(theme.FyneLogo())
w.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayout(2), img1, img2, img3))
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
GridWrapLayout相当于时GridLayout的扩展版,我们可以在创建时指定每个子控件的size,当界面大小变化了这些子控件就会重新排列。
package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
func test() {
a := app.New()
w := a.NewWindow("Grid Layout")
img1 := canvas.NewImageFromResource(theme.FyneLogo())
img2 := canvas.NewImageFromResource(theme.FyneLogo())
img3 := canvas.NewImageFromResource(theme.FyneLogo())
w.SetContent(fyne.NewContainerWithLayout(layout.NewGridWrapLayout(fyne.NewSize(100, 100)), img1, img2, img3))
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
从名字我们大致可以才出来,这是一种边框布局。当我们需要把控件设置在四个方向的边框上时可以使用,最典型的就是Toolbar的布局。
package main
import (
"image/color"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
)
func test() {
a := app.New()
w := a.NewWindow("Grid Layout")
left := canvas.NewText("left", color.Black)
right := canvas.NewText("right", color.Black)
top := canvas.NewText("top", color.Black)
bottom := canvas.NewText("bottom", color.Black)
container := fyne.NewContainerWithLayout(
layout.NewBorderLayout(top, bottom, left, right),
top, bottom, left, right,
)
w.SetContent(container)
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
表单布局其实就是两列的GridLayout,但是做了一些微调。
package main
import (
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
)
func test() {
a := app.New()
w := a.NewWindow("Grid Layout")
input1 := widget.NewEntry()
input1.SetPlaceHolder("enter name")
input2 := widget.NewEntry()
input2.SetPlaceHolder("enter age")
label1 := widget.NewLabel("Name")
label2 := widget.NewLabel("age")
container := fyne.NewContainerWithLayout(
layout.NewFormLayout(),
label1, input1,
label2, input2,
)
w.SetContent(container)
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
CenterLayout就是将空间布局在容器的中心位置,按照传入的顺序,后面传入的控件可能会遮挡前面传入的控件。
package main
import (
"image/color"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
func test() {
a := app.New()
w := a.NewWindow("Grid Layout")
image := canvas.NewImageFromResource(theme.FyneLogo())
// 设置图片为填充模式,会尽可能占满容器
image.FillMode = canvas.ImageFillOriginal
text := canvas.NewText("Fyne Logo", color.Black)
container := fyne.NewContainerWithLayout(
layout.NewCenterLayout(),
image, text,
)
w.SetContent(container)
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果
MaxLayout会让容器中的元素都显示为最大的尺寸。
package main
import (
"image/color"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/canvas"
"fyne.io/fyne/layout"
"fyne.io/fyne/theme"
)
func test() {
a := app.New()
w := a.NewWindow("Max Layout")
image := canvas.NewImageFromResource(theme.FyneLogo())
text := canvas.NewText("Fyne Logo", color.Black)
container := fyne.NewContainerWithLayout(
layout.NewMaxLayout(),
image, text,
)
w.SetContent(container)
w.Resize(fyne.NewSize(200, 200))
w.ShowAndRun()
}
func main() {
test()
}
效果