开发过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控件进行改进,这样就可以直接用而不用那么费事了。