Designing task list

我们在这一节中将会设计一个,左边是列表,右边列表中代办事项的具体内容

实现效果如图:
请添加图片描述

实现该需要使用总共用了两个文件,一个task_list.go

package main

import (
   "fyne.io/fyne/v2"
   "fyne.io/fyne/v2/app"
   "fyne.io/fyne/v2/container"
   "fyne.io/fyne/v2/theme"
   "fyne.io/fyne/v2/widget"
   "time"
)

type taskApp struct {
   data *taskList
   // 记录当前代办事项
   visible []*task
   // 当前选中的task
   current *task

   tasks *widget.List
   // Entry widget allows simple text to be input when focused.
   title, description, due *widget.Entry
   category                *widget.Select
   priority                *widget.RadioGroup
   completion              *widget.Slider
}

func (a *taskApp) refreshData() {
   // hide done
   a.visible = a.data.remaining()
   a.tasks.Refresh()
}

func (a *taskApp) setTask(t *task) {
   // 最新设置的那个task就是当前的代办列表
   a.current = t

   // 将data的title设置到APP的title中
   a.title.SetText(t.title)
   a.description.SetText(t.description)
   a.category.SetSelected(t.category)

   if t.priority == midPriority {
      a.priority.SetSelected("Mid")
   } else if t.priority == highPriority {
      a.priority.SetSelected("High")
   } else {
      a.priority.SetSelected("Low")
   }

   a.due.SetText(formatData(t.due))

   a.completion.Value = t.completion
   a.completion.Refresh()
}

func formatData(date *time.Time) string {
   if date == nil {
      return ""
   }
   return date.Format(dateFormat)
}

func (a *taskApp) makeUI() fyne.CanvasObject {
   a.tasks = widget.NewList(
      func() int {
         return len(a.visible)
      },
      func() fyne.CanvasObject {
         return container.NewHBox(widget.NewCheck("", func(bool) {}),
            widget.NewLabel("TODO ITtem x"))
      },
      func(i widget.ListItemID, c fyne.CanvasObject) {
         task := a.visible[i]
         box := c.(*fyne.Container)
         check := box.Objects[0].(*widget.Check)
         check.Checked = task.done
         check.OnChanged = func(done bool) {
            task.done = done
            a.refreshData()
         }
         labelData := box.Objects[1].(*widget.Label)
         labelData.SetText(task.title)
      })
   // 当选中一个list的时候,调用该回调函数
   // 选中哪个list之后,将界面上需要显示的都更换成当前ID 的list对象
   a.tasks.OnSelected = func(id widget.ListItemID) {
      a.setTask(a.visible[id])
   }
   // 标题支持输入
   a.title = widget.NewEntry()
   a.title.OnChanged = func(text string) {
      if a.current == nil {
         return
      }
      // 更新当前选中的标题
      a.current.title = text
      a.tasks.Refresh()
   }

   a.description = widget.NewMultiLineEntry()
   a.description.OnChanged = func(text string) {
      if a.current == nil {
         return
      }

      a.current.description = text
   }

   a.category = widget.NewSelect([]string{"Home"}, func(cat string) {
      if a.current == nil {
         return
      }

      a.current.category = cat
   })

   a.priority = widget.NewRadioGroup([]string{"Low", "Mid", "High"}, func(pri string) {
      if a.current == nil {
         return
      }

      if pri == "Mid" {
         a.current.priority = midPriority
      } else if pri == "High" {
         a.current.priority = highPriority
      } else {
         a.current.priority = lowPriority
      }
   })

   a.due = widget.NewEntry()
   a.due.Validator = dateValidator
   a.due.OnChanged = func(str string) {
      if a.current == nil {
         return
      }

      if str == "" {
         a.current.due = nil
      } else {
         date, err := time.Parse(dateFormat, str)
         if err != nil {
            a.current.due = &date
         }
      }
   }

   a.completion = widget.NewSlider(0, 100)
   a.completion.OnChanged = func(done float64) {
      if a.current == nil {
         return
      }

      a.current.completion = done
   }

   details := widget.NewForm(
      widget.NewFormItem("Title", a.title),
      widget.NewFormItem("Description", a.description),
      widget.NewFormItem("Category", a.category),
      widget.NewFormItem("Priority", a.priority),
      widget.NewFormItem("Due", a.due),
      widget.NewFormItem("Completion", a.completion),
   )

   toolBar := widget.NewToolbar(
      widget.NewToolbarAction(theme.ContentAddIcon(), func() {
         task := &task{title: "New task"}
         a.data.add(task)
         a.setTask(task)
         a.refreshData()
      }),
   )

   return container.NewBorder(toolBar, nil, a.tasks, nil, details)
}

