简介

fynefynefynefynefyne

快速使用

本文代码使用 Go Modules。

先初始化:

$ mkdir fyne && cd fyne
$ go mod init github.com/darjun/go-daily-lib/fyne
复制代码
fynegccgccgcc
TDM-GCCPATHgcc -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()
}
复制代码

运行结果如下:

fynefynemyAppapp.New()myAppmyApp.NewWindow("Hello")myApp.NewWindow()
fynewidget.NewXXX()XXXLabelmyWin.ShowAndRun()myWin.ShowAndRun()
myWin.Show()
myApp.Run()
复制代码
myWin.Show()myApp.Run()
fynemyWin.Resize()Hello Fyne!
fyne
fyne
fyne.io/fynefynefyne.io/fyne/appfyne.io/fyne/canvasFynefyne.io/fyne/dialogfyne.io/fyne/layoutfyne.io/fyne/widgetfyne

Canvas

fyneCanvasCanvasObjectCanvas.SetContent()CanvasLayoutContainercanvas
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.Rectanglecanvas.NewRectangle()
canvas.Textcanvas.NewText()TextAlignment
TextAlignLeadingTextAlignCenterTextAlignTrailing
TextTextStyleTextStyle
type TextStyle struct {
  Bold      bool
  Italic    bool
  Monospace bool
}
复制代码
true
BoldItalicMonospace
FYNE_FONT.ttf
canvas.Linecanvas.NewLine()line.StrokeWidthline.Move()line.Resize()
canvas.Circlecanvas.NewCircle()StrokeColorStrokeWidth
canvas.Imagecanvas.NewImageFromResource()canvas.NewImageFromFile()image.Imagecanvas.NewImageFromImage()FillMode
ImageFillStretchImageFillContainImageFillOriginal

下面程序演示了这 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()luffy.jpgimage.Imagecanvas.Image
canvas.LinearGradientcanvas.RadialGradientcanvas.NewHorizontalGradient()canvas.NewVerticalGradient()canvas.NewRadialGradient()
func main() {
  a := app.New()
  w := a.NewWindow("Canvas")

  gradient := canvas.NewRadialGradient(color.White, color.Transparent)
  w.SetContent(gradient)
  w.Resize(fyne.NewSize(200, 200))
  w.ShowAndRun()
}
复制代码

运行效果如下:

放射效果就是从中心向周围渐变。

Widget

Fyne

Label

Labelcanvas.TextLabel\n
func main() {
  myApp := app.New()
  myWin := myApp.NewWindow("Label")

  l1 := widget.NewLabel("Name")
  l2 := widget.NewLabel("da\njun")

  container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), l1, l2)
  myWin.SetContent(container)
  myWin.Resize(fyne.NewSize(150, 150))
  myWin.ShowAndRun()
}
复制代码
widget.Label\n

Button

ButtonButtonwidget.NewButton()widget.NewButtonWithIcon()fyne.Resource
func main() {
  myApp := app.New()
  myWin := myApp.NewWindow("Button")

  btn1 := widget.NewButton("text button", func() {
    fmt.Println("text button clicked")
  })

  btn2 := widget.NewButtonWithIcon("icon", theme.HomeIcon(), func() {
    fmt.Println("icon button clicked")
  })

  container := fyne.NewContainerWithLayout(layout.NewVBoxLayout(), btn1, btn2)
  myWin.SetContent(container)
  myWin.Resize(fyne.NewSize(150, 50))
  myWin.ShowAndRun()
}
复制代码
theme

点击按钮,对应的回调就会被调用,试试看!

Box

BoxBoxBox Layoutwidget.NewHBox()widget.NewVBox()widget.BoxAppend()Prepend()
func main() {
  myApp := app.New()
  myWin := myApp.NewWindow("Box")

  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("Appended"))
  }))
  content.Append(widget.NewButton("Prepend", func() {
    content.Prepend(widget.NewLabel("Prepended"))
  }))

  myWin.SetContent(content)
  myWin.Resize(fyne.NewSize(150, 150))
  myWin.ShowAndRun()
}
复制代码
widget.BoxLabel

Entry

