go
复制代码
package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"github.com/flopp/go-findfont"
"os"
"strconv"
"strings"
)
func init() {
//设置中文字体:解决中文乱码问题
fontPaths := findfont.List() // win系统中所有字体路径
for _, path := range fontPaths { // 循环遍历所有路径,选择设置的字体
if strings.Contains(path, "msyh.ttf") || strings.Contains(path, "simhei.ttf") || strings.Contains(path, "simsun.ttc") || strings.Contains(path, "simkai.ttf") || strings.Contains(path, "STHeiti Medium.ttc") || strings.Contains(path, "Songti.ttc") {
os.Setenv("FYNE_FONT", path) // 第一个系统存在的字体,设置为本应用字体
break
}
}
}
type MyWidget struct { // 存储一行添加的控件
id int // 本行控件的id,方便删除
myWidgets []fyne.CanvasObject // 本行控件
}
var (
l11, l12, l41 *widget.Label // 固定显示的标签,非动态添加的标签
b31 *widget.Button // 固定显示的按钮,非动态添加的按钮
sid int // 存储id自增值,用于区别动态添加的一行控件的唯一性
)
var myWidgetList []*MyWidget // 存储所有动态添加的控件行
var cc = &fyne.Container{} // 用于存储每行布局(或控件),方便更新整个竖向布局
func main() {
myApp := app.NewWithID("com.xiantianshizhong.gogyne") // 创建应用,设置唯一id
w := myApp.NewWindow("竖向中间位置动态添加一行控件") // 新建窗口,设置窗口标题
w.Resize(fyne.NewSize(800, 400)) // 设置窗口大小
w.CenterOnScreen() // 窗口居中显示
// ------------------动态添加控件前面的控件,本实例为第一行------------------
l11 = widget.NewLabel("添加控件行数:")
l12 = widget.NewLabel("0")
// ------------------动态添加控件后面的控件,本实例为添加控件按钮------------------
b31 = widget.NewButton("添加控件", func() {
addContainer(w) // 添加控件方法
})
// ------------------后面的控件,本实例为标签描述------------------
l41 = widget.NewLabel("其他控件...")
// ------------------刷新布局------------------
refreshlayout(w)
// ------------------显示窗口并运行应用------------------
w.ShowAndRun()
}
// 动态添加控件方法
func addContainer(w fyne.Window) { // 动态添加
n := len(myWidgetList) // 当前动态添加的控件数量
if n < 5 { // 最多添加5行
ll1 := widget.NewLabel("") // 本行序号,刷新布局时重新设置
ee := widget.NewEntry() // 文本框,本应用占位用,无意义
ll2 := widget.NewLabel("请点击右边按钮选择文件夹...") // 标签,用于显示选择的文件夹路径
bb1 := widget.NewButton("选择文件夹", func() { // 选择文件夹路径按钮
dialog.NewFolderOpen(func(uri fyne.ListableURI, err error) { // 文件夹选择对话框
if uri != nil { // 取消选择时为nil,不判断会报错退出应用
ll2.SetText(uri.Path()) // 将选择的文件夹路径赋值给标签显示
}
}, w).Show() // 展示对话框
})
var bb2 *widget.Button // 删除本行动态添加的控件按钮
bb2 = widget.NewButton("删除本行", func() {
var ctemp []*MyWidget // 临时存放不删除的动态控件行
for _, c := range myWidgetList { // 循环遍历所有动态添加的控件行
b, _ := c.myWidgets[4].(*widget.Button) // 获取每行的删除按钮
if bb2 == b { // 存储的删除按钮,与本按钮内存地址相同,为统一按钮
fmt.Println(c.id)
continue // 不存储本行控件,不存储就等于删除
}
ctemp = append(ctemp, c) // 将不删除的控件行,添加到临时控件列表
}
myWidgetList = ctemp // 将删除掉的控件列表,重新赋值给动态控件列表
refreshlayout(w) // 刷新布局后,就不存在当前控件行
})
sid++ // 当前行的id值
myw := new(MyWidget) // 存储新的一行控件
myw.id = sid // 新的一行控件的唯一id
myw.myWidgets = []fyne.CanvasObject{ll1, ee, ll2, bb1, bb2} // 新的一行控件
myWidgetList = append(myWidgetList, myw) // 将一行动态控件加到数组,ctemp存储所有动态添加的控件
// 添加一行后,必须刷新布局,就多了一行控件
refreshlayout(w)
}
}
// 每次变动都要,刷新窗口布局
func refreshlayout(w fyne.Window) {
n := len(myWidgetList) // 动态添加控件的行数
l12.SetText(strconv.Itoa(n)) // 设置显示动态控件行数
cc = &fyne.Container{} // 重新布置所有布局的总容器
cc.Objects = append(cc.Objects, container.NewHBox(l11, l12)) // 添加第一行布局到总容器
for i, c := range myWidgetList { // 循环添加动态添加的每行控件
v, _ := c.myWidgets[0].(*widget.Label) // 获取每行的序号标签
v.SetText(strconv.Itoa(i + 1)) // 按照顺序设置序号标签,添加、删除后,序号都重新排序
cc.Objects = append(cc.Objects, container.NewHBox((c.myWidgets)...)) // 将本行控件放在横向容器中,并添加到总容器
}
cc.Objects = append(cc.Objects, b31, l41) // 添加动态控件后的控件
w.SetContent(container.NewVBox(cc.Objects...)) // 将容器中每行容器或控件展开,用于垂直布局到行
}