Bootstrap

go语言gui窗口应用之fyne框架-动态添加、删除一行控件(逐行注释)

演示动画

在这里插入图片描述

功能

  • 动态添加一行控件
  • 最多添加5行
  • 可添加文件夹路径
  • 动态删除本行控件

全部代码

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...)) // 将容器中每行容器或控件展开,用于垂直布局到行
}
;