《101 Windows Phone 7 Apps》读书笔记-Silly Eye
課程內容
? Animation
? Event Triggers
? Named Resources
? Settings Page
? Color Picker
? Clipping
?
??? Silly Eye是一個非常吸引眼球的應用,特別是在一群小孩子中間。該應用程序會以一種有趣、近乎瘋狂的方式來展示一個巨大的卡通眼球。展示該應用程序,只需要將手機放在右眼的前面,并假裝它就是你的右眼球。本應用程序介紹了一些有用的新技術,并且與創建新的頁面和選擇用戶自定義的顏色相關。但最重要的是,這是與本書第二部分的主題“Transforms & Animations”中“Animations”相關的第一個應用程序。
?
Introducing Animation
??? 大多數人看到動畫,就會認為是一種類似于卡通的東西,它利用連續快速地播放圖片來模擬運動效果。在Silverlight中,動畫有一個更加詳細的定義:在時間軸上改變一個屬性的值。這與運動相關,例如,通過增加元素的寬度來營造一種生長的效果,或者,改變一個元素的不透明度來營造另一種完全不同的效果。
??? 在時間線上改變一個屬性的值有很多種方法。最經典的方法是使用定時器,比如,很多前面章節中使用的DispatcherTimer,以及基于定時器觸發時間而設置的周期性回調方法(Tick事件處理)。在這種方法中,我們可以手動更新目標屬性(即基于逝去的時間,通過數學計算來決定當前的屬性值),直到最終的設定值。這個時候,我們可以停止計時器,并且/或者刪除事件處理程序。
??? 但是,Silverlight提供了一種更容易使用的動畫機制,它更加強大,性能要比定時器方法更加出色。這是一種以Storyboard的對象為中心的機制。Storyboard包含了一個或多個特定的動畫對象,它們被應用到特定元素的指定屬性中。
??? Silly Eye使用了三個Storyboard來實現動畫。為了理解Storyboard的概念和工作原理,我們來學習以下三個內容:
? 與瞳孔相關的 Storyboard
? 與虹膜相關的 Storyboard
? 與眼瞼相關的 Storyboard
?
The Pupil Storyboard
???? 下面是Silly Eye應用程序中,將Storyboard應用到瞳孔中,使其進行重復地擴張和縮小。其注意點如下:
? Storyboard.TargetName這個可附加的屬性表明,該動畫被應用到本頁面中一個名為Pupil的元素中,而Pupil是一個橢圓。
? Storyboard.TargetProperty這個可附加的屬性表明,Pupil的StrokeThickness屬性被設置了動畫效果。
? Storyboard中的DoubleAnimation表明,StrokeThickness這個值會在半秒鐘之內,從100變為70。DoubleAnimation中的“Double”代表了目標屬性的類型(因為StrokeThickness類型是double)。
? 因為AutoReverse被設置為true,所以,在StrokeThickness達到70時,會自動從70變為100。由于RepeatBehavior被設置為Forever,所以只要應用程序啟動以后,這種重復的動作就會一直持續。
? EasingFunction屬性(設置為ElasticEase的實例)控制著StrokeThickness值是如何在時間線上進行改寫的。這部分的內容會在下面的“Interpolation”節介紹。
開始動畫中,storyboard的Begin方法調用如下:
this.PupilStoryboard.Begin();
??? 這個動畫在整個應用程序中表現出來的效果如圖12.2所示。Pupil這個橢圓的元素在背后代碼中被賦為亮藍色的畫刷。
圖12.2 PupilStoryboard使得Pupil的stroke(藍色部分)厚度從100縮小到70,因而使得黑色填充變大。
??? 在XAML中,有一種方法來觸發storyboard的所有行為。因此,我們沒有必要在背后代碼中調用它的Begin方法。我們可以為一個元素的Triggers屬性添加一個事件觸發程序。幸虧這個特殊的BeginStoryboard元素,在grid的Loaded事件中,它會內部調用storyboard 的Begin方法。而Loaded事件是Silverlight事件觸發中支持的唯一一個事件。
?
Types of Animations
??? Silverlight 提供了animation類來實現四種不同的數據類型:double、Color、Point和Object。在Silly Eye應用程序中,只使用了double屬性。剩余的類型在第15章“Mood Ring”中介紹。如果我們想要在時間線上改變元素中double類型的屬性值(比如Width、Height、Opacity、Canvas.Left等等),我們可以使用DoubleAnimation的實例。如果我們想要在時間線上改變元素中Point類型的屬性值(比如一個線性漸變brush的StartPoint和EndPoint屬性),我們可以使用PointAnimation的實例。目前為止,由于大量的double類型的屬性需要動畫效果,所以DoubleAnimation是最常用的動畫類。
并不是所有的屬性都可以做動畫效果,即使它的數據類型與動畫類使用的數據類型匹配!
本章內容所討論的動畫機制,只有那些被稱為“依賴項屬性”(dependency property)的類型才能夠使用。幸運的是,大部分視覺元素中的屬性就是該類型的。依賴項屬性的有關內容會在第18章“Cocktails”中討論。與判斷一個事件是否是“路由事件”(routed event)類似,我們可以通過檢查類中所包含的一個名為PropertyNameProperty的DependencyProperty類型的靜態字段來決定該屬性是否是依賴項屬性。如果該類中包含了這種字段,如ellipse類中的StrokeThicknessProperty字段,那么它就是一個依賴項屬性。
??? 為了達到理想的效果,思考一個元素的哪些屬性來做動畫,這需要通過一些試驗來決定。例如,如果我們想要一個元素以漸變的方式出現,那么,對它的Visibility屬性做動畫就沒有任何意義,因為在Collapsed 和 Visible兩者之間沒有中間值。相反,我們應該對它的Opacity屬性做動畫,它的值是double類型的,范圍從0到1。
?
Interpolation
??? 認識下面這點很重要:在默認情況下,DoubleAnimation通過時間線上的線性插值來達到平緩地改變double類型的屬性值。換句話說,在一秒鐘之內,如果該值從50增加到100,那么,在0.1秒的時刻,它的值就是55(時間增加10%,其值也線性增加10%),在0.5秒的時刻,它的值就是75(時間增加50%,其值也線性增加50%),以此類推。這也就是圖12.2中,StrokeThickness屬性的中間值是85的原因。
??? 但是,Windows Phone應用程序中使用的大部分動畫是非線性的。而且,他們更傾向以突然加速或者突然減速的方式從一個值改變到另一個值。這種方式使得動畫變得妙趣橫生。我們可以通過應用一個緩和的函數來實現這種非線性的動畫效果。
??? 這種過渡函數負責屬性值從起始到最終值之間的自定義插值。Pupil Storyboard使用了名為ElasticEase的函數來實現這種行為。圖12.3展示了屬性值從100減小到70時,使用默認的線性變換和彈性變換之間的差異。在這種情況下,85這個中間值并不是在中間時間點達到,它其實更接近于終點時才達到。
圖12.3 ElasticEase過渡函數極大地改變了double類型值從100減小到70的方式。
??? Silverlight提供了11個不同的過渡函數,每個函數有三種不同的模式,有些函數提供了更深層次的屬性行為自定義。例如,ElasticEase具有Oscillations 和 Springiness 屬性,默認設置為3。在實際應用中,如果我們想要在動畫中加入自定義的函數,那么這種自定義行為的可能性是無窮無盡的。與默認的線性過渡相比,本應用中使用的過渡函數為用戶提供了一種完全不同的體驗。
??? 附錄D“Animation Easing Reference”中展示了每個內置過渡函數的行為。我覺得這些函數非常有用,因為每次當我想要設計一個新的動畫時,我都會回去參考這些函數。
另一種產生非線性動畫的方法
過渡函數并非是產生非線性過渡動畫效果的唯一方法。第14章“Love Meter”中討論的Keyframe animations,使得我們可以利用不同的差值方法,將一個動畫分解為多個片段。
?
The Iris Storyboard
??? Silly Eye應用程序將以下Storyboard應用到了一個名為Iris的canvas控件中,使得眼球看上去在左右移動。其注意點如下:
? TargetProperty的語法與其名稱相比,稍顯復雜。當它設置為一個可附加的屬性(如Canvas.Left)時,它必須被包含在括號內。
? 該動畫使用了一個不同的過渡函數,使得其運動的邊界更加明顯。關于BounceEase的行為,請參考附錄D。
? 該動畫缺少了From值!這是可行的,而且我們推薦這樣處理。當From值沒有指定時,動畫就從目標屬性的當前值開始,而不管該值大小為多少。同樣,一個動畫可以指定From值,但并不指定To值!這樣的話,動畫就從屬性的指定值開始,到當前值為止。
??? 為了達到平緩的效果,將From設置為缺省是很重要的,特別是動畫開始于一個可重復的用戶輸入。比如,在用戶點擊一個界面元素時,開始它增長的動畫,那么,每次快速的點擊會使得其大小迅速變回From設定的值。但是,通過缺省的From值設定,隨后的點擊也會使動畫從當前值開始,使得動畫更加的平滑和自然。
在目標屬性值無法插值的情況下,我們必須指定From 和 To 的值!
假如我們嘗試著為一個auto-sized元素的寬度或者高度做動畫效果,而它的From和To沒有指定,那么,動畫效果就不會出現。當元素的寬度或者高度被設置為Double.NaN(非數值)時,它的大小是自適應的。因為當兩個值中存在一個非數值的數時,DoubleAnimation也就無法完成插值的操作。而且,將動畫應用到ActualWidth或者ActualHeight中去(它們被設置為真實的寬度或高度值,而非NaN),這并不是一個好的選擇。因為這些屬性是只讀的,而且并不是依賴項屬性。相反,為了有動畫效果,我們必須顯式地設置目標元素的寬度/高度。
??? 對于Pupil Storyboard來說,我們必須調用Storyboard的Begin方法來使得它開始工作。
??? 動畫的效果如圖12.4所示。Iris Canvas 除了包含 Pupil ellipse(事實上,它的外圍就是Iris)以外,還包括另外兩個Ellipse,它們為Iris增添了“光澤”。因為Canvas控件的位置加入了動畫效果,所以其包含的所有視覺元素都會一起移動。
圖12.4 IrisStoryboard水平移動Iris Canvas,其值從287(它初始的Canvas.Left值)增加到543。
??? 動畫效果類中,也有一個名為 By 的字段,它可以用來代替 To 字段。下面的動畫意味著“在當前屬性值的基礎上,增加256”:
<DoubleAnimation By=”256” Duration=”0:0:2”/>
??? 對于縮減當前值時,出現負值的情況也是支持的。
?
The Eyelid Animation
??? Silly Eye應用程序中使用的最后一個storyboard,用來對具有皮膚顏色的Eyelid Ellipse模仿眨眼的效果。對于Pupil Ellipse來說,皮膚顏色的畫刷在背后的代碼中進行設置。其注意點如下:
? Storyboard.TargetName 和 Storyboard.TargetProperty 這兩個屬性被設置為attachable的原因是:它們可以在單獨的動畫中使用,而不用去理會任何Storyboard中的設置。該Storyboard以 Eyelid Ellipse 中的 Height 和 Canvas.Top 這兩個屬性為目標。因此,單個目標的名字被標記在了Storyboard上,但是多個不同的目標屬性被用來標記多個不同的動畫效果。
? Canvas.Top 與 Height 具有同步的動畫效果,所以橢圓在垂直縮小的同時,保持在中間的位置。下一章介紹的“Transforms”,提供了一種更加快捷的方法來實現這個效果。
? 這兩種動畫都使用了默認的線性插值方法。它們的移動速度如此之快,以至于沒有必要再去嘗試別的更具生命力的方法。
? Storyboard不僅僅是一個簡單的用來給相關的物體實現動畫效果的容器。該Storyboard具有自身的持續行為和重復行為!這兩種動畫只持續了0.2秒鐘的時間(0.1秒之內將屬性的當前值從380減小到50,另外的0.1秒鐘之內,由于其auto-reverse設置,將屬性值變回到原來的值)。但是,因為給Storyboard的持續時間是3秒鐘,而且它自身具有auto-reverse設置(并非是它的子元素),動畫在后面的時間里面保持不動,知道最后3秒時間消耗完。然后,持續0.2秒鐘的動作再次開始,之后便是2.8秒鐘的靜止時間。因此,該Storyboard使得眼睛眨得非常快,且在3秒鐘發生一次。
??? 這種動畫的效果如圖12.5所示(在C#中調用 Begin 方法開始動畫)。因為Eyelid Ellipse與背景的顏色一致(有意在其左邊的相鄰區域用黑色做填充),我們就無法看到Eyelid Ellipse本身。相反,一旦Ellipse的高度(380)小于其填充厚度的兩倍(400),我們可以看到其內部的空間急劇縮小到0。
圖12.5 Eyelid Storyboard 壓縮 Eyelid Ellipse的高度,并向下移動,使其保持居中。
?
Storyboard and Animation Properties
??? 我們已經了解了 Duration、AutoReverse 和 RepeatBehavior 屬性,它們可以應用到單獨的動畫或者一個完整的Storyboard中去。總的來說,有6種屬性可以應用到 Storyboard 和Animation 中去:
? Duration:Animation 或者 Storyboard 的長度,默認值為1秒。
要謹慎指定duration的值!
一個 duration 的字符串格式與 TimeSpan.Parse 這個接口函數的格式相同:
????????????????????????????????????????????????? days.hours:minutes:seconds.fraction
這里允許使用快捷方式,所以我們不用指定每個字符串。但是,它的行為可能不是如你所愿。字符“2”表示2天,而非2秒。字符串“2.5”表示2天零5個小時!字符串“0:2”表示2分鐘。如果大多數動畫都不能超過幾秒鐘的時限,那么,典型的使用格式就是 hours:minutes:seconds 或者 hours:minutes:seconds.fraction。所以,2秒可以表示為“0:0:2”,半秒可以表示為“0:0:0.5”或者“0:0:.5”。
? BeginTime:動畫或者Storyboard延時開始的時間長度,由一個具體的時間值表示,默認為0。對于子元素的動畫,Storyboard可以使用自定義的BeginTime值,使得它們可以相繼開始動畫,而非同時開始。
? SpeedRatio:持續時間(Duration)的乘子,默認為1。我們可以將它設置為任意的大于0的double類型的值。一個小于1的值能夠減緩動畫的速度,一個大于1的值能夠加速動畫的速度。SpeedRatio不會影響BeginTime。
? AutoReverse:該屬性設置為True時,使得動畫或者Storyboard達到終點以后,實現自動回播。回播花費同樣長度的時間,所以SpeedRatio也會影響回播。需要注意的是,通過BeginTime指定的延時并不會影響回播。回播一般會在正常的動畫結束以后,立即啟動。
? RepeatBehavior:可以設置為一個時間段,或者設置為一個字符串,例如“2x”、“3x”或者“Forever”。因此,我們可以使用RepeatBehavior,使得動畫的持續時間減短(或者減少他們的持續時間),或者使得動畫自動重復多次(甚至可以是一個帶小數的倍數,如2.5倍),或者是永遠重復動畫(本章就是使用這個方法)。如果AutoReverse設置為True,那么,回播操作也會重復。
? FillBehavior:可以設置為 Stop,而它的默認值為 HoldEnd,使得相關的動畫完成以后,其屬性值恢復到動畫之前的值。
?
The Total Length of an Animation
??? 通過使用諸如BeginTime、SpeedRatio、AutoReverse和RepeatBehavior屬性,可以對動畫做多方面的調整,在動畫開始以后,測試其持續總時間的長度是有難度的。它的Duration值當然不足以描述真正的時間長度!相反,以下的公式描述了一個動畫真正的持續時間:
總時間= BeginTime +(Duration *(AutoReverse ? 2:1)* RepeatBehavior)/ SpeedRatio
??? 這可以在RepeatBehavior屬性指定為一個double類型的值時使用(或者使用它的默認值1)。如果RepeatBehavior設置為一個時間段,那么總的時間長度就是RepeatBehavior的值加上BeginTime的值。
?
The Main Page
??? Silly Eye主頁面的XAML包含了一些矢量圖片,一個應用程序欄,以及三個Storyboard。它同樣包含了一個“使用說明頁面”,暗示用戶點擊屏幕開始應用,如圖12.6所示。因此,我們一開始可以展示應用程序欄,但是應用程序開始運行時,它就隱藏了,因為屏幕上顯示的按鈕會妨礙應用程序的效果。介紹頁面暗示用戶他們可以通過點擊屏幕,在任何時候達到重新調出應用程序欄的目的。
圖12.6 應用程序欄只有在“介紹頁面”出現使可見
? 應用程序欄包含了導向設置頁面、說明頁面和關于頁面的鏈接。前兩個頁面會在下面兩節中介紹。我們已經在第6章“Baby Sign Language”中學習了關于頁面。我們認為,設置頁面的鏈接作為按鈕放置在應用程序欄,要好于一個菜單項,因為在本應用程序中,用戶對設置進行自定義也是一件很正常的事情(在應用程序的正常操作過程中,應用程序欄不會引入視覺上的混亂,因為它是隱藏的!)。
? 注意,三個Storyboard資源的名稱被命名為“x:Name”,而不是“x:Key”!這是一種方便的手段,使得我們可以更加方便地使用背后的代碼。在我們給資源命名以后,它就可以作為字典中的一個鍵來使用,或者作為C#生成的一個字段。
? 顯式的From值已經從Pupil Storyboard的動畫中移除了,因為它并不是必須的。這部分內容已經在本章進行了介紹,它有助于理解動畫是如何工作的。
? IntroTextBlock元素用來監聽用戶的點擊,并且隱藏IntroPanel。它的寬度值為700,比整個頁面的寬度還要大,因為如果它距離應用程序欄太近的話,用戶在想點擊應用程序欄時,可能不小心點到它(然后就會隱藏應用程序欄)。
?
The Code-Behind
? 由于XAML中的x:Name標記,通過各自的名稱,三個Storyboard在構造函數中初始化。
? 頁面的Clip屬性被設置為一個屏幕大小的矩形區域。這樣做是為了在動畫頁面切換期間,防止對屏幕以外的矢量圖形進行渲染。這不僅避免了出現非常奇怪的視覺元素,而且也有助于提高應用程序的性能。所有具有這個Clip屬性的UI元素可以被設置為一個任意的幾何圖形。
?
Geometries Used for Clipping
??? Clip屬性可以被設置為一些幾何形狀,這些形狀與第5章“Ruler”中介紹的形狀對象類似,但又有不同。我們可以使用 RectangleGeometry、 EllipseGeometry 、LineGeometry、PathGeometry 或者是 GeometryGroup 來組合多個幾何形狀。附錄E“Geometry Reference”中將會討論這些幾何形狀。
? 我們使用兩個存儲設置來存儲皮膚和眼睛顏色的值,它們在OnNavigatedTo事件處理中使用。在OnNavigatedFrom事件處理中,它們沒有必要進行儲存,因為設置頁面會進行處理。這些設置的內容在單獨的Settings.cs文件中定義。事實上,眼睛默認的顏色就是手機的主題強調色,就和本章圖片中展示的藍色一樣。
? IntroPanel的可視性(以及應用程序欄)放置于頁面的狀態中,所以如果頁面在休眠和激活以后,看上去是一致的。無論頁面經歷了多大的改變,也不要忘記使用頁面設置,有了它,在應用程序經歷被打斷、又重新激活時,我們可以快速并且自動地恢復頁面狀態。
? IntroTextBlock的對齊方式在OnOrientationChanged事件中進行調整,這樣做是為了保持它與應用程序欄保持對立的位置關系。前面已經敘述過,在手機的方向為landscape right時,應用程序欄出現在屏幕的左邊;當手機的方向為landscape left時,應用程序欄出現在屏幕的右邊。
?
The Settings Page
??? 設置頁面如圖12.7所示,它使得用戶可以為眼睛和皮膚選擇不同的顏色。
圖12.7 設置頁面使得用戶可以選擇Silly Eye應用程序的顏色。
在系統自帶的設置程序中,如何為我們的應用程序添加一個設置頁面?
在目前Windows Phone 7.0的版本中,我們還無法做到這點。雖然設置應用包含了一個系統設置的列表和一個應用設置的列表,但是后者只是針對系統自帶的應用來說的。相反,我們需要為我們的應用程序添加設置頁面,使其用戶體驗和系統自帶的設置頁面一致。換句話說,對于我們的設置頁面,應該使用“SETTINGS”作為應用程序的名稱出現在標準的header中,使用應用程序的名稱作為頁面標題,如圖12.7所示。
??? 對于設置頁面的設計指導,請參考第20章“Alarm Clock”中的內容。
??? 頁面設計的注意點如下:
? 該頁面的自定義header樣式從App.xaml文件獲得。對于本書中剩余的應用程序來說,App.xaml.cs這個文件同樣提供了自定義的頁面過渡效果,如第19章“Animation Lab”所述。
? 那兩個可點擊的區域顯示了當前的顏色,看上去和按鈕很像,但實際上它們只不過是矩形填充。它們的MouseLeftButtonUp事件處理包含了用戶對于每種界面顏色改變的處理。
? 雖然在不同的方向模式下,內容完全符合屏幕,但是主要的stack panel控件被放置在scroll viewer內。這對于用戶來說,很適合觸摸操作,因為用戶可以用手指拖動屏幕查看內容,并使他們確信瀏覽了屏幕中所有的內容。
??? 在很多頁面中,例如設置頁面、說明頁面或者是關于頁面,將內容放置于scroll viewer中是一個很好的選擇,即使所有的內容可以用一個屏幕來容納。那樣的話,用戶可以用手指做一個快速的拖動,從視覺上就可以判定沒有更多的內容可以看了(因為scroll viewer控件的scroll-and-squish特性)。這種反饋是非常另用戶滿意的。當屏幕對于用戶的拖動沒有反應時,用戶會認為他不夠用力,可能會再嘗試一次。
?
The Code-Behind
??? 為了使用戶能夠改變每個顏色,該頁面會將用戶導航到一個顏色選擇頁面,如圖12.8所示。這個特性頁面被本書中的很多應用程序共用,包含在本書的源代碼中。它提供了一個標準顏色的調色板,它也允許用戶自定義顏色的色相、飽和度和亮度,不管是通過交互式的界面或者是輸入一個十六進制的數值(或者是任何能夠被XAML解析的字符串,如“red”、“tan”或者是“lemonchiffon”)。調整顏色的不透明度是可選的。
圖12.8 顏色選擇器頁面為用戶提供了一個漂亮的頁面來選擇顏色。
??? 顏色選擇器頁面通過查詢字符串可以接受以下四種參數:
? showOpacity-默認值為True,可以將其設置為False來隱藏opacity slider。它也會將調色板頂層的透明顏色移除,并且阻止用戶輸入透光的顏色。因此,當我們將它設置為False時,我們可以確定一個不透明的顏色將會被選中。
? currentColor-當頁面呈現時,開始被選擇的顏色。它必須作為一個對XAML有效的字符串參數傳入。如果指定為一個十六進制的數,“#”必須被移除,這樣做是為了與URI混淆。
? defaultColor-在顏色選擇器頁面中,用戶點擊reset按鈕時,可以得到的顏色。它指定的字符串格式要求與currentColor一樣。
? settingName-隔離存儲空間中使用的名稱,在從頁面返回時,選定的顏色可以從中被找到。在構造一個Setting的實例時,用到了同樣的名字。在列表12.4的OnNavigatedTo方法中,當從顏色選擇器頁面返回時,它自動選擇新的顏色數值,那只是因為導航到顏色選擇頁面之前,需要調用ForceRefresh方法。第20章詳細介紹了這種方法。
??? 使用本書的顏色選擇器頁面(或者類似的頁面)為用戶提供一種簡便高效的顏色選取方法。在當前的版本中,該頁面的主要缺點是只支持portrait的屏幕方向。因此,在landscape屏幕方向下,使用硬件鍵盤輸入一個顏色的十六進制數的用戶體驗并不好。
?
The Instructions Page
??? 本應用程序的說明頁面如圖12.9所示,其注意點如下:
圖12.9 Silly Eye應用程序中的說明頁面
? 和設置頁面采用的方法一樣,主要的內容被放置在scroll viewer中,從而提示用戶沒有更多的內容可以瀏覽。
? 和主頁面中的intro pane一樣,單個text block利用LineBreak元素來格式化其文本內容。
? 對于背后的代碼文件-InstructionsPage.xaml.cs,在其構造函數中,只包含了對InitializeComponent方法的調用。
轉載于:https://www.cnblogs.com/dearsj001/archive/2012/08/22/101App4WP7_SillyEye.html
總結
以上是生活随笔為你收集整理的《101 Windows Phone 7 Apps》读书笔记-Silly Eye的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL 错误对照表
- 下一篇: 关于多边形内点数问题的一些变形