使用go-gio库示例温度转换器的详解
包含的控件和功能
1、编辑器组件的显示和获取文本、设置文本和提示信息
2、边框的显示和设置
3、按钮事件
package main
import (
"image/color"
"log"
"os"
"strconv"
"gioui.org/app" // app contains Window handling.
"gioui.org/font/gofont" // gofont is used for loading the default font.
"gioui.org/io/key" // key is used for keyboard events.
"gioui.org/io/system" // system is used for system events (e.g. closing the window).
"gioui.org/layout" // layout is used for layouting widgets.
"gioui.org/op" // op is used for recording different operations.
"gioui.org/unit" // unit is used to define pixel-independent sizes
"gioui.org/widget" // widget contains state handling for widgets.
"gioui.org/widget/material" // material contains material design widgets.
)
func main() {
ui := NewUI()
go func() {
w := app.NewWindow(
app.Title("Temperature Converter"),
app.Size(unit.Dp(400), unit.Dp(60)),
)
if err := ui.Run(w); err != nil {
log.Println(err)
os.Exit(1)
}
os.Exit(0)
}()
app.Main()
}
var defaultMargin = unit.Dp(10)
// UI 界面类
type UI struct {
// 主题
Theme *material.Theme
// 转换器类
Converter Converter
}
// NewUI 实例化界面
func NewUI() *UI {
ui := &UI{}
ui.Theme = material.NewTheme(gofont.Collection())
ui.Converter.Init()
return ui
}
// Run 运行逻辑
func (ui *UI) Run(w *app.Window) error {
var ops op.Ops
for e := range w.Events() {
switch e := e.(type) {
case system.FrameEvent:
gtx := layout.NewContext(&ops, e)
ui.Layout(gtx)
e.Frame(gtx.Ops)
case key.Event:
switch e.Name {
case key.NameEscape:
return nil
}
case system.DestroyEvent:
return e.Err
}
}
return nil
}
// Layout 显示界面
func (ui *UI) Layout(gtx layout.Context) layout.Dimensions {
// 在窗口周边填充
inset := layout.UniformInset(defaultMargin)
return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return ui.Converter.Layout(ui.Theme, gtx)
})
}
// Converter 记录各自的状态
// 包含两个编辑器
type Converter struct {
clicked widget.Clickable
Celsius Field // 摄氏度组件
Fahrenheit Field // 华氏度组件
}
// Init 设置编辑器的显示、强制文本显示在一行上、单行显示不换行
func (conv *Converter) Init() {
conv.Celsius.SingleLine = true
conv.Fahrenheit.SingleLine = true
}
// Layout lays out the editors.
func (conv *Converter) Layout(th *material.Theme, gtx layout.Context) layout.Dimensions {
// 创建一个空的组件来占位
spacer := layout.Rigid(layout.Spacer{Width: defaultMargin}.Layout)
// 检查摄氏度值是否更改
if conv.Celsius.Changed() {
newValue, err := strconv.Atoi(conv.Celsius.Text())
// update whether the editor is displaying a valid value
conv.Celsius.Invalid = err != nil
if !conv.Celsius.Invalid {
// 设置华氏度数值合法
conv.Fahrenheit.Invalid = false
// 设置华氏度编辑器显示的值
conv.Fahrenheit.SetText(strconv.Itoa(newValue*9/5 + 32))
}
}
// 检查华氏度数值是否变化
if conv.Fahrenheit.Changed() {
newValue, err := strconv.Atoi(conv.Fahrenheit.Text())
conv.Fahrenheit.Invalid = err != nil
if !conv.Fahrenheit.Invalid {
// 更改摄氏度状态和显示值
conv.Celsius.Invalid = false
conv.Celsius.SetText(strconv.Itoa((newValue - 32) * 5 / 9))
}
}
// 按钮点击、清空输入框内容
for range conv.clicked.Clicks() {
conv.Celsius.SetText("")
conv.Fahrenheit.SetText("")
}
return layout.Flex{}.Layout(gtx,
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return conv.Celsius.Layout(th, gtx)
}),
spacer,
// 刚体结构、不会变形
layout.Rigid(material.Body1(th, "Celsius").Layout),
spacer,
layout.Rigid(material.Body1(th, "=").Layout),
spacer,
// 可以变形
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
return conv.Fahrenheit.Layout(th, gtx)
}),
spacer,
layout.Rigid(material.Body1(th, "Fahrenheit").Layout),
spacer,
layout.Rigid(material.Button(th, &conv.clicked, "clear").Layout),
)
}
// Field 编辑器插件类
type Field struct {
widget.Editor //编辑器组件
Invalid bool // 数值是否非法
old string // 旧值
}
// Changed 检查当前值是否更改了
func (ed *Field) Changed() bool {
// 获取当前值
newText := ed.Editor.Text()
// 检查是否和旧值相等
changed := newText != ed.old
// 记录新值
ed.old = newText
return changed
}
// SetText 设置编辑器显示的内容
func (ed *Field) SetText(s string) {
ed.old = s // 将值记录为旧值
ed.Editor.SetText(s) // 将值设置到编辑器内
}
// Layout 编辑器显示逻辑
func (ed *Field) Layout(th *material.Theme, gtx layout.Context) layout.Dimensions {
// 根据编辑器的状态确定边框的颜色
borderWidth := float32(0.5) // 边框宽度
borderColor := color.NRGBA{A: 107} // 边框颜色
switch {
case ed.Editor.Focused(): // 当编辑器被选中
borderColor = th.Palette.ContrastBg
borderWidth = 2 // 加大边框值
case ed.Invalid:
borderColor = color.NRGBA{R: 200, A: 0xFF}
}
// 画一个带边框的编辑器
return widget.Border{
Color: borderColor, // 颜色
CornerRadius: unit.Dp(4), // 边角角度值
Width: unit.Dp(borderWidth), // 宽度
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.UniformInset(unit.Dp(4)).Layout(gtx,
// 编辑器、主题-编辑器组件、提示信息
material.Editor(th, &ed.Editor, "1").Layout)
})
}