Entrywidget.NewEntry()TextOnChangedOnChangedSetReadOnly(true)SetPlaceHolder()MultilineNewPasswordEntry()
func main() {
  myApp := app.New()
  myWin := myApp.NewWindow("Entry")

  nameEntry := widget.NewEntry()
  nameEntry.SetPlaceHolder("input name")
  nameEntry.OnChanged = func(content string) {
    fmt.Println("name:", nameEntry.Text, "entered")
  }

  passEntry := widget.NewPasswordEntry()
  passEntry.SetPlaceHolder("input password")

  nameBox := widget.NewHBox(widget.NewLabel("Name"), layout.NewSpacer(), nameEntry)
  passwordBox := widget.NewHBox(widget.NewLabel("Password"), layout.NewSpacer(), passEntry)

  loginBtn := widget.NewButton("Login", func() {
    fmt.Println("name:", nameEntry.Text, "password:", passEntry.Text, "login in")
  })

  multiEntry := widget.NewEntry()
  multiEntry.SetPlaceHolder("please enter\nyour description")
  multiEntry.MultiLine = true

  content := widget.NewVBox(nameBox, passwordBox, loginBtn, multiEntry)
  myWin.SetContent(content)
  myWin.ShowAndRun()
}
复制代码

这里我们实现了一个简单的登录界面:

Checkbox/Radio/Select
CheckBoxwidget.NewCheck()bool
Radiowidget.NewRadio()Selected
SelectSelectwidget.NewSelect()NewRadio()
func main() {
  myApp := app.New()
  myWin := myApp.NewWindow("Choices")

  nameEntry := widget.NewEntry()
  nameEntry.SetPlaceHolder("input name")

  passEntry := widget.NewPasswordEntry()
  passEntry.SetPlaceHolder("input password")

  repeatPassEntry := widget.NewPasswordEntry()
  repeatPassEntry.SetPlaceHolder("repeat password")

  nameBox := widget.NewHBox(widget.NewLabel("Name"), layout.NewSpacer(), nameEntry)
  passwordBox := widget.NewHBox(widget.NewLabel("Password"), layout.NewSpacer(), passEntry)
  repeatPasswordBox := widget.NewHBox(widget.NewLabel("Repeat Password"), layout.NewSpacer(), repeatPassEntry)

  sexRadio := widget.NewRadio([]string{"male", "female", "unknown"}, func(value string) {
    fmt.Println("sex:", value)
  })
  sexBox := widget.NewHBox(widget.NewLabel("Sex"), sexRadio)

  football := widget.NewCheck("football", func(value bool) {
    fmt.Println("football:", value)
  })
  basketball := widget.NewCheck("basketball", func(value bool) {
    fmt.Println("basketball:", value)
  })
  pingpong := widget.NewCheck("pingpong", func(value bool) {
    fmt.Println("pingpong:", value)
  })
  hobbyBox := widget.NewHBox(widget.NewLabel("Hobby"), football, basketball, pingpong)

  provinceSelect := widget.NewSelect([]string{"anhui", "zhejiang", "shanghai"}, func(value string) {
    fmt.Println("province:", value)
  })
  provinceBox := widget.NewHBox(widget.NewLabel("Province"), layout.NewSpacer(), provinceSelect)

  registerBtn := widget.NewButton("Register", func() {
    fmt.Println("name:", nameEntry.Text, "password:", passEntry.Text, "register")
  })

  content := widget.NewVBox(nameBox, passwordBox, repeatPasswordBox,
    sexBox, hobbyBox, provinceBox, registerBtn)
  myWin.SetContent(content)
  myWin.ShowAndRun()
}
复制代码

这里我们实现了一个简单的注册界面:

Form

FormLabelOnSubmitOnCancelButtonwidget.NewForm()widget.FormItemFormLabelFormAppend(label, widget)
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Form")

  nameEntry := widget.NewEntry()
  passEntry := widget.NewPasswordEntry()

  form := widget.NewForm(
    &widget.FormItem{"Name", nameEntry},
    &widget.FormItem{"Pass", passEntry},
  )
  form.OnSubmit = func() {
    fmt.Println("name:", nameEntry.Text, "pass:", passEntry.Text, "login in")
  }
  form.OnCancel = func() {
    fmt.Println("login canceled")
  }

  myWindow.SetContent(form)
  myWindow.Resize(fyne.NewSize(150, 150))
  myWindow.ShowAndRun()
}
复制代码
FormForm
SubmitCancel

ProgressBar

ProgressBarwidget.NewProgressBar()0.01.1Min/MaxSetValue()
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("ProgressBar")

  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)
  myWindow.SetContent(content)
  myWindow.Resize(fyne.NewSize(150, 150))
  myWindow.ShowAndRun()
}
复制代码

在另一个 goroutine 中更新进度。效果如下:

TabContainer

TabContainerwidget.NewTabContainer()widget.TabItemwidget.TabItemwidget.NewTabItem(label, widget)
TabLocationBottomTabLocationLeadingTabLocationTrailing

