在WP7手機的開始屏幕,如果你Hold住某一個瓷貼,就會發現除了你按住的那個瓷貼其他全部下沉半透明,然后開始在不停地漂來漂去~~
今天來模仿一下這個效果。
新建一個項目,然后在Grid里放一個ListBox。
OK 開始編寫 ListBox 的模版。
首先是ItemsPanelTemplate。
<ListBox.ItemsPanel> <ItemsPanelTemplate> <toolkit:WrapPanel/> </ItemsPanelTemplate> </ListBox.ItemsPanel>
我們放一個WrapPanel,讓它進行自動排列和換行。
然后就是主要的 ItemTemplate。
<ListBox.ItemTemplate> <DataTemplate> <Canvas Width="185" Height="185"> <Grid x:Name="grid"> <Grid.RenderTransform> <TranslateTransform/> </Grid.RenderTransform> <Grid.Resources> <Storyboard x:Name="sbTranslate"> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"> <EasingDoubleKeyFrame KeyTime="0:0:0" Value="0"/> <EasingDoubleKeyFrame x:Name="translateX" KeyTime="00:00:1" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut"/> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"> <EasingDoubleKeyFrame KeyTime="0:0:0" Value="0"/> <EasingDoubleKeyFrame x:Name="translateY" KeyTime="00:00:1" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <SineEase EasingMode="EaseInOut"/> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </Grid.Resources> <Image Visibility="Collapsed" Source="/Images/StartPage_DeleteFav.png" Width="42" Height="42" HorizontalAlignment="Right" VerticalAlignment="Top" Canvas.ZIndex="1" MouseLeftButtonUp="Image_MouseLeftButtonUp" Margin="-10"/> <toolkit:HubTile GroupTag="QuickLink" Notification="{Binding Notification}" Message="{Binding Message}" Title="{Binding Title}" Source="{Binding Src}" Background="{StaticResource PhoneAccentBrush}"/> </Grid> </Canvas> </DataTemplate> </ListBox.ItemTemplate>
這里 放了一個 Grid ,里面很簡單,一個 Image 用來 做 右上角刪除圖標。然后是一個 HubTile,簡單模仿一下。
主要的是 Grid 里 寫的 資源,兩個 DoubleAnimationUsingKeyFrames,用來操作 TranslateTransform。
好,前台很簡單。來看看后台。先為 ListBox 添加幾個事件:
Loaded="listBox_Loaded" LostFocus="listBox_LostFocus" SelectionChanged="listBox_SelectionChanged"
先來寫Loaded 事件:
for (int i = 0; i < listBox.Items.Count; i++) { ListBoxItem item = (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromIndex(i); item.Hold += item_Hold; }
為每個listboxitem 添加 必須的 Hold 事件。
void item_Hold(object sender, System.Windows.Input.GestureEventArgs e) { if (!isHold) { isHold = true; var item = sender as ListBoxItem; holdItem = item; for (int i = 0; i < items.Count; i++) { ListBoxItem _item = (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(items[i]); if (_item != item) { Transform(1, 0.75, _item, 0.2); var grid = FindVisualChild<Grid>(_item); var sb = grid.Resources["sbTranslate"] as Storyboard; storyList.Add(Bouncing(sb,false), grid.DataContext); } else { //顯示按住item的關閉按鈕 var img = FindVisualChild<Image>(_item); img.Visibility = System.Windows.Visibility.Visible; } } } }
在item_Hold事件里做了兩件事:顯示按住item的關閉按鈕,其他item 下沉半透明,然后是啟動漂動:Bouncing(sb,false);
Bouncing 這個方法,返回一個 DispatcherTimer 第一個參數是一個Storyboard,那這里放入的就是在前台為 Grid 添加的 資源里的 Storyboard。
先是找到這個 ListboxItem 里的 FindVisualChild<Grid>(_item),然后再找出他的 Storyboard。
那重點來說一下這個函數。
private DispatcherTimer Bouncing(Storyboard sb,bool isover) { Random random = new Random(); DoubleKeyFrame sp0 = ((DoubleAnimationUsingKeyFrames)sb.Children[0]).KeyFrames[0]; DoubleKeyFrame ea0 = ((DoubleAnimationUsingKeyFrames)sb.Children[0]).KeyFrames[1]; DoubleKeyFrame sp1 = ((DoubleAnimationUsingKeyFrames)sb.Children[1]).KeyFrames[0]; DoubleKeyFrame ea1 = ((DoubleAnimationUsingKeyFrames)sb.Children[1]).KeyFrames[1]; sp0.Value = 0; sp1.Value = 0; ea0.Value = 0; ea1.Value = 0; DispatcherTimer dispatcherTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(0) }; dispatcherTimer.Tick += delegate { sb.Stop(); sp0.Value = ea0.Value; sp1.Value = ea1.Value; if (!isover) { ea0.Value = random.Next(-10, 10);//隨機數 X ea1.Value = random.Next(-10, 10);//隨機數 Y sb.Begin(); } dispatcherTimer.Interval = TimeSpan.FromSeconds(random.Next(9, 13) *0.1); }; if (isover) sb.Begin(); else dispatcherTimer.Start(); return dispatcherTimer; }
先是根據傳入的 Storyboard 找出 它里面DoubleAnimationUsingKeyFrames的每個DoubleKeyFrame。
然后在找出每個DoubleKeyFrame的Value,並初始化。
接下來是生成一個DispatcherTimer對象,這里初始化的Interval是0,也就是立馬執行下面Tick的事件,不會造成剛開始動畫延遲的感覺。
在對ea0和ea1賦值,用一個隨機數,范圍在-10到10。也這就在這個方圓內漂動。
而上面的sp0和sp1的賦值是為了下一次動畫,不是從零開始的。
下面還有一句:dispatcherTimer.Interval = TimeSpan.FromSeconds(random.Next(9, 13) *0.1);
這句是為了讓每個Timer的間隔有所不同,就不會造成所有瓷貼同時漂動同時結束的統一動作。
OK,主要的代碼都介紹完了,上圖
還有很多功能,比如 listBox_SelectionChanged,OnBackKeyPress,StopBouncing,就不多介紹了,這里就說一下我實現的方法思路,
就是通過,調用動畫通過 Timer 來控制,來實現 飄動效果。
如果有興趣,可以繼續了解我下面的 DEMO。