官术网_书友最值得收藏!

3.1 擬物時鐘開發(fā)要點

在開發(fā)擬物時鐘時,需要用到一些Flutter中最為常用的布局組件,如Container容器組件、Stack層疊布局組件。同時也需要對Flutter自定義視圖繪制、動畫進行學(xué)習。因此,本節(jié)先介紹這部分內(nèi)容,完成基礎(chǔ)知識學(xué)習。之后給出擬物時鐘的產(chǎn)品原型,最后進行實戰(zhàn)。

3.1.1 使用Container定制組件展示效果

在UI開發(fā)中經(jīng)常需要對視圖添加邊距、內(nèi)距等效果,Flutter提供了一個非常方便的Con-tainer組件,能夠方便地控制組件邊距、大小、位置等屬性。不僅如此,通過Container還能實現(xiàn)更多高級展示效果,比如基于Container實現(xiàn)一個帶有新擬物風格的時鐘表盤,具體實現(xiàn)方法將在實戰(zhàn)環(huán)節(jié)中介紹。

創(chuàng)建一個新的Flutter工程作為Container學(xué)習工程,參照2.5節(jié)的實例代碼替換lib/main.dart。這樣在練習時,只需要將Text('Hello!')替換為接下來的Container組件示例即可。

1設(shè)置Container背景色

通過Container組件的color屬性可以修改背景顏色,例如:

代碼運行效果如圖3-1所示。

2 設(shè)置Container子組件

通過Container的child屬性傳入子組件:

其中,FlutterLogo是Flutter提供的一個組件,用于展示Flutter的Logo標識。代碼運行效果如圖3-2所示。

圖3-1 Container修改背景為綠色

圖3-2 Container指定child組件

3 設(shè)置Container對齊方式

Container的尺寸默認會充滿父布局,在上面代碼中,F(xiàn)lutterLogo的尺寸小于Container,默認會擺放在左上角位置。通過Container的alignment屬性可以更改對齊方式,例如,更改為右下角對齊:

代碼運行效果如圖3-3所示。

除了Alignment.bottomRight外,F(xiàn)lutter提供的默認對齊方式見表3-1。如果默認對齊方式無法滿足,也可以通過構(gòu)造Alignment實例的方式手動指定精確位置。

圖3-3 Container指定右下角對齊

表3-1 Flutter Alignment對齊方式

4 設(shè)置Container外邊距

通過Container的margin屬性可以指定外邊距,示例如下:

代碼運行效果如圖3-4所示。

其中,外邊距具體分為上下左右4條,EdgeInsets.all的作用是將這4條邊距設(shè)為同一個值。除此之外,也可以通過EdgeInsets.fromLTRB分別進行設(shè)置,如方法名稱中的縮寫LTRB,設(shè)置的順序分別為左、上、右、下。具體代碼如下:

代碼運行效果如圖3-5所示,可以看出左邊距與上邊距明顯不同。

5 設(shè)置Container內(nèi)邊距

通過Container的padding屬性進行內(nèi)邊距設(shè)置。內(nèi)邊距也是基于EdgeInsets進行設(shè)置的。示例代碼如下:

圖3-4 Container指定外邊距效果

圖3-5 Container通過fromLTRB指定外邊距效果

代碼運行效果如圖3-6所示。與圖3-5對比可以看出,Container內(nèi)部舉例FlutterLogo的內(nèi)邊距明顯加大了。

圖3-6 Container指定內(nèi)邊距效果

6 通過BoxDecoration添加邊界

Container支持裝飾器機制,能夠為容器添加更高級的樣式效果,例如,添加邊界、修改形狀,以及添加陰影等。裝飾器通過Container的decoration屬性傳入,F(xiàn)lutter提供BoxDecoration類,封裝了常用的樣式定制功能。

例如,通過下面代碼為Container添加一個圓角、帶有紅色邊界的邊框:

其中,通過BoxDecoration的border屬性指定邊框,邊框類Border的創(chuàng)建方法與EdgeInsets有些類似,也分為上、下、左、右4條邊框,通過Border.all將4種指定為同一個值。邊框包含3個屬性,分別是顏色、粗細,以及邊框樣式。borderRadius屬性用于指定圓角,傳入BorderRadius.circular指定一個圓角值。

