<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go Gio 實(shí)戰(zhàn):煮蛋計(jì)時(shí)器的實(shí)現(xiàn) 04— 布局

          共 6155字,需瀏覽 13分鐘

           ·

          2021-11-09 18:14

          上篇文章介紹了按鈕,但整個(gè)按鈕是占據(jù)屏幕的,這顯然不合適。本文就解決該問題。

          01 目標(biāo)

          要解決按鈕的顯示問題,我們引入布局的概念。本文使用 Flexbox[1] 布局。

          A low button with a spacer below

          關(guān)于 Flex 布局的基本概念請(qǐng)參考 mozilla[2]

          02 布局的整體代碼結(jié)構(gòu)

          先忽略細(xì)節(jié),看看布局整體結(jié)構(gòu)的代碼:

          case?system.FrameEvent:

          ????layout.Flex{
          ????//?...
          ????}.Layout(?//?...
          ????????//?插入兩個(gè)?rigid?元素:
          ????????//?第一個(gè)放按鈕
          ????????layout.Rigid(),
          ????????//?這一個(gè)放一個(gè)空的?spacer
          ????????layout.Rigid(),
          ????}

          解釋說明

          解釋下這段代碼的結(jié)構(gòu)。

          1. 首先我們通過結(jié)構(gòu)體 layout.Flex{ } 定義一個(gè) ?Flexbox
          2. 然后我們向它增加一個(gè)要放置的子項(xiàng)列表 Layout(gtx, ...)。圖形上下文 gtx 包含子項(xiàng)必須遵守的約束,并且任何數(shù)量的子項(xiàng)都要遵循。

          我們列出的子項(xiàng)都是由 layout.Rigid( ) 創(chuàng)建的:第一個(gè)是按鈕的占位符,另一個(gè)占位符,用于包含按鈕下方的空白區(qū)域。

          什么是 Rigid[3]?很簡(jiǎn)單 - 它的工作是填充給定的空間。Rigid 的子項(xiàng)首先占據(jù)它的部分,而 Flexed[4] 子項(xiàng)占據(jù)剩下的。除此之外,子項(xiàng)按照定義的順序排列。

          約束和尺寸(Constraints 和 Dimensions)

          在這一點(diǎn)上,我們可以退后一步,看看將所有這些結(jié)合在一起的概念,即 ConstraintsDimensions

          • Constraints[5] 表示 widget 的最大和最小大小,即 widget 能多大或多小。
          • Dimensions[6] 表示 widget 的實(shí)際大小,即 widget 的實(shí)際多大或多小。

          父級(jí)設(shè)置 Constraints,子級(jí)響應(yīng) Dimensions。父級(jí)創(chuàng)建一個(gè)小部件并調(diào)用Layout(),小部件用它自己的尺寸響應(yīng),有效地布置自己。好比真實(shí)世界中,并非所有孩子都表現(xiàn)得很好,而且孩子們會(huì)認(rèn)為媽媽或爸爸的一些限制是不公平的 —— 因此需要一些細(xì)微差別和協(xié)商。但在大多數(shù)情況下,就是這樣。約束尺寸將它們綁定在一起。

          正如我們?cè)谏厦婵吹降模季植僮魇沁f歸的。一個(gè)子項(xiàng)本身還可以有子項(xiàng)。布局本身可以包含布局。如此下去,你可以從簡(jiǎn)單的組件構(gòu)建復(fù)雜的結(jié)構(gòu)。

          03 詳細(xì)代碼

          上面從高層次介紹了整個(gè)代碼框架,現(xiàn)在深入細(xì)節(jié),看看 ?system.FrameEvent 部分的代碼:

          case?system.FrameEvent:
          ????gtx?:=?layout.NewContext(&ops,?e)
          ??//?flexbox?布局概念
          ????layout.Flex{
          ???????//?從上到下,垂直對(duì)齊
          ????????Axis:?layout.Vertical,
          ???????//?開始時(shí)(即頂部)留有空白
          ????????Spacing:?layout.SpaceStart,
          ????}.Layout(gtx,
          ????????//?我們插入兩個(gè) rigid 元素:
          ????????//?首先是?Button
          ????????layout.Rigid(
          ????????????func(gtx?layout.Context)?layout.Dimensions?{
          ????????????????btn?:=?material.Button(th,?&startButton,?"Start")
          ????????????????return?btn.Layout(gtx)
          ????????????},
          ????????),
          ????????//?然后是一個(gè)空?spacer
          ????????layout.Rigid(
          ????????????//?spacer?的高度為?25?個(gè)設(shè)備獨(dú)立像素
          ????????????layout.Spacer{Height:?unit.Dp(25)}.Layout,
          ????????),
          ????)
          ????e.Frame(gtx.Ops)

          代碼注解

          layout.Flex{} 里面,我們定義了兩個(gè)屬性:

          1. Axis(軸):垂直對(duì)齊意味各項(xiàng)豎著排列。
          2. Spacing(間距):多出來的空間在頂部(上方),注意,這個(gè)不是 spacer。

          進(jìn)一步看 layout.Flex 結(jié)構(gòu)體的定義,可以根據(jù) Mozilla 上的文檔對(duì)應(yīng)著學(xué)習(xí)。

          //?Flex?lays?out?child?elements?along?an?axis,
          //?according?to?alignment?and?weights.
          type?Flex?struct?{
          ?//?Axis?is?the?main?axis,?either?Horizontal?or?Vertical.
          ?Axis?Axis
          ?//?Spacing?controls?the?distribution?of?space?left?after
          ?//?layout.
          ?Spacing?Spacing
          ?//?Alignment?is?the?alignment?in?the?cross?axis.
          ?Alignment?Alignment
          ?//?WeightSum?is?the?sum?of?weights?used?for?the?weighted
          ?//?size?of?Flexed?children.?If?WeightSum?is?zero,?the?sum
          ?//?of?all?Flexed?weights?is?used.
          ?WeightSum?float32
          }

          然后是調(diào)用 Flex 的 Layout 方法。該方法的簽名如下:

          //?Layout?a?list?of?children.?The?position?of?the?children?are
          //?determined?by?the?specified?order,?but?Rigid?children?are?laid?out
          //?before?Flexed?children.
          func?(f?Flex)?Layout(gtx?Context,?children?...FlexChild)?Dimensions

          接收 0 到多個(gè) FlexChild。如果獲得 FlexChild 實(shí)例呢?這就是 layout.Rigid 函數(shù):

          //?Rigid?returns?a?Flex?child?with?a?maximal?constraint?of?the?remaining?space.
          func?Rigid(widget?Widget)?FlexChild

          本例子中,我們傳遞了兩個(gè) FlexChild。

          那 Dimensions 又是怎么定義的呢?它是一個(gè)結(jié)構(gòu)體:

          //?Dimensions?are?the?resolved?size?and?baseline?for?a?widget.
          //
          //?Baseline?is?the?distance?from?the?bottom?of?a?widget?to?the?baseline?of
          //?any?text?it?contains?(or?0).?The?purpose?is?to?be?able?to?align?text
          //?that?span multiple?widgets.
          type?Dimensions?struct?{
          ?Size?????image.Point
          ?Baseline?int
          }

          上文已經(jīng)介紹了 Dimensions 的作用,即它負(fù)責(zé)解析小部件的大小和基線。基線是小部件底部到其包含的任何文本基線的距離(或 0)。其目的是能夠?qū)R跨多個(gè)小部件的文本。

          現(xiàn)在就看看對(duì) layout.Rigid( ) 的兩個(gè)調(diào)用:

          • Rigid 接受一個(gè)Widget[7],即小部件
          • 小部件只是返回它自己的 Dimensions 信息
          • 如何得到小部件并不重要。這里使用了兩種截然不同的方式:在第一個(gè) Rigid 中,我們傳入一個(gè) func(),它返回 btn.Layout(),即 layout.Dimensions。在第二個(gè) Rigid 中,我們創(chuàng)建了一個(gè) Spacer{} 結(jié)構(gòu)體,調(diào)用它的 Layout 方法,進(jìn)而得到 layout.Dimensions
          • 從父組件的角度來看,這并不重要。只要子項(xiàng)返回 layout.Dimensions 即可。

          Button above spacer

          這是布局小部件。但是小部件(widget)到底是什么?

          • 顧名思義,material.Button 就是一個(gè)基于材料設(shè)計(jì)的 Button[8],我們?cè)谏弦徽略敿?xì)介紹過。
          • Spacer[9] 添加空白空間,這里由 Height 定義的。由于我們已將整體布局定義為垂直布局,多余的空間應(yīng)位于頂部,因此它會(huì)落到底部并且按鈕位于其頂部。這讓按鈕底部有空白。

          從源碼角度,Widget 的定義如下:

          //?Widget?is?a?function?scope?for?drawing,?processing?events?and
          //?computing?dimensions?for?a?user?interface?element.
          type?Widget?func(gtx?Context)?Dimensions

          即 Widget 是用于繪圖(drawing)、處理事件和計(jì)算用戶界面元素尺寸的函數(shù)。

          因此,我們可以推斷,layout.Spacer 的 Layout 方法簽名符合 Widget 類型:

          func?(s?Spacer)?Layout(gtx?Context)?Dimensions

          實(shí)際上,各個(gè)組件的 Layout 方法都是一個(gè) Widget。

          04 小結(jié)

          要掌握本章的內(nèi)容,必須先熟悉 Flex。Web 前端開發(fā)對(duì)此會(huì)很熟悉。

          為了方便,附上完整代碼:

          package?main

          import?(
          ?"gioui.org/app"
          ?"gioui.org/font/gofont"
          ?"gioui.org/io/system"
          ?"gioui.org/layout"
          ?"gioui.org/op"
          ?"gioui.org/unit"
          ?"gioui.org/widget"
          ?"gioui.org/widget/material"
          )

          func?main()?{
          ?go?func()?{
          ??//?創(chuàng)建一個(gè)新窗口
          ??w?:=?app.NewWindow(
          ???app.Title("煮蛋計(jì)時(shí)器"),
          ???app.Size(unit.Dp(400),?unit.Dp(600)),
          ??)

          ??//?ops?表示?UI?上的操作
          ??var?ops?op.Ops

          ??//?startButton?時(shí)候一個(gè)可點(diǎn)擊的小部件
          ??var?startButton?widget.Clickable

          ??//?th?定義?material?design(材料設(shè)計(jì))的風(fēng)格
          ??th?:=?material.NewTheme(gofont.Collection())

          ??//?循環(huán)監(jiān)聽窗口上的事件
          ??for?e?:=?range?w.Events()?{

          ???//?監(jiān)聽事件的類型
          ???switch?e?:=?e.(type)?{

          ???//?當(dāng)應(yīng)用程序需要重新渲染是發(fā)送該事件
          ???case?system.FrameEvent:
          ????gtx?:=?layout.NewContext(&ops,?e)
          ????//?flexbox?布局概念
          ????layout.Flex{
          ?????//?從上到下,垂直對(duì)齊
          ?????Axis:?layout.Vertical,
          ?????//?開始時(shí)(即頂部)留有空白
          ?????Spacing:?layout.SpaceStart,
          ????}.Layout(gtx,
          ?????//?我們插入兩個(gè) rigid 元素:
          ?????//?首先是?Button
          ?????layout.Rigid(
          ??????func(gtx?layout.Context)?layout.Dimensions?{
          ???????btn?:=?material.Button(th,?&startButton,?"Start")
          ???????return?btn.Layout(gtx)
          ??????},
          ?????),
          ?????//?然后是一個(gè)空?spacer
          ?????layout.Rigid(
          ??????//?spacer?的高度為?25?個(gè)設(shè)備獨(dú)立像素
          ??????layout.Spacer{Height:?unit.Dp(25)}.Layout,
          ?????),
          ????)
          ????e.Frame(gtx.Ops)
          ???}
          ??}
          ?}()
          ?app.Main()
          }

          參考資料

          [1]

          Flexbox: https://pkg.go.dev/gioui.org/layout#Flex

          [2]

          mozilla: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox

          [3]

          Rigid: https://pkg.go.dev/gioui.org/layout#Rigid

          [4]

          Flexed: https://pkg.go.dev/gioui.org/layout#Flexed

          [5]

          Constraints: https://pkg.go.dev/gioui.org/layout#Constraints

          [6]

          Dimensions: https://pkg.go.dev/gioui.org/layout#Dimensions

          [7]

          Widget: https://pkg.go.dev/gioui.org/layout#Widget

          [8]

          Button: https://pkg.go.dev/gioui.org/widget/material#Button

          [9]

          Spacer: https://pkg.go.dev/[email protected]/layout#Spacer



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。

          瀏覽 28
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文字幕永久永久在线 | 欧美日韩免费 | 一级a毛片免费观看久久精品 | 7799天天综合症 | 精品51日韩 |