看示例:

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

  nameLabel := widget.NewLabel("Name: dajun")
  sexLabel := widget.NewLabel("Sex: male")
  ageLabel := widget.NewLabel("Age: 18")
  addressLabel := widget.NewLabel("Province: shanghai")
  addressLabel.Hide()
  profile := widget.NewVBox(nameLabel, sexLabel, ageLabel, addressLabel)

  musicRadio := widget.NewRadio([]string{"on", "off"}, func(string) {})
  showAddressCheck := widget.NewCheck("show address?", func(value bool) {
    if !value {
      addressLabel.Hide()
    } else {
      addressLabel.Show()
    }
  })
  memberTypeSelect := widget.NewSelect([]string{"junior", "senior", "admin"}, func(string) {})

  setting := widget.NewForm(
    &widget.FormItem{"music", musicRadio},
    &widget.FormItem{"check", showAddressCheck},
    &widget.FormItem{"member type", memberTypeSelect},
  )

  tabs := widget.NewTabContainer(
    widget.NewTabItem("Profile", profile),
    widget.NewTabItem("Setting", setting),
  )

  myWindow.SetContent(tabs)
  myWindow.Resize(fyne.NewSize(200, 200))
  myWindow.ShowAndRun()
}
复制代码
show address?

Toolbar

Toolbarwidget.NewToolbar()widget.ToolbarItemToolbarItemActionSeparatorSpacerwidget.NewToolbarItemAction(resource, callback)widget.NewToolbarSeparator()widget.NewToolbarSpacer()
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Toolbar")

  toolbar := widget.NewToolbar(
    widget.NewToolbarAction(theme.DocumentCreateIcon(), func() {
      fmt.Println("New document")
    }),
    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() {
      log.Println("Display help")
    }),
  )

  content := fyne.NewContainerWithLayout(
    layout.NewBorderLayout(toolbar, nil, nil, nil),
    toolbar, widget.NewLabel(`Lorem ipsum dolor, 
    sit amet consectetur adipisicing elit.
    Quidem consectetur ipsam nesciunt,
    quasi sint expedita minus aut,
    porro iusto magnam ducimus voluptates cum vitae.
    Vero adipisci earum iure consequatur quidem.`),
  )
  myWindow.SetContent(content)
  myWindow.ShowAndRun()
}
复制代码
BorderLayout

扩展控件

widget.IconExtendBaseWidget()
type tappableIcon struct {
  widget.Icon
}

func newTappableIcon(res fyne.Resource) *tappableIcon {
  icon := &tappableIcon{}
  icon.ExtendBaseWidget(icon)
  icon.SetResource(res)

  return icon
}
复制代码

然后实现相关的接口:

// src/fyne.io/fyne/canvasobject.go
// 鼠标左键
type Tappable interface {
  Tapped(*PointEvent)
}

// 鼠标右键或长按
type SecondaryTappable interface {
  TappedSecondary(*PointEvent)
}

// 双击
type DoubleTappable interface {
  DoubleTapped(*PointEvent)
}
复制代码

接口实现:

func (t *tappableIcon) Tapped(e *fyne.PointEvent) {
  log.Println("I have been left tapped at", e)
}

func (t *tappableIcon) TappedSecondary(e *fyne.PointEvent) {
  log.Println("I have been right tapped at", e)
}

func (t *tappableIcon) DoubleTapped(e *fyne.PointEvent) {
  log.Println("I have been double tapped at", e)
}
复制代码

最后使用:

func main() {
  a := app.New()
  w := a.NewWindow("Tappable")
  w.SetContent(newTappableIcon(theme.FyneLogo()))
  w.Resize(fyne.NewSize(200, 200))
  w.ShowAndRun()
}
复制代码

运行,点击图标控制台有相应输出:

2020/06/18 06:44:02 I have been left tapped at &{{110 97} {106 93}}
2020/06/18 06:44:03 I have been left tapped at &{{110 97} {106 93}}
2020/06/18 06:44:05 I have been right tapped at &{{88 102} {84 98}}
2020/06/18 06:44:06 I have been right tapped at &{{88 102} {84 98}}
2020/06/18 06:44:06 I have been left tapped at &{{88 101} {84 97}}
2020/06/18 06:44:07 I have been double tapped at &{{88 101} {84 97}}
复制代码
fyne.PointEvent

Layout

Layoutfyne.NewContainerWithLayout()

BoxLayout

BoxLayoutfynelayout.NewHBoxLayout()layout.NewVBoxLayout()MinSize().WidthMinSize().Height

垂直布局中的控件都排列在一列中,每个控件的高度等于其内容的最小高度,它们都拥有相同的宽度,即所有控件的最大宽度。

