Flutter實(shí)戰(zhàn) 對(duì)齊與相對(duì)定位(Align)

2021-03-08 10:07 更新

在上一節(jié)中我們講過通過StackPositioned,我們可以指定一個(gè)或多個(gè)子元素相對(duì)于父元素各個(gè)邊的精確偏移,并且可以重疊。但如果我們只想簡(jiǎn)單的調(diào)整一個(gè)子元素在父元素中的位置的話,使用Align組件會(huì)更簡(jiǎn)單一些。

#4.6.1 Align

Align 組件可以調(diào)整子組件的位置,并且可以根據(jù)子組件的寬高來(lái)確定自身的的寬高,定義如下:

Align({
  Key key,
  this.alignment = Alignment.center,
  this.widthFactor,
  this.heightFactor,
  Widget child,
})

  • alignment : 需要一個(gè)AlignmentGeometry類型的值,表示子組件在父組件中的起始位置。AlignmentGeometry 是一個(gè)抽象類,它有兩個(gè)常用的子類:AlignmentFractionalOffset,我們將在下面的示例中詳細(xì)介紹。
  • widthFactorheightFactor是用于確定Align 組件本身寬高的屬性;它們是兩個(gè)縮放因子,會(huì)分別乘以子元素的寬、高,最終的結(jié)果就是Align 組件的寬高。如果值為null,則組件的寬高將會(huì)占用盡可能多的空間。

#示例

我們先來(lái)看一個(gè)簡(jiǎn)單的例子:

Container(
  height: 120.0,
  width: 120.0,
  color: Colors.blue[50],
  child: Align(
    alignment: Alignment.topRight,
    child: FlutterLogo(
      size: 60,
    ),
  ),
)

運(yùn)行效果如圖4-11所示:

FlutterLogo 是 Flutter SDK 提供的一個(gè)組件,內(nèi)容就是 Flutter 的商標(biāo)。在上面的例子中,我們顯式指定了Container的寬、高都為 120。如果我們不顯式指定寬高,而通過同時(shí)指定widthFactorheightFactor 為2也是可以達(dá)到同樣的效果:

Align(
  widthFactor: 2,
  heightFactor: 2,
  alignment: Alignment.topRight,
  child: FlutterLogo(
    size: 60,
  ),
),

因?yàn)?code>FlutterLogo的寬高為60,則Align的最終寬高都為2*60=120。

另外,我們通過Alignment.topRightFlutterLogo定位在Container的右上角。那Alignment.topRight是什么呢?通過源碼我們可以看到其定義如下:

//右上角
static const Alignment topRight = Alignment(1.0, -1.0);

可以看到它只是Alignment的一個(gè)實(shí)例,下面我們介紹一下Alignment

#Alignment

Alignment繼承自AlignmentGeometry,表示矩形內(nèi)的一個(gè)點(diǎn),他有兩個(gè)屬性x、y,分別表示在水平和垂直方向的偏移,Alignment定義如下:

Alignment(this.x, this.y)

Alignment Widget 會(huì)以矩形的中心點(diǎn)作為坐標(biāo)原點(diǎn),即Alignment(0.0, 0.0) 。x、y的值從-1到1分別代表矩形左邊到右邊的距離和頂部到底邊的距離,因此2個(gè)水平(或垂直)單位則等于矩形的寬(或高),如Alignment(-1.0, -1.0) 代表矩形的左側(cè)頂點(diǎn),而Alignment(1.0, 1.0)代表右側(cè)底部終點(diǎn),而Alignment(1.0, -1.0) 則正是右側(cè)頂點(diǎn),即Alignment.topRight。為了使用方便,矩形的原點(diǎn)、四個(gè)頂點(diǎn),以及四條邊的終點(diǎn)在Alignment類中都已經(jīng)定義為了靜態(tài)常量。

Alignment可以通過其坐標(biāo)轉(zhuǎn)換公式將其坐標(biāo)轉(zhuǎn)為子元素的具體偏移坐標(biāo):

(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)

其中childWidth為子元素的寬度,childHeight為子元素高度。

現(xiàn)在我們?cè)倏纯瓷厦娴氖纠覀儗?code>Alignment(1.0, -1.0)帶入上面公式,可得FlutterLogo的實(shí)際偏移坐標(biāo)正是(60,0)。下面再看一個(gè)例子:

 Align(
  widthFactor: 2,
  heightFactor: 2,
  alignment: Alignment(2,0.0),
  child: FlutterLogo(
    size: 60,
  ),
)

