++++++++++++++++++++++++++++++++++++++++++
本文系本站原創,歡迎轉載! 轉載請注明出處:
http://blog.csdn.net/mr_raptor/article/details/7358226
++++++++++++++++++++++++++++++++++++++++++
前言
最近一些時間有一些雜事,沒有及時更新Blog,Sorry,現在將下拉刷新更新完。
上一節講了,下拉刷新的基本框架結構,現在我們在上節的基礎上添加內容。
WindowsPhone下拉刷新控件 - PullRefreshListBox(一)
在第一節中,解決了:Control選擇問題和樣式模板,本節主要內容包含:
- 用戶操作及距離計算問題
- 刷新事件處理問題
- 動畫效果
思路:
獲得點擊Y坐標startY,在手指移動時將新的移動坐標currentY與startY對比,如果currentY - startY的值大於了一定的坐標值就設置刷新顯示區,同時設置控件的依賴項屬性,同時還開始動畫效果。
1. 用戶操作及距離計算問題
用戶操作主要是重寫了基類里的三個方法:OnManipulationCompleted,OnManipulationStarted,OnMouseMove。
- OnManipulationCompleted:用於用戶的手勢行為結束時調用。
- OnManipulationStarted:用於用戶的手勢行為開始時調用。
- OnMouseMove:用於用戶的手勢行為持續變化時調用 。
通過OnManipulationStarted(ManipulationStartedEventArgs e)參數ManipulationStartedEventArgs 可以獲得Y坐標點
- protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
- {
- base.OnManipulationStarted(e);
- startY = e.ManipulationOrigin.Y;
- ...
通過OnMouseMove(MouseEventArgs e)參數MouseEventArgs 可以獲得當前相對參照控件的坐標點Y
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
- double currentY = e.GetPosition(this.ScrollViewer).Y;
- ...
在OnManipulationCompleted方法里將下拉顯示關閉掉,同時關閉動畫效果,修改依賴項屬性 PullDownPanel.Visibility = Visibility.Collapsed;
具體代碼如下:
- private double startY, endY;
- private double scrolledOffset;
- private bool isShowing = false;
- protected override void OnManipulationStarted(ManipulationStartedEventArgs e)
- {
- base.OnManipulationStarted(e);
- startY = e.ManipulationOrigin.Y;
- scrolledOffset = this.ScrollViewer.VerticalOffset;
- //Debug.WriteLine("OnManipulationStarted startY = " + startY + " scrolledY = " + scrolledOffset);
- }
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
- double currentY = e.GetPosition(this.ScrollViewer).Y;
- double offset = currentY - startY;
- if (scrolledOffset - offset < -30.0)
- {
- setVisible();
- }
- else
- {
- setInvisible();
- }
- }
- private void setInvisible()
- {
- if (!isShowing)
- return;
- isShowing = false;
- //Debug.WriteLine("stop show");
- ShowAnimation.Stop();
- TransformAnimation.Stop();
- PullDownPanel.Visibility = Visibility.Collapsed;
- }
- private void setVisible()
- {
- if (isShowing)
- return;
- isShowing = true;
- Debug.WriteLine("show");
- PullDownPanel.Visibility = Visibility.Visible;
- ShowAnimation.Begin();
- TransformAnimation.Begin();
- DataRefreshedEventHandler handler = DataRefreshed;
- if (handler != null)
- {
- handler(this, null);
- }
- }
- <p> protected override void OnManipulationCompleted(ManipulationCompletedEventArgs e)
- {
- base.OnManipulationCompleted(e);
- endY = e.ManipulationOrigin.Y;
- //Debug.WriteLine("OnManipulationCompleted endY" + endY);</p><p> setInvisible();
- }</p>
這個時候編譯一個工程會發現,我們的下拉能夠被正常識別到,當Y方向拉動超過30像素,就會有顯示下拉顯示區內容。
但是,這時還沒有動畫效果,我們為它添加上動畫效果,其中下拉顯示區有淡進淡出的效果,然后,有一個轉動的就箭頭,表示正在刷新。
2. 添加動畫效果
動畫效果是發生在下拉時,所以我們要回去Generic.xaml樣式模板里,在Grid.Resources中添加如下代碼:
- <Setter Property="Template">
- <Setter.Value>
- <ControlTemplate TargetType="local:PullRefreshListBox">
- <Grid>
- <Grid.Resources>
- <Storyboard x:Name="showAnimation">
- <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PullDownPanel">
- <EasingDoubleKeyFrame KeyTime="0" Value="0"/>
- <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="1"/>
- </DoubleAnimationUsingKeyFrames>
- </Storyboard>
- <Storyboard x:Name="transformAnimation" RepeatBehavior="10x">
- <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)"
- Storyboard.TargetName="RefreshImage" RepeatBehavior="Forever">
- <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="90"/>
- <EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="180"/>
- <EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="270"/>
- <EasingDoubleKeyFrame KeyTime="0:0:1.6" Value="360"/>
- </DoubleAnimationUsingKeyFrames>
- </Storyboard>
- </Grid.Resources>
其中,showAnimation動畫為在0.8秒內其透明度由0到1從全透明到不透明,從而實現淡進淡出的效果,transformAnimation動畫為小轉輪在0.4秒里順時針重復轉動90度,實現轉動效果。
由於我們要在下拉條件成立時,開啟動畫,因此要在OnApplyTemplate方法中得到兩個動畫的引用:
- public override void OnApplyTemplate()
- {
- // the Animation of the Image and RefreshText
- TransformAnimation = this.GetTemplateChild(TransformAnimationName) as Storyboard;
- ShowAnimation = this.GetTemplateChild(ShowAnimationName) as Storyboard;
這樣,在setInvisible和setVisible方法里分別開啟和關閉動畫效果就可以了。
編譯當前代碼,再執行,會發現,下拉時,動畫效果已經實現,但是下拉列表基本的點擊事件不能使用了。
這是因為我們的下拉控件繼承自ItemsControl控件,對於ItemsControl控件里的列表內容,要自己添加其列表項的點擊行為。
3. 添加Item點擊事件
當用戶在列表中選擇一個item時,我們希望將所有的Item都放到一個棧里,然后從Tap事件里找到被Tap的對象,然后從棧里遍歷該對象,如果找到,就將SelectedItem與SelectedIndex設置為其對應的值,在其屬性改變事件里響應點擊行為。
在OnApplyTemplate方法里注冊Tap 事件:
- public override void OnApplyTemplate()
- {
- // add the Event handler for the Tap action on ScrollViewer
- this.ScrollViewer.Tap += new EventHandler<GestureEventArgs>(ScrollViewer_Tap);
Tap事件處理:
- void ScrollViewer_Tap(object sender, GestureEventArgs e)
- {
- DependencyObject dpendObj = e.OriginalSource as DependencyObject;
- object obj;
- // get StackPanel of the listbox
- StackPanel itemsStack = VisualTreeHelper.GetChild(_ItemsContainer, 0) as StackPanel;
- if ((obj = RetriveElementOfTree<ItemsPresenter>(dpendObj)) != null)
- {
- //ItemsPresenter itemsGroup = obj as ItemsPresenter;
- if (childObjStack != null)
- {
- // pop StackPanel
- childObjStack.Pop();
- // the event Element
- obj = childObjStack.Pop();
- }
- for(int i = 0; i < itemsStack.Children.Count; i++)
- {
- if (object.Equals(obj, itemsStack.Children[i]))
- {
- // found the Taped obj
- saveSelectedItem = itemsStack.Children[i];
- SelectedIndex = i;
- SelectedItem = saveSelectedItem;
- OnSelectedItem(SelectedItem);
- break;
- }
- }
- Debug.WriteLine("ScrollViewer_Tap::"+VisualTreeHelper.GetChildrenCount(itemsStack));
- }
- }
VisualTreeHelper.GetChild是個很牛B的東西,它可以從指定的控件里獲得對應的子控件引用。
VisualTreeHelper.GetChildVisualTreeHelper.GetChild這里我們從裝有所有Items的控件父控件_ItemsContainer里得到依次得到每一個子控件放到childObjStack棧里,如下圖所示。

具體的入棧如下代碼所示:
- private Stack<DependencyObject> childObjStack;
- private object RetriveElementOfTree<T>(DependencyObject tree)
- {
- if (childObjStack == null)
- childObjStack = new Stack<DependencyObject>();
- else
- childObjStack.Clear();
- while (tree != null)
- {
- if (tree is T)
- {
- return tree;
- }
- childObjStack.Push(tree);
- tree = VisualTreeHelper.GetParent(tree);
- }
- return null;
- }
在獲得了被點擊的Item之后,主動回調SelectionChangedEvent事件,響應用戶點擊Item操作。
- private void OnSelectedItem(object obj)
- {
- SelectionChangedEventHandler handler = SelectionChanged;
- if (handler != null)
- {
- _selectionList[0] = obj;
- // Call back user define Event
- handler(this, new SelectionChangedEventArgs(EmptyList, _selectionList));
- }
- }
現在來看我們的控件看似沒有問題了,但是不要忘記了本控件的最初目的,即,用戶下拉列表時,要去刷新其數據源,所以在用戶下拉條件成立時,調用用戶的回調方法。
3. 添加下拉刷新處理事件
首先添加一個代理和事件
- // user pull down the list delegate event
- public delegate void DataRefreshedEventHandler(object sender, RoutedEventArgs e);
- // pull down refresh the ItemSource Event
- public event DataRefreshedEventHandler DataRefreshed;
然后在setVisible()里回調用戶注冊的事件方法:
- private void setVisible()
- {
- ...
- DataRefreshedEventHandler handler = DataRefreshed;
- if (handler != null)
- {
- handler(this, null);
- }
- }
至此,自定義下拉控件完篇,有問題請跟評論,作者第一時間看到回復。
++++++++++++++++++++++++++++++++++++++++++
本文系本站原創,歡迎轉載! 轉載請注明出處:
http://blog.csdn.net/mr_raptor/article/details/7358226
++++++++++++++++++++++++++++++++++++++++++