BoxLayoutlayout.NewSpacer()layout.NewSpacer()layout.NewSpacer()layout.NewSpacer()
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Box Layout")

  hcontainer1 := fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
    canvas.NewText("left", color.White),
    canvas.NewText("right", color.White))

  // 左对齐
  hcontainer2 := fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
    layout.NewSpacer(),
    canvas.NewText("left", color.White),
    canvas.NewText("right", color.White))

  // 右对齐
  hcontainer3 := fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
    canvas.NewText("left", color.White),
    canvas.NewText("right", color.White),
    layout.NewSpacer())

  // 中间对齐
  hcontainer4 := fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
    layout.NewSpacer(),
    canvas.NewText("left", color.White),
    canvas.NewText("right", color.White),
    layout.NewSpacer())

  // 两边对齐
  hcontainer5 := fyne.NewContainerWithLayout(layout.NewHBoxLayout(),
    canvas.NewText("left", color.White),
    layout.NewSpacer(),
    canvas.NewText("right", color.White))

  myWindow.SetContent(fyne.NewContainerWithLayout(layout.NewVBoxLayout(),
    hcontainer1, hcontainer2, hcontainer3, hcontainer4, hcontainer5))
  myWindow.Resize(fyne.NewSize(200, 200))
  myWindow.ShowAndRun()
}
复制代码

运行效果:

GridLayout

GridLayoutlayout.NewGridLayout(cols)
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Grid Layout")

  img1 := canvas.NewImageFromResource(theme.FyneLogo())
  img2 := canvas.NewImageFromResource(theme.FyneLogo())
  img3 := canvas.NewImageFromResource(theme.FyneLogo())
  myWindow.SetContent(fyne.NewContainerWithLayout(layout.NewGridLayout(2),
    img1, img2, img3))
  myWindow.Resize(fyne.NewSize(300, 300))
  myWindow.ShowAndRun()
}
复制代码

运行效果:

该布局有个优势,我们缩放界面时,控件会自动调整大小。试试看~

GridWrapLayout

GridWrapLayoutGridLayoutGridWrapLayoutsizesizesize
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Grid Wrap Layout")

  img1 := canvas.NewImageFromResource(theme.FyneLogo())
  img2 := canvas.NewImageFromResource(theme.FyneLogo())
  img3 := canvas.NewImageFromResource(theme.FyneLogo())
  myWindow.SetContent(
    fyne.NewContainerWithLayout(
      layout.NewGridWrapLayout(fyne.NewSize(150, 150)),
      img1, img2, img3))
  myWindow.ShowAndRun()
}
复制代码

初始:

加大宽度:

再加大宽度:

BorderLayout

BorderLayoutToolbarBorderLayoutlayout.NewBorderLayout(top, bottom, left, right)
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Border Layout")

  left := canvas.NewText("left", color.White)
  right := canvas.NewText("right", color.White)
  top := canvas.NewText("top", color.White)
  bottom := canvas.NewText("bottom", color.White)
  content := widget.NewLabel(`Lorem ipsum dolor, 
  sit amet consectetur adipisicing elit.
  Quidem consectetur ipsam nesciunt,
  quasi sint expedita minus aut,
  porro iusto magnam ducimus voluptates cum vitae.
  Vero adipisci earum iure consequatur quidem.`)

  container := fyne.NewContainerWithLayout(
    layout.NewBorderLayout(top, bottom, left, right),
    top, bottom, left, right, content,
  )
  myWindow.SetContent(container)
  myWindow.ShowAndRun()
}
复制代码

效果:

FormLayout

FormLayoutGridLayout
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Border Layout")

  nameLabel := canvas.NewText("Name", color.Black)
  nameValue := canvas.NewText("dajun", color.White)
  ageLabel := canvas.NewText("Age", color.Black)
  ageValue := canvas.NewText("18", color.White)

  container := fyne.NewContainerWithLayout(
    layout.NewFormLayout(),
    nameLabel, nameValue, ageLabel, ageValue,
  )
  myWindow.SetContent(container)
  myWindow.Resize(fyne.NewSize(150, 150))
  myWindow.ShowAndRun()
}
复制代码

运行效果:

CenterLayout

CenterLayoutCenterLayout
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Center Layout")

  image := canvas.NewImageFromResource(theme.FyneLogo())
  image.FillMode = canvas.ImageFillOriginal
  text := canvas.NewText("Fyne Logo", color.Black)

  container := fyne.NewContainerWithLayout(
    layout.NewCenterLayout(),
    image, text,
  )
  myWindow.SetContent(container)
  myWindow.ShowAndRun()
}
复制代码

运行结果:

Fyne Logotextimage

MaxLayout