需要注意的一點是,上面代碼中將color背景色移入BoxDecoration下,而不是像之前代碼那樣放在Container下。這是因為Container的color屬性與decoration屬性不能同時指定。當指定decoration后,如果需要設(shè)置Container的背景色,由于其已被裝飾器接管,因此要設(shè)置到裝飾器中。

運行代碼效果如圖3-7所示。

圖3-7 Container指定邊框效果

7 通過BoxDecoration修改形狀

通過BoxDecoration的shape屬性可以修改Container的形狀。在本章的時鐘實戰(zhàn)項目中,即通過這個屬性實現(xiàn)圓形表盤的繪制。示例代碼如下:

代碼運行效果如圖3-8所示。

8 通過BoxDecoration添加陰影效果

BoxDecoration還支持向Container添加陰影,具體通過boxShadow屬性,傳入一個BoxShad-ow實例的列表。BoxShadow用于描述陰影,其中color表示陰影顏色,offset表示陰影偏移,blurRadius表示陰影邊緣的模糊程度。示例代碼如下:

執(zhí)行代碼,效果如圖3-9所示。本章后續(xù)實戰(zhàn)項目中的新擬物設(shè)計風格也是通過BoxShad-ow屬性實現(xiàn)的。

圖3-8 Container指定形狀效果

圖3-9 Container指定陰影效果

3.1.2 使用CustomPaint創(chuàng)建Flutter自定義視圖

像按鈕、文本這些常見組件,都是“畫”到屏幕上的。這里所說的“畫”,與現(xiàn)實中人們繪畫的過程是一樣的。首先需要有一塊畫布(Canvas),之后使用畫筆(Paint)在上面作畫。不同之處是人們繪制的是自然的線條,而計算機繪制的是規(guī)整的矩形、直線、文本等。

自定義視圖,就是由開發(fā)者通過程序來設(shè)定組件的繪制流程。為此,F(xiàn)lutter提供了CustomPaint組件,它用于在界面中創(chuàng)建一個指定大小的Canvas,并通過painter屬性指定具體的作畫流程。

創(chuàng)建一個新的Flutter工程作為CustomPaint學(xué)習工程,參照2.5節(jié)的實例代碼替換lib/main.dart。在后續(xù)小節(jié)中,只需要用對應(yīng)的CustomPaint組件替換掉Text組件即可。

1 CustomPaint組件

CustomPaint是Flutter中專門用于繪制自定義視圖的組件,提供了對底層繪制API的訪問能力。首先舉一個簡單的例子,在屏幕中繪制一個紅色的圓圈,替換MyHomePage的代碼如下:

運行顯示效果如圖3-10所示。

上面代碼中的RedCirclePainter部分通過復(fù)寫的paint方法指定具體的繪制過程。paint方法包含兩個參數(shù):Canvas是Flutter實際的畫布類,Size為此畫布的大小。在繪制過程中,首先創(chuàng)建畫筆實例paint,并指定了顏色、填充類型及線條粗細。之后通過canvas.drawCircle方法傳入圓心、半徑及畫筆,在畫布上繪制出紅色的圓圈。

CustomPaint組件包含的屬性見表3-2。

圖3-10 繪制紅色圓圈

表3-2 CustomPaint組件屬性

2 Canvas畫布類

在CustomPainter的paint方法中的canvas參數(shù),是用于繪制的畫布。可以將畫布看作一個二維坐標系,通過傳入坐標在其上進行繪制。在Flutter中,坐標系的方向如圖3-11所示。

圖3-11 Canvas坐標系

除了繪制圓形之外,canvas還提供了一系列繪制方法,用于在畫布上繪制不同的圖形元素。下面列舉常用的繪制方法。

使用drawLine繪制直線,示例代碼如下,只需要替換paint方法即可:

運行效果如圖3-12所示。

3 Paint畫筆類

Paint類用于控制在Canvas上繪制時的繪制樣式。大多數(shù)Canvas繪制方法需要傳入一個Paint對象,并按照Paint對象中的繪制樣式進行繪制。

在前面的代碼中已經(jīng)用到了Paint類,通過其color屬性更改顏色,通過strokeWidth屬性更改線條粗細。

Paint常用屬性見表3-3。

圖3-12 drawLine直線繪制效果

表3-3 Paint常用屬性

3.1.3 Flutter動畫入門與擬物時鐘的開發(fā)流程

