You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gio(Gioui)开发问询:如何拆分窗口并精确定位右上角按钮

Gio(Gioui) 窗口布局优化:拆分区域与右上角按钮定位

问题描述

我熟练掌握Go语言,刚上手Gio(Gioui),目前是初学者。现有代码能在窗口中绘制文本和按钮,当前运行效果如下:
当前运行效果

需要解决两个问题:

  • 将窗口拆分为上下两个区域
  • 将按钮精确定位到窗口右上角,达到如下目标效果:
    目标效果

原始代码

import (
    "fmt"
    "log"
    "os"

    "gioui.org/app"
    "gioui.org/layout"
    "gioui.org/op"
    "gioui.org/text"
    "gioui.org/unit"
    "gioui.org/widget"
    "gioui.org/widget/material"
)

func main() {
    go func() {
        w := new(app.Window)
        w.Option(app.Title("Hello World"))
        w.Option(app.MaxSize(unit.Dp(400), unit.Dp(600)))
        if err := draw(w); err != nil {
            log.Fatal(err)
        }
        os.Exit(0)
    }()
    app.Main()
}

func draw(w *app.Window) error {
    var ops op.Ops
    var lblButton widget.Clickable
    var btnClickArea widget.Clickable

    th := material.NewTheme()
    btn := layout.Rigid(
        func(gtx layout.Context) layout.Dimensions {
            btn := material.Button(th, &btnClickArea, "Button1")
            return btn.Layout(gtx)
        },
    )
    columns := layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceAround}
    for {
        switch e := w.Event().(type) {
        case app.FrameEvent:
            gtx := app.NewContext(&ops, e)

            if lblButton.Clicked(gtx) {
                fmt.Println("the label was clicked")
            }
            if btnClickArea.Clicked(gtx) {
                fmt.Println("button1 was clicked")
            }

            layout.Flex{
                Axis:    layout.Vertical,
                Spacing: layout.SpaceStart,
            }.Layout(gtx,
                layout.Rigid(
                    layout.Spacer{Height: unit.Dp(250)}.Layout,
                ),
                layout.Rigid(
                    func(gtx layout.Context) layout.Dimensions {
                        lbl := material.Label(th, unit.Sp(50), "hello world")
                        lbl.Alignment = text.Middle
                        lblButton.Layout(gtx, lbl.Layout)
                        return lbl.Layout(gtx)
                    },
                ),
                layout.Rigid(
                    func(gtx layout.Context) layout.Dimensions {
                        return columns.Layout(gtx, btn)
                    },
                ),
            )
            e.Frame(gtx.Ops)
        case app.DestroyEvent:
            return e.Err
        }
    }
    return nil
}

修改后的代码

import (
    "fmt"
    "log"
    "os"

    "gioui.org/app"
    "gioui.org/layout"
    "gioui.org/op"
    "gioui.org/text"
    "gioui.org/unit"
    "gioui.org/widget"
    "gioui.org/widget/material"
)

func main() {
    go func() {
        w := new(app.Window)
        w.Option(app.Title("Hello World"))
        w.Option(app.MaxSize(unit.Dp(400), unit.Dp(600)))
        if err := draw(w); err != nil {
            log.Fatal(err)
        }
        os.Exit(0)
    }()
    app.Main()
}

func draw(w *app.Window) error {
    var ops op.Ops
    var lblButton widget.Clickable
    var btnClickArea widget.Clickable

    th := material.NewTheme()
    
    for {
        switch e := w.Event().(type) {
        case app.FrameEvent:
            gtx := app.NewContext(&ops, e)

            if lblButton.Clicked(gtx) {
                fmt.Println("the label was clicked")
            }
            if btnClickArea.Clicked(gtx) {
                fmt.Println("button1 was clicked")
            }

            // 外层垂直Flex,将窗口拆分为上下两个区域
            layout.Flex{
                Axis: layout.Vertical,
            }.Layout(gtx,
                // 顶部区域:包含右上角按钮
                layout.Rigid(
                    func(gtx layout.Context) layout.Dimensions {
                        // 水平Flex,通过Spacer将按钮推到右侧
                        return layout.Flex{
                            Axis: layout.Horizontal,
                        }.Layout(gtx,
                            layout.Flexed(1, layout.Spacer{}.Layout), // 占满剩余水平空间
                            layout.Rigid(
                                func(gtx layout.Context) layout.Dimensions {
                                    btn := material.Button(th, &btnClickArea, "Button1")
                                    // 给按钮添加右侧和顶部边距,避免贴边
                                    return layout.Inset{
                                        Top: unit.Dp(10),
                                        Right: unit.Dp(10),
                                    }.Layout(gtx, btn.Layout)
                                },
                            ),
                        )
                    },
                ),
                // 底部区域:占满剩余空间,居中显示文本
                layout.Flexed(1,
                    func(gtx layout.Context) layout.Dimensions {
                        return layout.Center.Layout(gtx,
                            func(gtx layout.Context) layout.Dimensions {
                                lbl := material.Label(th, unit.Sp(50), "hello world")
                                lbl.Alignment = text.Middle
                                lblButton.Layout(gtx, lbl.Layout)
                                return lbl.Layout(gtx)
                            },
                        )
                    },
                ),
            )
            e.Frame(gtx.Ops)
        case app.DestroyEvent:
            return e.Err
        }
    }
    return nil
}

关键改动说明

  1. 窗口拆分:

    • 使用外层垂直layout.Flex,将窗口分为两个子项:顶部的按钮区域(layout.Rigid)和底部的文本区域(layout.Flexed(1, ...))。
    • layout.Flexed(1, ...)让文本区域占满窗口剩余的垂直空间,实现上下区域的清晰拆分。
  2. 右上角按钮定位:

    • 在顶部区域内部使用水平layout.Flex,左侧添加layout.Flexed(1, layout.Spacer{}.Layout),这个Spacer会占满所有剩余的水平空间,从而将右侧的按钮推到窗口右上角。
    • 通过layout.Inset给按钮添加顶部和右侧边距,避免按钮紧贴窗口边缘,提升视觉效果。
  3. 文本居中优化:

    • 文本区域使用layout.Center.Layout,让文本在剩余空间中水平和垂直居中,替代原来的固定高度Spacer,布局更灵活。

内容的提问来源于stack exchange,提问作者nurub

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:59:54