我們可以先想象一下運(yùn)行效果:將Alignment(2,0.0)帶入上述坐標(biāo)轉(zhuǎn)換公式,可以得到FlutterLogo的實(shí)際偏移坐標(biāo)為(90,30)。實(shí)際運(yùn)行如圖4-12所示:

#FractionalOffset

FractionalOffset 繼承自 Alignment,它和 Alignment唯一的區(qū)別就是坐標(biāo)原點(diǎn)不同!FractionalOffset 的坐標(biāo)原點(diǎn)為矩形的左側(cè)頂點(diǎn),這和布局系統(tǒng)的一致,所以理解起來(lái)會(huì)比較容易。FractionalOffset的坐標(biāo)轉(zhuǎn)換公式為:

實(shí)際偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)

下面看一個(gè)例子:

Container(
  height: 120.0,
  width: 120.0,
  color: Colors.blue[50],
  child: Align(
    alignment: FractionalOffset(0.2, 0.6),
    child: FlutterLogo(
      size: 60,
    ),
  ),
)

實(shí)際運(yùn)行效果如圖4-13所示下:

我們將FractionalOffset(0.2, 0.6)帶入坐標(biāo)轉(zhuǎn)換公式得FlutterLogo實(shí)際偏移為(12,36),和實(shí)際運(yùn)行效果吻合。

#4.6.2 Align和Stack對(duì)比

可以看到,AlignStack/Positioned都可以用于指定子元素相對(duì)于父元素的偏移,但它們還是有兩個(gè)主要區(qū)別:

  1. 定位參考系統(tǒng)不同;Stack/Positioned定位的的參考系可以是父容器矩形的四個(gè)頂點(diǎn);而Align則需要先通過alignment 參數(shù)來(lái)確定坐標(biāo)原點(diǎn),不同的alignment會(huì)對(duì)應(yīng)不同原點(diǎn),最終的偏移是需要通過alignment的轉(zhuǎn)換公式來(lái)計(jì)算出。
  2. Stack可以有多個(gè)子元素,并且子元素可以堆疊,而Align只能有一個(gè)子元素,不存在堆疊。

#4.6.3 Center組件

我們?cè)谇懊嬲鹿?jié)的例子中已經(jīng)使用過Center組件來(lái)居中子元素了,現(xiàn)在我們正式來(lái)介紹一下它。通過查找SDK源碼,我們看到Center組件定義如下:

class Center extends Align {
  const Center({ Key key, double widthFactor, double heightFactor, Widget child })
    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}

可以看到Center繼承自Align,它比Align只少了一個(gè)alignment 參數(shù);由于Align的構(gòu)造函數(shù)中alignment 值為Alignment.center,所以,我們可以認(rèn)為Center組件其實(shí)是對(duì)齊方式確定(Alignment.center)了的Align。

上面我們講過當(dāng)widthFactorheightFactornull時(shí)組件的寬高將會(huì)占用盡可能多的空間,這一點(diǎn)需要特別注意,我們通過一個(gè)示例說(shuō)明:

...//省略無(wú)關(guān)代碼
DecoratedBox(
  decoration: BoxDecoration(color: Colors.red),
  child: Center(
    child: Text("xxx"),
  ),
),
DecoratedBox(
  decoration: BoxDecoration(color: Colors.red),
  child: Center(
    widthFactor: 1,
    heightFactor: 1,
    child: Text("xxx"),
  ),
)

運(yùn)行效果如圖4-14所示:

#總結(jié)

本節(jié)重點(diǎn)介紹了Align組件及兩種偏移類AlignmentFractionalOffset,讀者需要理解這兩種偏移類的區(qū)別及各自的坐標(biāo)轉(zhuǎn)化公式。另外,在此建議讀者在需要制定一些精確的偏移時(shí)應(yīng)優(yōu)先使用FractionalOffset,因?yàn)樗淖鴺?biāo)原點(diǎn)和布局系統(tǒng)相同,能更容易算出實(shí)際偏移。

在后面,我們又介紹了Align組件和Stack/Positioned、Center的關(guān)系,讀者可以對(duì)比理解。

還有,熟悉 Web 開發(fā)的同學(xué)可能會(huì)發(fā)現(xiàn)Align組件的特性和 Web 開發(fā)中相對(duì)定位(position: relative)非常像,是的!在大多數(shù)時(shí)候,我們可以直接使用Align組件來(lái)實(shí)現(xiàn) Web 中相對(duì)定位的效果,讀者可以類比記憶。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)