良好的用戶體驗是移動端應(yīng)用的特色之一,移動端應(yīng)用通常會加入大量動畫元素提升應(yīng)用的交互體驗。Flutter框架在設(shè)計時充分考慮了動畫的重要性,提供了一個非常強大且易用的動畫開發(fā)框架,能夠快速開發(fā)出復(fù)雜動畫,并高效執(zhí)行。

所謂動畫,實際上是屏幕上的元素以一定的時間間隔進行位置變換。由于時間間隔非常短,且移動的距離非常小,從人眼感受來看,這個過程是流暢平滑的。如果感到難以理解,可以在網(wǎng)絡(luò)上查閱一下“定格動畫”,這是一種歷史悠久的動畫形式,它的原理是將實物擺放好后進行拍照,之后對實物進行微調(diào)后再拍照,如此往復(fù)得到了大量照片,然后再以一定的速度播放這些照片,人眼感受到的就是流暢的動畫。

1 動畫要素

從開發(fā)角度來看,動畫效果可以被拆解為值的變化。比如一個視圖元素橫向移動,實際上是這個元素的橫坐標在移動,而橫坐標的移動,可以被抽象為一個值隨著時間變化,這個過程被稱為插值。

從插值的角度,可將動畫過程拆分為以下要素。

(1)起始值和終止值

以平移動畫為例,起始值為元素的起始橫坐標,終止值為元素在動畫終止時刻的橫坐標。

對于一個動畫過程,開發(fā)者首先要指定起始值和終止值。對于變色動畫來說,起始值為元素的起始顏色,終止值為動畫終止時的目標顏色。

(2)插值

在動畫過程中,從起始值到終止值的變化過程由插值器產(chǎn)生。插值器的作用是隨著時間變化生成動畫的中間狀態(tài),這一個過程稱之為插值。

以平移動畫為例,假設(shè)元素勻速運動,整個動畫過程為1s,在指定好起始值和終止值后,啟動動畫,插值器會根據(jù)當前時間計算此時元素應(yīng)當處于哪個位置,并以回調(diào)的方式將這個值返回。開發(fā)者通常在回調(diào)中接收這個值,并通過setState方法更新元素的橫坐標。

插值還分為勻速插值變速插值。勻速插值比較好理解,從起始值以恒定的步長運動到終止值。而變速插值的步長是不固定的,從而形成變速運動效果。以變速平移為例,通過使用變速插值,可以讓元素運動“先快后慢”“先慢后快”,或者“先快,中間慢,最后再快”。

變速插值在移動應(yīng)用中被廣泛使用,常見的彈層、懸浮框等都大量采用變速插值進行展示、隱藏,能夠給用戶帶來更加自然、流暢的使用體驗。在本章的實戰(zhàn)項目擬物時鐘中,表針的轉(zhuǎn)動就應(yīng)用了變速插值,能讓用戶感受到表針像真實表針一樣在轉(zhuǎn)動,增強了擬物感。

(3)動畫時長

動畫時長指定整個動畫的執(zhí)行時間,決定了動畫執(zhí)行的快與慢。以平移動畫為例,假設(shè)動畫時長設(shè)置非常長,在同樣的平移距離下,物體移動會非常緩慢。

(4)幀

屏幕是以幀進行渲染的,手機會以每秒60幀或更高的刷新率對屏幕進行刷新,稱為幀率。看似連續(xù)的動畫過程,實際是由插值器拆成離散的值后,通過Flutter的渲染機制渲染為離散的幀后展示出來的。只不過在60幀的幀率下人眼已經(jīng)無法感受到離散幀的跳變了。目前市場上出現(xiàn)了高刷新率手機,能夠達到每秒90幀、每秒120幀甚至更高的刷新率,能夠給用戶帶來更加流暢的使用體驗。

2 AnimationController動畫控制器

有了前面的知識鋪墊后,接下來學(xué)習Flutter的動畫框架。在Flutter中,針對不同的職責創(chuàng)建了不同的類,比如AnimationController用于動畫控制,Tween用于指定起始值和終止值,Curve用于變速插值。通過將這些類結(jié)合在一起,共同組裝出一個動畫Animation。在初學(xué)時可能會因為涉及的類和概念比較多而感到有些亂,實際上只要通過學(xué)習完成幾個示例,就能夠體會到這種架構(gòu)劃分的合理性與強大之處。

