開發過android手機應用的人肯定對gallery control這個控制項很熟悉,這個控制項實現了幾個螢幕之間的動態切換,也就是Andriod手機首頁上那幾頁滑來滑去的效果。當然,windows phone上也有相應的東東,譬如panorama控制項和pivot控制項,但無奈這兩個控制項都不是gallery control控制項的對應版,例如panorama永遠會在右端顯示那20像素,告訴人們右邊還有內容,而pivot控制項在滑動的時候頁面會變成背景色而看不到一點東西,如果沒有背景將會是黑乎乎的一片。這兩者可能在別的場合用的很好,但卻沒有實現gallery
control那種連續和漸進式的效果。既然不能生搬硬套,那隻能自己實現windows phone版的gallery control了。
在stackoverflow上提問,無奈沒有解答,用google搜,如大海撈針,好在功夫不負有心人,最終還是找到了一個國外大牛給出的解決方案。下面就詳細說說如何自己實現gallery control。
首先給出效果,這裡只有三個頁面,無奈不能動態,這裡只好看看靜態了:
從上面三個頁面可以看到頁面可以分為兩個部分,上面是一個panoramatitle控制項,底部就是類似於item的控制項,不過這些控制項都是需要自己實現,整個工程結構如下:
其中,panoramicTile控制項布局如下:
<UserControl x:Class="PhoneApp1.PanoramicTitle" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" d:DesignHeight="140" d:DesignWidth="1200"> <Grid x:Name="LayoutRoot" Background="#FF1F1F1F"> <TextBlock Style="{StaticResource PhoneTextPageTitle2Style}" Text="Panoramic title" /> </Grid></UserControl>
windowsphoneControl1控制項布局如下:
<UserControl x:Class="PhoneApp1.WindowsPhoneControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" d:DesignHeight="480" d:DesignWidth="480"> <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitleGrid is the name of the application and page title--> <Grid x:Name="TitleGrid" Grid.Row="0"> <TextBlock Text="MY APPLICATION" x:Name="textBlockPageTitle" Style="{StaticResource PhoneTextPageTitle1Style}"/> <TextBlock Text="First page" x:Name="textBlockListTitle" Style="{StaticResource PhoneTextPageTitle2Style}"/> </Grid> <!--ContentGrid contains ListBox. Place additional content here--> <Grid x:Name="ContentGrid" Grid.Row="1"> <TextBlock Height="135" HorizontalAlignment="Left" Margin="6,6,0,0" Name="textBlock1" Text="This is the first page!" VerticalAlignment="Top" Width="468" /> </Grid> </Grid></UserControl>
其他兩個控制項就是改了其中的textblock內容而已,這裡不再贅述。
下面來說說最重要的mainpage, 其xaml頁面如下:
<phone:PhoneApplicationPage x:Class="PhoneApp1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="800" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" Loaded="PhoneApplicationPage_Loaded" xmlns:my="clr-namespace:PhoneApp1" > <UserControl.Resources> <Storyboard x:Name="PageChangeAnimation"> <DoubleAnimation To="-480.0" SpeedRatio="4" Storyboard.TargetName="PanoramaContentTranslate" Storyboard.TargetProperty="X" /> <DoubleAnimation x:Name="SlideTitleDoubleAnimation" SpeedRatio="4" Storyboard.TargetName="TitleTranslate" Storyboard.TargetProperty="X" /> </Storyboard> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}"> <Grid.RowDefinitions> <RowDefinition Height="140" /> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Grid.Column="0" x:Name="TitlePanel"> <StackPanel.RenderTransform> <TranslateTransform x:Name="TitleTranslate" /> </StackPanel.RenderTransform> </StackPanel> <Grid x:Name="PanoramicGrid" Grid.Row="1" Grid.Column="0" ManipulationDelta="PhoneApplicationPage_ManipulationDelta" ManipulationCompleted="PhoneApplicationPage_ManipulationCompleted" > <Grid.RenderTransform> <TranslateTransform x:Name="PanoramaContentTranslate" X="-480" Y="0" /> </Grid.RenderTransform> <Grid.RowDefinitions> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="480" /> <ColumnDefinition Width="480" /> <ColumnDefinition Width="480" /> </Grid.ColumnDefinitions> </Grid> </Grid></phone:PhoneApplicationPage>
可以看到其中定義了動畫效果,滑動的依據是X軸方向上的位移量,首頁面是一個兩行三列的grid,上面一行就是放panoramictitle控制項的地方,下面三列分別對應三個windowsphonecontrol控制項。
mainpage.xaml.cs代碼如下:
using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Windows;using System.Windows.Controls;using System.Windows.Documents;using System.Windows.Input;using System.Windows.Media;using System.Windows.Media.Animation;using System.Windows.Shapes;using Microsoft.Phone.Controls;namespace PhoneApp1{ public partial class MainPage : PhoneApplicationPage { /// <summary> /// Size of the pages /// </summary> public const int PageWidth = 480; public MainPage() { //建立三個頁面列表 this.PageList = new List<UserControl>() { new WindowsPhoneControl1() { IsEnabled = false }, new WindowsPhoneControl2() { IsEnabled = false }, new WindowsPhoneControl3() { IsEnabled = false } }; this.CurrentPageIndex = 0; InitializeComponent(); SupportedOrientations = SupportedPageOrientation.Portrait; } /// <summary> /// Ordered list of the panorama pages /// </summary> protected List<UserControl> PageList { get; set; } /// <summary> /// Index of the page currently displayed /// </summary> protected int CurrentPageIndex { get; set; } private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { var frame = (PhoneApplicationFrame)Application.Current.RootVisual; frame.Width = PageWidth * 3; //載入panoramictitle控制項 var title = new PanoramicTitle(); this.TitlePanel.Children.Add(title); this.LoadPages(); } private void LoadPages() { this.PanoramicGrid.Children.Clear(); //滑倒第一頁再向左滑動 if (CurrentPageIndex == -1) { CurrentPageIndex = 2; } //滑倒第三頁再向右滑動 if (CurrentPageIndex == 3) { CurrentPageIndex = 0; } var currentPage = this.PageList[this.CurrentPageIndex]; currentPage.IsEnabled = true; this.PanoramicGrid.Children.Add(currentPage); Grid.SetColumn(currentPage, 1); Grid.SetRow(currentPage, 1); if (this.PageList.Count > this.CurrentPageIndex + 1) { var nextPage = this.PageList[this.CurrentPageIndex + 1]; nextPage.IsEnabled = false; this.PanoramicGrid.Children.Add(nextPage); Grid.SetColumn(nextPage, 2); Grid.SetRow(nextPage, 1); } if (this.CurrentPageIndex > 0) { var previousPage = this.PageList[this.CurrentPageIndex - 1]; previousPage.IsEnabled = false; this.PanoramicGrid.Children.Add(previousPage); Grid.SetColumn(previousPage, 0); Grid.SetRow(previousPage, 1); } } private void PhoneApplicationPage_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { if (e.OriginalSource is Panel) { if (e.TotalManipulation.Translation.X < 0) { if (e.TotalManipulation.Translation.X > -100 /*|| this.CurrentPageIndex >= this.PageList.Count - 1*/) { this.SlideTitleDoubleAnimation.To = this.CurrentPageIndex * PageWidth / 2 * -1; this.PageChangeAnimation.Begin(); } else { this.ChangePage(1); } } else if (e.TotalManipulation.Translation.X > 0) { if (e.TotalManipulation.Translation.X < 100 ) { this.SlideTitleDoubleAnimation.To = this.CurrentPageIndex * PageWidth / 2 * -1; this.PageChangeAnimation.Begin(); } else { this.ChangePage(-1); } } } } private void ChangePage(int step) { this.CurrentPageIndex += step; this.LoadPages(); this.PanoramaContentTranslate.X += PageWidth * step; this.SlideTitleDoubleAnimation.To = this.CurrentPageIndex * PageWidth / 2 * -1; this.PageChangeAnimation.Begin(); } private void PhoneApplicationPage_ManipulationDelta(object sender, ManipulationDeltaEventArgs e) { if (e.OriginalSource is Panel) { this.PanoramaContentTranslate.X = e.CumulativeManipulation.Translation.X - PageWidth; this.TitleTranslate.X = e.CumulativeManipulation.Translation.X / 2 - (this.CurrentPageIndex * PageWidth / 2); } } }}
這裡首先把三個控制項載入到頁面列表中,通過判斷當前頁面索引來選擇當前頁面,下一個頁面,前一個頁面分別是什麼,並且將它們放到grid的三列中去。至於動畫效果,可以從最後兩個函數看出,確實是使用X軸的位移量來調整的,使用者自己也可以進一步調整效果。
以上就是在windows phone上實現Android gallery control的實現過程,不知道微軟以後會不會對pivot控制項進行改進,這樣就可以直接用而不用那麼費事了。