Windows Phone 实现类似“微博”下拉刷新效果
请看新版本代码:
http://www.cnblogs.com/jinzhao/archive/2012/02/27/2370424.html
在我的O7上跑了下张善友写的博客园阅读APP,有很多体验上的想法,一时技痒想实现下拉刷新的效果。
如果要实现下拉的效果就需要三个状态,见下图:
第一种状态(提示下拉可以更新博客园新闻):
第二种状态(提示下拉的幅度已够可以释放进入更新操作)
第三种状态(提示正在更新,更新完毕回到第一个状态)
上面这个效果为了方便独立到一个UserControl中,代码如下:
XAML:
<UserControl x:Class="sdkRSSReaderCS.LoadingBox"
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">
<StackPanel Orientation="Horizontal">
<Grid Margin="8,0,0,0" Width="50" Height="50">
<Image x:Name="PulldownArrow" Width="50" Height="50" Source="/sdkRSSReaderCS;component/images/pulldown_arrow.png">
<Image.RenderTransform>
<RotateTransform x:Name="FlipTransform" CenterX="25" CenterY="25" Angle="180"/>
</Image.RenderTransform>
<Image.Resources>
<Storyboard x:Name="FlipDown">
<DoubleAnimation Storyboard.TargetName="FlipTransform"
Storyboard.TargetProperty="Angle"
From="0" To="180" Duration="0:0:0.2"/>
</Storyboard>
<Storyboard x:Name="FlipUp">
<DoubleAnimation Storyboard.TargetName="FlipTransform"
Storyboard.TargetProperty="Angle"
From="180" To="0" Duration="0:0:0.2"/>
</Storyboard>
</Image.Resources>
</Image>
<Image x:Name="SpinningBall" Source="/sdkRSSReaderCS;component/images/spinning_ball.png" Width="36" Height="36">
<Image.RenderTransform>
<RotateTransform x:Name="SpinTransform" CenterX="18" CenterY="18"/>
</Image.RenderTransform>
<Image.Resources>
<Storyboard x:Name="Spin">
<DoubleAnimation Storyboard.TargetName="SpinTransform"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:1"
RepeatBehavior="Forever"/>
</Storyboard>
</Image.Resources>
</Image>
</Grid>
<TextBlock x:Name="TextBlock" Margin="10,0,0,0"
VerticalAlignment="Center" FontSize="26"
Style="{StaticResource PhoneTextExtraLargeStyle}"
Foreground="{StaticResource PhoneAccentBrush}" />
</StackPanel>
</Grid>
</UserControl>
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">
<StackPanel Orientation="Horizontal">
<Grid Margin="8,0,0,0" Width="50" Height="50">
<Image x:Name="PulldownArrow" Width="50" Height="50" Source="/sdkRSSReaderCS;component/images/pulldown_arrow.png">
<Image.RenderTransform>
<RotateTransform x:Name="FlipTransform" CenterX="25" CenterY="25" Angle="180"/>
</Image.RenderTransform>
<Image.Resources>
<Storyboard x:Name="FlipDown">
<DoubleAnimation Storyboard.TargetName="FlipTransform"
Storyboard.TargetProperty="Angle"
From="0" To="180" Duration="0:0:0.2"/>
</Storyboard>
<Storyboard x:Name="FlipUp">
<DoubleAnimation Storyboard.TargetName="FlipTransform"
Storyboard.TargetProperty="Angle"
From="180" To="0" Duration="0:0:0.2"/>
</Storyboard>
</Image.Resources>
</Image>
<Image x:Name="SpinningBall" Source="/sdkRSSReaderCS;component/images/spinning_ball.png" Width="36" Height="36">
<Image.RenderTransform>
<RotateTransform x:Name="SpinTransform" CenterX="18" CenterY="18"/>
</Image.RenderTransform>
<Image.Resources>
<Storyboard x:Name="Spin">
<DoubleAnimation Storyboard.TargetName="SpinTransform"
Storyboard.TargetProperty="Angle"
From="0" To="360" Duration="0:0:1"
RepeatBehavior="Forever"/>
</Storyboard>
</Image.Resources>
</Image>
</Grid>
<TextBlock x:Name="TextBlock" Margin="10,0,0,0"
VerticalAlignment="Center" FontSize="26"
Style="{StaticResource PhoneTextExtraLargeStyle}"
Foreground="{StaticResource PhoneAccentBrush}" />
</StackPanel>
</Grid>
</UserControl>
Cs代码:
public partial class LoadingBox : UserControl
{
private string arrow;
public static readonly DependencyProperty BusyProperty = DependencyProperty.Register("Busy", typeof(bool), typeof(LoadingBox), new PropertyMetadata(false));
public LoadingBox()
{
InitializeComponent();
}
// Properties
public string Arrow
{
get
{
return this.arrow;
}
set
{
if (this.arrow == null)
{
if (value == "Invisible")
this.PulldownArrow.Visibility = Visibility.Collapsed;
else if (value == "Up")
{
this.FlipTransform.Angle = 0.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
else if (value == "Down")
{
this.FlipTransform.Angle = 180.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
}
else if (value == "Invisible")
this.PulldownArrow.Visibility = Visibility.Collapsed;
else if (value == "Up")
{
if (this.arrow == "Invisible")
{
this.FlipTransform.Angle = 0.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
else if (this.arrow == "Down") this.FlipUp.Begin();
}
else if (value == "Down")
{
if (this.arrow == "Invisible")
{
this.FlipTransform.Angle = 180.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
else if (this.arrow == "Up") this.FlipDown.Begin();
}
this.arrow = value;
}
}
public bool Busy
{
get
{
return (bool)base.GetValue(BusyProperty);
}
set
{
base.SetValue(BusyProperty, value);
if (value)
{
this.Arrow = "Invisible";
this.SpinningBall.Visibility = Visibility.Visible;
this.Spin.Begin();
}
else
{
this.SpinningBall.Visibility = Visibility.Collapsed;
this.Spin.Stop();
}
}
}
public string Text
{
get
{
return this.TextBlock.Text;
}
set
{
this.TextBlock.Text = value;
}
}
}
{
private string arrow;
public static readonly DependencyProperty BusyProperty = DependencyProperty.Register("Busy", typeof(bool), typeof(LoadingBox), new PropertyMetadata(false));
public LoadingBox()
{
InitializeComponent();
}
// Properties
public string Arrow
{
get
{
return this.arrow;
}
set
{
if (this.arrow == null)
{
if (value == "Invisible")
this.PulldownArrow.Visibility = Visibility.Collapsed;
else if (value == "Up")
{
this.FlipTransform.Angle = 0.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
else if (value == "Down")
{
this.FlipTransform.Angle = 180.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
}
else if (value == "Invisible")
this.PulldownArrow.Visibility = Visibility.Collapsed;
else if (value == "Up")
{
if (this.arrow == "Invisible")
{
this.FlipTransform.Angle = 0.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
else if (this.arrow == "Down") this.FlipUp.Begin();
}
else if (value == "Down")
{
if (this.arrow == "Invisible")
{
this.FlipTransform.Angle = 180.0;
this.PulldownArrow.Visibility = Visibility.Visible;
}
else if (this.arrow == "Up") this.FlipDown.Begin();
}
this.arrow = value;
}
}
public bool Busy
{
get
{
return (bool)base.GetValue(BusyProperty);
}
set
{
base.SetValue(BusyProperty, value);
if (value)
{
this.Arrow = "Invisible";
this.SpinningBall.Visibility = Visibility.Visible;
this.Spin.Begin();
}
else
{
this.SpinningBall.Visibility = Visibility.Collapsed;
this.Spin.Stop();
}
}
}
public string Text
{
get
{
return this.TextBlock.Text;
}
set
{
this.TextBlock.Text = value;
}
}
}
下面是在引用的页面中的计算代码(里面的偏移量需要再具体引用的地方修改来适应相应的窗体和动作幅度):
#region ScrollView
private void NewsScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
this.newsScrollViewer = sender as ScrollViewer;
}
private void ScrollViewer_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
this.actualOffset = this.startOffset + this.startPoint - e.ManipulationOrigin.Y;
if (this.actualOffset < -100 && !headLoadingBox.Busy)
{
Update();
}
if (this.actualOffset + 250 > this.newsScrollViewer.ScrollableHeight && !tailLoadingBox.Busy)
{
NextPage();
}
}
private void ScrollViewer_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
this.startOffset = this.newsScrollViewer.VerticalOffset;
this.startPoint = e.ManipulationOrigin.Y;
}
private void ScrollViewer_MouseEnter(object sender, MouseEventArgs e)
{
//this.startRelativePoint = e.GetPosition(this.TopPlaceholder).Y;
}
private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
{
this.actualOffset = this.startOffset + this.startRelativePoint - e.GetPosition(this.TopPlaceholder).Y;
if (actualOffset < -100)
{
this.headLoadingBox.Arrow = "Up";
this.headLoadingBox.Text = "松开就可以更新!";
}
if (this.actualOffset + 250 > this.newsScrollViewer.ScrollableHeight && !tailLoadingBox.Busy)
{
this.tailLoadingBox.Arrow = "Down";
this.tailLoadingBox.Text = "松开就可以更新!";
}
}
private void NewsScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
this.newsScrollViewer = sender as ScrollViewer;
}
private void ScrollViewer_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
this.actualOffset = this.startOffset + this.startPoint - e.ManipulationOrigin.Y;
if (this.actualOffset < -100 && !headLoadingBox.Busy)
{
Update();
}
if (this.actualOffset + 250 > this.newsScrollViewer.ScrollableHeight && !tailLoadingBox.Busy)
{
NextPage();
}
}
private void ScrollViewer_ManipulationStarted(object sender, ManipulationStartedEventArgs e)
{
this.startOffset = this.newsScrollViewer.VerticalOffset;
this.startPoint = e.ManipulationOrigin.Y;
}
private void ScrollViewer_MouseEnter(object sender, MouseEventArgs e)
{
//this.startRelativePoint = e.GetPosition(this.TopPlaceholder).Y;
}
private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
{
this.actualOffset = this.startOffset + this.startRelativePoint - e.GetPosition(this.TopPlaceholder).Y;
if (actualOffset < -100)
{
this.headLoadingBox.Arrow = "Up";
this.headLoadingBox.Text = "松开就可以更新!";
}
if (this.actualOffset + 250 > this.newsScrollViewer.ScrollableHeight && !tailLoadingBox.Busy)
{
this.tailLoadingBox.Arrow = "Down";
this.tailLoadingBox.Text = "松开就可以更新!";
}
}
有不明白的问题请留言,需要代码的请留邮箱。