AnimationController用于控制動畫過程,職責是控制動畫的啟停。通過以下代碼可以創(chuàng)建一個AnimationController:

在上面的代碼中,傳入了兩個參數(shù),duration控制動畫時長,vsync是一個特殊參數(shù),用于優(yōu)化動畫資源,實際傳入類型為TickerProvider。TickerProvider會在后文中進行講解,這里先介紹動畫框架的使用。

AnimationController創(chuàng)建完成后,通過forward可以啟動動畫:

在頁面退出時,如果有動畫還在執(zhí)行,會導(dǎo)致內(nèi)存泄漏。AnimationController提供了一個dispose方法,用于銷毀動畫。AnimationController的典型使用方法為在類中作為一個屬性,在initState中進行創(chuàng)建,在頁面dispose時進行釋放,如下面代碼所示:

在上述代碼中,關(guān)于SingleTickerProviderStateMixin可暫時忽略,將在后文中進行講解。

3 執(zhí)行動畫并打印插值過程

前面提到了插值的概念,AnimationController中自帶有一個勻速插值器,并且默認起始值為0,終止值為1。下面以一個實例介紹如何執(zhí)行動畫。創(chuàng)建一個組件,代碼如下:

運行代碼,可以看到命令行中輸出了插值,注意觀察日志時間的變化:

4 使用Tween設(shè)置起始值與結(jié)束值

AnimationController默認起始值為0,結(jié)束值為1,對于平移動畫來說,希望能夠自定義區(qū)間值。這時可使用Tween組件對起止值進行更改。在Tween的animate方法中,傳入一個Ani-mationController實例。對initState方法進行修改,示例代碼如下:

在上面的代碼中,修改了插值監(jiān)聽回調(diào)的添加位置,之前是在AnimationController上添加監(jiān)聽,現(xiàn)在是在Tween.animate返回的animation對象上監(jiān)聽,同時取插值也改成了從animation對象中獲取。運行代碼,命令行輸出日志如下:

5 完成平移動畫

到目前為止都是在打印插值數(shù)值,build方法的布局中只有一個靜態(tài)的Container方法。下面創(chuàng)建一個矩形,并使其平移運動起來。修改組件如下:

再次運行代碼,可看到紅色矩形的平移運動,具體如圖3-13所示。

圖3-13 視圖平移動畫

6 CurvedAnimation變速插值

前面介紹的AnimationController功能為勻速插值,如果要實現(xiàn)變速插值,需要使用CurvedAnimation。

CurvedAnimation的功能建立在AnimationController之上,在創(chuàng)建變速動畫時,首先要創(chuàng)建一個AnimationCon-troller實例,之后再創(chuàng)建CurvedAnimation實例,并將Ani-mationController傳入CurvedAnimation的構(gòu)造方法。示例代碼如下:

在上面的代碼中,CurvedAnimation還接收一個curve參數(shù),用于傳入變速插值曲線。在Flutter中預(yù)設(shè)了數(shù)十種變速曲線,可訪問https://api.flutter.dev/flutter/animation/Curves-class.html進行查看。以Curves.elasticInOut為例,其變速曲線如圖3-14所示。

圖3-14 Curves.elasticInOut變速曲線

在平移動畫示例中,修改initState方法,實現(xiàn)變速動畫,具體代碼如下:

再次運行程序,可以看到紅色方塊由勻速運動變?yōu)樽兯龠\動,并且按照Curves.elasticInOut的變速曲線實現(xiàn)了一種類似于蓄力沖刺的運動效果,使得原本平淡無奇的動畫變得富有動感。

7 TickerProvider

之所以將TickerProvider放到后面來講,是因為它與動畫的創(chuàng)建與使用關(guān)系不大,而是與動畫的底層渲染相關(guān)。通過前面的小節(jié)完成了對Flutter動畫的入門后,本節(jié)重點介紹TickerPro-vider。

