一. 開始
在Android中View是所有控件的基礎, 在Flutter中與View對等的是Widget. 但Widget又不同于Android中的views. 在Flutter中你可以聲明構造界面.
Flutter中的控件不能修改, 一直到它們需要改變. 當狀態發生變更, Flutter的底層會重新創建一顆新的控件樹實例. 而在Android中控件繪制一次就不再繪制, 直到invalidate
被調用.
由于不可變性, Flutter的控件很輕量. 因為它們不是視圖本身,也不是直接繪制任何東西,而是對UI及其語義的描述. 最終才會被構造成實際的試圖對象.
二. Android控件
常用基礎控件View, TextView, Button, ImageView, 其它控件如ProgressBar, SeekBar等.
這些控件都繼承自View. 基礎控件不夠用時, 可繼承這些控件實現自己想要的特性.
在android中存在5種基本布局
- 線性布局(LinearLayout)
- 相對布局(RelativeLayout)
- 表格布局(TableLayout)
- 幀布局(FrameLayout)
- 絕對布局(AbsoluteLayout)
TableLayout和AbsoluteLayout我基本沒有用到. LinearLayout用于布局水平一行或者縱向一行.
FrameLayout用于布局有層次的界面, 可以簡單設置居中. 更復雜控件間的相對關系使用RelativeLayout布局.
FrameLayout性能會好于RelativeLayout, 當FrameLayout無法解決的時候才用RelativeLayout
RelativeLayout通過相對關系也能布局出LinearLayout的界面, 但不如LinearLayout直觀
列表可滑動滑動布局ScrollView/ListView/GridView/RecyclerView/ViewPager.
上述控件繼承自ViewGroup, ViewGroup又繼承于View 相對于繼承View的控件, 前者內部可包含子控件, 而后者不行.前者一般會實現onLayout和onMeasure, 來控件子控件的布局和大小. 這些布局不夠用時, 也可繼承這些控件實現自己想要的特性.
上述所有控件都有的屬性有
- 寬度(layout_width)
- 高度(layout_height)
- 背景(backgroud)
- 內間距(padding)
- 外間距(margin)
- 可見性(visibility)
- 位于父控件的位置(layout_gravity)
- 內部子控件的位置(gravity, 繼承自ViewGroup的才有)
- 點擊事件(onClick)
- 動畫屬性(alpha/rotation/scale)
三. Flutter控件
flutter中的控件非常多, 每一種布局只具備特定的特性. 功能劃分的非常細. 最大的不同也正是這點, 基本控件不再擁有通用屬性,比如背景, 點擊事件等, 這些都被獨立出來成為單獨的控件.
舉個簡單例子如下
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中, 屬性都被提取成一個控件了, GestureDetector負責處理點擊事件, Container負責大小和背景, Center負責居中.Text僅僅只有文字相關的屬性, 這樣Android一個簡單的控件在Flutter中需要嵌套很多層才能實現. 但好處就是劃分的很細, 可以任意組合.
基本的顯示控件有Text, Image, Icon, RaisedButton等.屬性功能控件有Container, Padding, Center, Align, FittedBox等.這些都是Single-child控件. 就是child屬性指向為Widget.
布局控件有Row, Column, Stack, IndexedStack, GridView, ListView等.這些都是Multi-child控件. 就是child屬性指向為<Widget>[].
四. 實現自定義控件
在Android中通常是繼承View或其它基于View的控件. 然后重寫其中的方法, 來獲取想要的行為.
在Flutter中, 你只需要組合各種小組件而不是繼承. 某種程度上類似于實現Android中的一個自定義ViewGroup. 各種組件單元都已經存在, 你只是提供一種不同的行為, 比如, 重新定義布局邏輯.
舉個例子: 你想實現一個在構造時獲取文字的CustomButton
. 你可以通過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 |