/**
 * 创建一个代办事项的APP
 */

func main() {
   a := app.New()
   w := a.NewWindow("Task List")

   data := dummyData()

   tasks := &taskApp{data: data, visible: data.remaining()}

   w.SetContent(tasks.makeUI())
   // 若是首次启动的时候,项目大于0就显示首个list
   if len(data.remaining()) > 0 {
      tasks.setTask(data.remaining()[0])
   }

   w.ShowAndRun()
}

还有一个是data.go

package main

import "time"

const (
   dateFormat = "02 Jan 06 15:04"

   lowPriority  = 0
   midPriority  = 1
   highPriority = 2
)

func dateValidator(text string) error {
   _,err := time.Parse(dateFormat, text)
   return err
}

type task struct {
   // 定义标题和描述语句
   title, description string
   // 该任务是否已经完成
   done                bool
   category         string
   priority         int
   due             *time.Time
   completion           float64
}

// 定义任务链表的切片
type taskList struct {
   tasks []*task
}

// 添加任务,新添加的任务放到列表头
func (l *taskList) add(t *task) {
   // 使用t初始化一个一样的切片,并将原有的切片添加到后面
   l.tasks = append([]*task{t}, l.tasks...)
}

/* 获取剩余没有完成的任务列表 */
func (l *taskList) remaining() []*task {
   var items []*task
   for _, task := range l.tasks {
      if !task.done {
         items = append(items, task)
      }
   }
   return items
}

/* 获取已经完成的任务列表 */
func (l *taskList) done() []*task {
   var items []*task
   for _, task := range l.tasks {
      if task.done {
         items = append(items, task)
      }
   }
   return items
}

func dummyData() *taskList {
   return &taskList{
      tasks: []*task{
         {title: "Nearly done", description: "you can tick my checkbox and I will be marked as done and disappear"},
         {title: "Functions", description: "Tap the plus icon above to add a new task, or tap the munus icon to remove this one"},
      },
   }
}

下面会讲解下具体思路

如图,我们首先需要创建一个toolbar,需要使用到函数NewToolbar,toolbar里面需要放置一个添加按钮

请添加图片描述

toolBar := widget.NewToolbar(
   widget.NewToolbarAction(theme.ContentAddIcon(), func() {
      task := &task{title: "New task"}
      a.data.add(task)
      a.setTask(task)
      a.refreshData()
   }),
)

然后添加面板中各个入口的对象

// 当选中一个list的时候,调用该回调函数
// 选中哪个list之后,将界面上需要显示的都更换成当前ID 的list对象
a.tasks.OnSelected = func(id widget.ListItemID) {
   a.setTask(a.visible[id])
}
// 标题支持输入
a.title = widget.NewEntry()
a.title.OnChanged = func(text string) {
   if a.current == nil {
      return
   }
   // 更新当前选中的标题
   a.current.title = text
   a.tasks.Refresh()
}

a.description = widget.NewMultiLineEntry()
a.description.OnChanged = func(text string) {
   if a.current == nil {
      return
   }

   a.current.description = text
}

a.category = widget.NewSelect([]string{"Home"}, func(cat string) {
   if a.current == nil {
      return
   }

   a.current.category = cat
})

a.priority = widget.NewRadioGroup([]string{"Low", "Mid", "High"}, func(pri string) {
   if a.current == nil {
      return
   }

   if pri == "Mid" {
      a.current.priority = midPriority
   } else if pri == "High" {
      a.current.priority = highPriority
   } else {
      a.current.priority = lowPriority
   }
})

a.due = widget.NewEntry()
a.due.Validator = dateValidator
a.due.OnChanged = func(str string) {
   if a.current == nil {
      return
   }

   if str == "" {
      a.current.due = nil
   } else {
      date, err := time.Parse(dateFormat, str)
      if err != nil {
         a.current.due = &date
      }
   }
}

a.completion = widget.NewSlider(0, 100)
a.completion.OnChanged = func(done float64) {
   if a.current == nil {
      return
   }

   a.current.completion = done
}

details := widget.NewForm(
   widget.NewFormItem("Title", a.title),
   widget.NewFormItem("Description", a.description),
   widget.NewFormItem("Category", a.category),
   widget.NewFormItem("Priority", a.priority),
   widget.NewFormItem("Due", a.due),
   widget.NewFormItem("Completion", a.completion),
)

toolBar := widget.NewToolbar(
   widget.NewToolbarAction(theme.ContentAddIcon(), func() {
      task := &task{title: "New task"}
      a.data.add(task)
      a.setTask(task)
      a.refreshData()
   }),
)