為了更好地理解Flutter的TickerProvider機制,先設(shè)想如果由開發(fā)者自己實現(xiàn)一個動畫框架,會是什么樣子呢?首先需要創(chuàng)建一個定時器,每次觸發(fā)時根據(jù)觸發(fā)時間計算插值器的值,并對上層進行回調(diào)。問題在于定時器的時間間隔,如果間隔太長,比如500 ms刷新一次,假設(shè)一個動畫總共時長才1s,即它在動畫過程中只插值了一個中間點,從用戶角度看相當不流暢。如果間隔設(shè)置太短,比如0.1 ms刷新一次,這樣能夠在動畫過程中插入足夠的中間點,保證動畫的流暢性,但是屏幕的刷新率是固定的(60 Hz、120 Hz),如果動畫的刷新率高于屏幕刷新率,多出來的中間點用戶是看不到的,白白增加了性能損耗。

最好的方式是什么樣的呢?是動畫的間隔跟屏幕的刷新率保持一致,保證每次動畫中間狀態(tài)的更新都被渲染在屏幕上,這就是TickerProvider的作用。在Flutter動畫框架中,包含以下概念。

? Ticker:會在每幀開始時觸發(fā)回調(diào)。

? TickerProvider:通常實現(xiàn)為SingleTickerProviderStateMixin,是對Ticker進行封裝。

通過向AnimationController中傳入TickerProvider,就能監(jiān)聽屏幕刷新,保證動畫的刷新率與屏幕刷新率一致。這樣,既避免了動畫刷新慢導(dǎo)致的不流暢,也避免了刷新過快導(dǎo)致的資源浪費。

8 使用AnimatedBuilder高效開發(fā)動畫

前面介紹了創(chuàng)建動畫的方法,即使用AnimationController,結(jié)合Tween、CurvedAnimation創(chuàng)建動畫效果。這是一種通用的動畫創(chuàng)建機制,通過組合能夠創(chuàng)建出復(fù)雜的動畫效果。

但是在實際中,AnimationController需要在類中通過屬性持有,需要在initState和dispose中進行初始化與銷毀,使用起來比較煩瑣。有沒有一種更加簡單、高效的動畫開發(fā)方式呢?答案是肯定的,F(xiàn)lutter提供了AnimatedBuilder用于以組件化的形式創(chuàng)建動畫。

比較常用的是TweenAnimationBuilder,它通過幾個屬性對動畫進行配置,tween屬性用于設(shè)置動畫的起止值,curve屬性用于設(shè)置變速曲線,duration屬性設(shè)置動畫時長,builder屬性接收一個回調(diào)方法,在方法參數(shù)中含有插值,在函數(shù)中更新組件的最新狀態(tài)并返回。使用Tween-AnimationBuilder實現(xiàn)彈性平移動畫的代碼如下:

在上面的代碼中,可以看出通過使用TweenAnimationBuilder,代碼量有所降低,同時也實現(xiàn)了統(tǒng)一的組件化編程風格,提高了開發(fā)效率和代碼質(zhì)量

有了理論開發(fā)知識作為鋪墊,下面進行擬物時鐘的產(chǎn)品功能構(gòu)思。擬物時鐘是一款仿照現(xiàn)實的時鐘,同時帶有擬物化設(shè)計風格。移動端設(shè)計風格經(jīng)歷過從擬物化到扁平化的轉(zhuǎn)變,近年來在扁平化的基礎(chǔ)上誕生了一種新擬物化設(shè)計風格,非常美觀。在擬物時鐘項目中,使用這種新擬物設(shè)計風格。

擬物時鐘的原型圖如圖3-15所示。其中包含時針與分針用于指示時間,還包括一個文本框,用于展示當前的日期。原型圖僅用于示意產(chǎn)品的功能,因此看起來有些“簡陋”,通過在接下來實戰(zhàn)中添加擬物設(shè)計樣式,會讓最終的效果變得“潮”起來。

圖3-15 擬物時鐘原型圖

主站蜘蛛池模板: 电白县| 库车县| 广河县| 黔南| 屏南县| 峨眉山市| 苏州市| 五河县| 博白县| 博乐市| 壶关县| 富阳市| 巫山县| 卢龙县| 临洮县| 八宿县| 三江| 华阴市| 革吉县| 花莲县| 红河县| 尼勒克县| 卢湾区| 麻栗坡县| 翁源县| 烟台市| 通榆县| 肥乡县| 沽源县| 望谟县| 牡丹江市| 嘉峪关市| 浦东新区| 三穗县| 肥乡县| 长泰县| 五家渠市| 桐城市| 长葛市| 乌苏市| 汾阳市|