MaxLayoutCenterLayoutMaxLayoutCenterLayoutImageFillOriginalMinSize(1, 1)fmt.Println(image.MinSize())
MaxLayout
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Max Layout")

  image := canvas.NewImageFromResource(theme.FyneLogo())
  text := canvas.NewText("Fyne Logo", color.Black)

  container := fyne.NewContainerWithLayout(
    layout.NewMaxLayout(),
    image, text,
  )
  myWindow.SetContent(container)
  myWindow.Resize(fyne.Size(200, 200))
  myWindow.ShowAndRun()
}
复制代码

运行结果:

canvas.TextAlignmentfyne.TextAlignCenter

自定义 Layout

layoutfyne.Layout
// src/fyne.io/fyne/layout.go
type Layout interface {
  Layout([]CanvasObject, Size)
  MinSize(objects []CanvasObject) Size
}
复制代码
fyne.Layout
type diagonal struct {
}

func (d *diagonal) MinSize(objects []fyne.CanvasObject) fyne.Size {
  w, h := 0, 0
  for _, o := range objects {
    childSize := o.MinSize()

    w += childSize.Width
    h += childSize.Height
  }

  return fyne.NewSize(w, h)
}

func (d *diagonal) Layout(objects []fyne.CanvasObject, containerSize fyne.Size) {
  pos := fyne.NewPos(0, 0)
  for _, o := range objects {
    size := o.MinSize()
    o.Resize(size)
    o.Move(pos)

    pos = pos.Add(fyne.NewPos(size.Width, size.Height))
  }
}
复制代码
MinSize()MinSizeLayout()
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Diagonal Layout")

  img1 := canvas.NewImageFromResource(theme.FyneLogo())
  img1.FillMode = canvas.ImageFillOriginal
  img2 := canvas.NewImageFromResource(theme.FyneLogo())
  img2.FillMode = canvas.ImageFillOriginal
  img3 := canvas.NewImageFromResource(theme.FyneLogo())
  img3.FillMode = canvas.ImageFillOriginal

  container := fyne.NewContainerWithLayout(
    &diagonal{},
    img1, img2, img3,
  )
  myWindow.SetContent(container)
  myWindow.ShowAndRun()
}
复制代码

运行结果:

fyne demo

fyne
$ go get fyne.io/fyne/cmd/fyne_demo
$ fyne_demo
复制代码

效果图:

fyne
fynefynefynefyne
$ go get fyne.io/fyne/cmd/fyne
复制代码
fyne$GOPATH/bin$GOPATH/bin$PATHfyne

静态资源

fynefyne.FyneLogo()image1.png/image2.jpgfyne bundle
$ fyne bundle image1.png >> bundled.go
$ fyne bundle -append image2.jpg >> bundled.go
复制代码
-append
// bundled.go
package main

import "fyne.io/fyne"

var resourceImage1Png = &fyne.StaticResource{
  StaticName: "image1.png",
  StaticContent: []byte{...}}

var resourceImage2Jpg = &fyne.StaticResource{
  StaticName: "image2.jpg",
  StaticContent: []byte{...}}
复制代码
canvas.NewImageFromResource()resourceImage1PngresourceImage2Jpgcanvas.Image
func main() {
  myApp := app.New()
  myWindow := myApp.NewWindow("Bundle Resource")

  img1 := canvas.NewImageFromResource(resourceImage1Png)
  img1.FillMode = canvas.ImageFillOriginal
  img2 := canvas.NewImageFromResource(resourceImage2Jpg)
  img2.FillMode = canvas.ImageFillOriginal
  img3 := canvas.NewImageFromResource(theme.FyneLogo())
  img3.FillMode = canvas.ImageFillOriginal

  container := fyne.NewContainerWithLayout(
    layout.NewGridLayout(1),
    img1, img2, img3,
  )
  myWindow.SetContent(container)
  myWindow.ShowAndRun()
}
复制代码

运行结果:

go run main.gogo run .
theme.FyneLogo()bundled-icons.go
// src/fyne.io/fyne/theme/icons.go
func FyneLogo() fyne.Resource {
  return fynelogo
}

// src/fyne.io/fyne/theme/bundled-icons.go
var fynelogo = &fyne.StaticResource{
  StaticName: "fyne.png",
  StaticContent: []byte{}}
复制代码

发布应用程序

fynefyne packagefyne package.exe.app.tar.xz
exe
$ fyne package -os windows -icon icon.jpg
复制代码
bundle.exefyne.sysobundle.exe
fyne

总结

fynefynefyne

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue????

参考

欢迎关注我的微信公众号【GoUpUp】,共同学习,一起进步~