一. 開(kāi)始
在Android中View是所有控件的基礎(chǔ), 在Flutter中與View對(duì)等的是Widget. 但Widget又不同于Android中的views. 在Flutter中你可以聲明構(gòu)造界面.
Flutter中的控件不能修改, 一直到它們需要改變. 當(dāng)狀態(tài)發(fā)生變更, Flutter的底層會(huì)重新創(chuàng)建一顆新的控件樹(shù)實(shí)例. 而在Android中控件繪制一次就不再繪制, 直到invalidate
被調(diào)用.
由于不可變性, Flutter的控件很輕量. 因?yàn)樗鼈儾皇且晥D本身站楚,也不是直接繪制任何東西,而是對(duì)UI及其語(yǔ)義的描述. 最終才會(huì)被構(gòu)造成實(shí)際的試圖對(duì)象.
二. Android控件
常用基礎(chǔ)控件View, TextView, Button, ImageView, 其它控件如ProgressBar, SeekBar等.
這些控件都繼承自View. 基礎(chǔ)控件不夠用時(shí), 可繼承這些控件實(shí)現(xiàn)自己想要的特性.
在android中存在5種基本布局
- 線性布局(LinearLayout)
- 相對(duì)布局(RelativeLayout)
- 表格布局(TableLayout)
- 幀布局(FrameLayout)
- 絕對(duì)布局(AbsoluteLayout)
TableLayout和AbsoluteLayout我基本沒(méi)有用到. LinearLayout用于布局水平一行或者縱向一行.
FrameLayout用于布局有層次的界面, 可以簡(jiǎn)單設(shè)置居中. 更復(fù)雜控件間的相對(duì)關(guān)系使用RelativeLayout布局.
FrameLayout性能會(huì)好于RelativeLayout, 當(dāng)FrameLayout無(wú)法解決的時(shí)候才用RelativeLayout
RelativeLayout通過(guò)相對(duì)關(guān)系也能布局出LinearLayout的界面, 但不如LinearLayout直觀
列表可滑動(dòng)滑動(dòng)布局ScrollView/ListView/GridView/RecyclerView/ViewPager.
上述控件繼承自ViewGroup, ViewGroup又繼承于View 相對(duì)于繼承View的控件, 前者內(nèi)部可包含子控件, 而后者不行.前者一般會(huì)實(shí)現(xiàn)onLayout和onMeasure, 來(lái)控件子控件的布局和大小. 這些布局不夠用時(shí), 也可繼承這些控件實(shí)現(xiàn)自己想要的特性.
上述所有控件都有的屬性有
- 寬度(layout_width)
- 高度(layout_height)
- 背景(backgroud)
- 內(nèi)間距(padding)
- 外間距(margin)
- 可見(jiàn)性(visibility)
- 位于父控件的位置(layout_gravity)
- 內(nèi)部子控件的位置(gravity, 繼承自ViewGroup的才有)
- 點(diǎn)擊事件(onClick)
- 動(dòng)畫(huà)屬性(alpha/rotation/scale)
三. Flutter控件
flutter中的控件非常多, 每一種布局只具備特定的特性. 功能劃分的非常細(xì). 最大的不同也正是這點(diǎn), 基本控件不再擁有通用屬性,比如背景, 點(diǎn)擊事件等, 這些都被獨(dú)立出來(lái)成為單獨(dú)的控件.
舉個(gè)簡(jiǎn)單例子如下
Android例子
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:onClick="xxx"
android:gravity="center"
android:text="按鈕"
android:textSize="18sp"
android:textColor="0xffffffff"
android:layout_width="100dp"
android:layout_height="40dp"
android:background="0xff808080"/>
</LinearLayout>
Flutter例子
var layout = Column(
children: <Widget>[
GestureDetector(
child: Container(
width: 100.0,
height: 40.0,
color: Color(0xff808080),
child: Center (
child: Text(
'按鈕',
style: TextStyle(
color: Color(0xffffffff),
fontSize: 18.0,
),
),
)
),
onTap: () {
print('clicked');
},
),
],
);
可以看出在Flutter中, 屬性都被提取成一個(gè)控件了, GestureDetector負(fù)責(zé)處理點(diǎn)擊事件, Container負(fù)責(zé)大小和背景, Center負(fù)責(zé)居中.Text僅僅只有文字相關(guān)的屬性, 這樣Android一個(gè)簡(jiǎn)單的控件在Flutter中需要嵌套很多層才能實(shí)現(xiàn). 但好處就是劃分的很細(xì), 可以任意組合.
基本的顯示控件有Text, Image, Icon, RaisedButton等.屬性功能控件有Container, Padding, Center, Align, FittedBox等.這些都是Single-child控件. 就是child屬性指向?yàn)閃idget.
布局控件有Row, Column, Stack, IndexedStack, GridView, ListView等.這些都是Multi-child控件. 就是child屬性指向?yàn)?lt;Widget>[].
四. 實(shí)現(xiàn)自定義控件
在Android中通常是繼承View或其它基于View的控件. 然后重寫(xiě)其中的方法, 來(lái)獲取想要的行為.
在Flutter中, 你只需要組合各種小組件而不是繼承. 某種程度上類(lèi)似于實(shí)現(xiàn)Android中的一個(gè)自定義ViewGroup. 各種組件單元都已經(jīng)存在, 你只是提供一種不同的行為, 比如, 重新定義布局邏輯.
舉個(gè)例子: 你想實(shí)現(xiàn)一個(gè)在構(gòu)造時(shí)獲取文字的CustomButton
. 你可以通過(guò)RaisedButton
組合文字, 而不是繼承RaisedButton
.
class CustomButton extends StatelessWidget {
final String label;
CustomButton(this.label);
@override
Widget build(BuildContext context) {
return RaisedButton(onPressed: () {}, child: Text(label));
}
}
然后使用CustomButton
就像跟其它Flutter組件一樣:
@override
Widget build(BuildContext context) {
return Center(
child: CustomButton("Hello"),
);
}
五. Android/Flutter映射表
Android | Flutter |
---|---|
TextView | Text |
ImageView | Image |
Button | RaisedButton |
LinearLayout | Row/Column |
FrameLayout/RelativeLayout | Stack |
ListView | ListView |
GridView | GridView |
ViewPager | PageView |