Windows phone應用開發[18]-下拉刷新

在windows phone 中採用數據列表時爲了保證用戶體驗常遇到加載數據的問題.這個問題廣泛到只要你用到數據列表就要遲早面對這個問題. 不少人會說這個問題已經有解決方案. 其實真正問題並不在於如何實現列表數據動態加載? 而咱們真正目標是如何使這種加載方式達到用戶在操做時良好的用戶體驗. 基於用戶體驗合理性要高於功能自己的實現.css

而這種合理性主要體如今何時須要加載數據? 何時須要數據本地緩存加速本地UI響應? 也是說咱們出發點是基於產品用戶體驗.須要咱們在列表動態加載上加以必定加載策略進行操做行爲上的約束. 用來達到這個目的. 在WP平臺上若是你留意.會發現每當遇到這樣的涉及用戶體驗的問題時.咱們也會一般會看看其餘平臺是作法.不妨也是一種開拓思路. 從Android 和IOS 平臺角度來看. 幾種常見加載數據的方式.html

[方式1]: 自動下拉加載git

04132328_gDBd

這種方式比較常見.一般一個獨立的數據列表中. 在咱們第一次進來時列表加載最新數據.當用戶須要獲取更多或是更舊的數據時.用戶向上滑動.當滾動到UI底部時自動加載更多的數據.特色是 自動加載 避免更多手動的操做. 在網絡通暢狀況 列表操做流暢. 肯定是用戶沒法控制整個數據過程.github

[方式2]:手動下拉加載windows

26221224_A5wJ

方式1採用的用戶下拉到UI底部時自動加載.整個加載過程是用戶是可不控.即 沒法實現用戶只在須要時才手動啓用加載更多或更舊的數據方式.二方式2當用戶滾動UI時能夠選擇是否加載更多數據.用戶可以對整個數據加載過程進行控制.緩存

[方式3]:UI提示加載網絡

201106091817486874

UI提示加載的方式和方式1 、2徹底不一樣.當用戶下拉時加載更多數據時. 會提示彈出一個UI提示層. 對加載進度進行提示. 在數據加載過程當中整個LiveView時沒法進行任何UI操做的.用戶只能等待數據加載完成才能從新操做UI. 這點在不少Pc平臺項目見到不少.ide

[方式4]:下拉刷新測試

2013-09-30_141801

當用戶第一次進來時.列表中獲取到最新數據時. 若是這個列表時隨着時間點會發生數據動態變化時. 用戶就但願在當前頁面就能獲取到最新的數據. 這個時候下拉刷新價值就體現出來了. 而不須要從新進入這個頁面來獲取最新數據.下拉刷新整個操做流程是. 用戶在UI頂部區域下拉整個列表.當用戶手勢離開UI頂部區域時. 列表自動回到頂部.並開始加載最新的數據.更新到ListView中來. 在加載過程當中用戶依然能夠隨意操做當前UI數據.動畫

如上四種方式時Android和Ios中比較常見的數據加載方式. 固然在Ios中還看到相似Pc端數據分頁. 還包含採用一些自定義動畫方式獲取更好的加載體驗. 拋開這些不談.咱們就從這些最基本的加載方式入手.來談談如何在Windows Phone 中數據列表中得到最好的加載體驗.

咱們目前需求時在一個豎屏中有一個ListBox. 但願用戶經過手勢操做方式可以實現操做獲取到最新和更舊的數據.那咱們從如上四種獨立加載方式來看.結合四種方式優缺點.設計一下windows phone 數據列表加載策略 總結以下:

WP ListBox數據加載策略:

A:列表頂部區域支持下拉數據刷新.

B:當用戶滑動操做時滾動列表到最底部時 能夠加載更多舊的數據

C:當用戶滑動操做時從列表底部滾動頂部時 依然支持能夠加載最新的數據.

明確了咱們需求既加載策略. 來嘗試Windows Phone 單個獨立類表嘗試實現如上三個特色.

列表上下滑動加載

從上面三點加載策略來看. 咱們首先來實現. 列表中上下滑動加載數據. 也就是當用戶滾動UI底部時自動加載更舊的數據. 當用戶滾動頂部自動加載最新的數據. 頁面採用加載數據集合就採用經常使用ListBox來演示這個實例.

首先咱們構建一個Project 命名爲DynamicLoadData 在MainPage添加一個默認的ListBox控件:

   1:  <!--ContentPanel - place additional content here-->
   2:  <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   3:      <ListBox x:Name="DynamicLoadData_LB"></ListBox>
   4:  </Grid>

衆所周知.實現Listbox滑動加載數據.不少人都會採用網上一種比較通用的方式.即採用監聽ListBox的MouseMove事件. 當手勢操做列表上下滑動會觸發該事件. 事件觸發後. 經過檢測ListBox.VerticalOffSet當前滾動條位置.再同ListBox.ScrollableHeight滾動條能達到最大位移二者之間的間距差. 來判斷是否到達底部. 加載新的數據.

但你會發現會存在一個問題. 在某些手勢操做時 會忽然發現Listbox已經滾動底部卻沒有執行加載數據的操做. 邏輯雖然正確但操做時卻時靈時而不靈 其實這個問題根本緣由是由於. ListBox.MouseMove事件是隻有的你的手指觸摸到屏幕上而且滑動屏幕纔會觸發.但只要你的手指離開屏幕. 相似在離開前用力下滑. 你會發現listbox已經到了底部卻沒有觸發這個加載事件. 主要由於當前手勢已經離開了屏幕 MouseMove事件就不會被觸發.哪怕ListBox已經滾動到底部了.

一樣咱們也知道ListBox控件自己就內置了ScrollViewer. 一樣的思路咱們經過判斷當前ListBox 的VerticalOffSet 和內置ScrollViewer實際滾動位置進行比較. 來判斷當前滾動是到達頂部或底部.

首先獲取ListBox中ScrollViewer控件:

   1:  public static List<T> GetVisualChildCollection<T>(object parent) where T : UIElement
   2:  {
   3:       List<T> visualCollection = new List<T>();
   4:       GetVisualChildCollection(parent as DependencyObject, visualCollection);
   5:       return visualCollection;
   6:  }
   7:   
   8:  public static void GetVisualChildCollection<T>(DependencyObject parent, List<T> visualCollection) where T : UIElement
   9:  {
  10:       int count = VisualTreeHelper.GetChildrenCount(parent);
  11:       for (int i = 0; i < count; i++)
  12:       {
  13:           DependencyObject child = VisualTreeHelper.GetChild(parent, i);
  14:           if (child is T)
  15:               visualCollection.Add(child as T);
  16:           else if (child != null)
  17:               GetVisualChildCollection(child, visualCollection);
  18:        }
  19:  }

獲取ScrollViewer控件並訂閱其垂直水平ValueChanged事件 實現以下:

   1:  private void RegisterScrollListBoxEvent()
   2:  {
   3:       List<ScrollBar> controlScrollBarList =GetVisualChildCollection<ScrollBar>(this.WholeCityPictureFllow_LB);
   4:       if (controlScrollBarList == null)
   5:            return;
   6:   
   7:       foreach (ScrollBar queryBar in controlScrollBarList)
   8:        {
   9:             if (queryBar.Orientation == System.Windows.Controls.Orientation.Vertical)
  10:                 queryBar.ValueChanged += queryBar_ValueChanged;
  11:        }
  12:  }

在ValueChange事件中判斷其到達最頂部仍是最底部:

   1:  void queryBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
   2:   {
   3:      ScrollBar scrollBar = (ScrollBar)sender;
   4:      object valueObj = scrollBar.GetValue(ScrollBar.ValueProperty);
   5:      object maxObj = scrollBar.GetValue(ScrollBar.MaximumProperty);
   6:      object minObj = scrollBar.GetValue(ScrollBar.MinimumProperty);
   7:   
   8:      if (valueObj != null && maxObj != null)
   9:      {
  10:         double value = (double)valueObj;
  11:         double max = (double)maxObj;
  12:         double min = (double)minObj;
  13:   
  14:          if (value >= max)
  15:          {
  16:            #region Load Old                    
  17:            #endregion
  18:          }
  19:   
  20:         if (value <= min)
  21:          {                  
  22:            #region Load New                        
  23:            #endregion                  
  24:          }
  25:      }
  26:  }

如上經過判斷判斷listbox當前位置和最大滾動區域Max和Min進行對比來判斷當前滾動是否到頂或底部. 方法及其簡單. 值得提到一點是. 咱們到達頂部判斷不須要額外處理. 有時咱們UI元素比較豐富時. 咱們但願保證下滑操做時不但願由於數據加載操做致使UI出現卡頓. 這裏須要有兩個須要額外控制一下. 若是你每次加載數據相似30條排版內容最好多出整個屏幕. 另外咱們須要在下滑時觸發加載時. 要把Max-100或是適當的值. 這樣的作目的是用戶向下滾動不用滾動底部纔開始加載. 而是快到達到底部時就已經開始預加載數據. 在網絡穩定狀況下回操做UI列表更爲流暢.

如上實際加載效果還須要微調才能達到最佳. 已經上下滑動加載.

so 在來重點說說 下拉刷新.

下拉刷新

說道下拉刷新.恐怕在Windows Phone上應用天天用的最頻繁應該就是Sina微博了.和IOS上效果基本一致 效果以下:

pulldown1-550x290

當用戶下拉時 數據列表頂部會顯示 一個向下箭頭和下拉刷新的文字提示. 緊接着提示鬆開自動刷新. 鬆開手勢操做 列表回到頂部.自動開始加載最新數據.並更新數據到ListBox中來, 整個流程如上.首先來分析一下如何實現思路?

因Listbox基本全部咱們須要操做事件和屬性. 基於ListBox咱們重寫一個控件RefreshListBox.首先來看看頂部提示區域如何實現.

其實ListBox的Template實現基於ScrollViewer控件中放置ItemsPresenter. ItemsPresenter是用來在項目控件模板中指定在 ItemsControl 定義的 ItemsPanel 要添加的控件的可視化樹.那麼咱們只須要在一個Grid把提示區域放在ItemsPresenter上面就能夠在下拉是看到整個提示區域. 相似這樣自定義ListBox的模板:

   1:  <ControlTemplate TargetType="local:RefreshBox">
   2:      <ScrollViewer x:Name="ScrollViewer" ...>
   3:          <Grid>
   4:              <Grid Margin="0,-90,0,30" Height="60" VerticalAlignment="Top" x:Name="ReleaseElement">
   5:                  <!-- Tip Area Here -->
   6:              </Grid>
   7:          </Grid>
   8:          <ItemsPresenter/>
   9:      </ScrollViewer>
  10:  </ControlTemplate>

在加載控件時. 咱們須要獲取到自定義控件RefreshListBox內置滑動ScrollViewer並訂閱其MouseMove和ManipulationCompleted事件. 並拿到提示區域ReleaseElement對象的引用. 重寫OnApplyTemplate方法:

   1:  public override void OnApplyTemplate()
   2:  {
   3:      base.OnApplyTemplate();
   4:      if (ElementScrollViewer != null)
   5:      {
   6:          ElementScrollViewer.MouseMove -= viewer_MouseMove;
   7:          ElementScrollViewer.ManipulationCompleted -= viewer_ManipulationCompleted;
   8:      }
   9:   
  10:      ElementScrollViewer = GetTemplateChild("ScrollViewer") as ScrollViewer;
  11:      if (ElementScrollViewer != null)
  12:      {            
  13:          ElementScrollViewer.MouseMove += viewer_MouseMove;
  14:          ElementScrollViewer.ManipulationCompleted += viewer_ManipulationCompleted;
  15:      }
  16:   
  17:      ElementRelease = GetTemplateChild("ReleaseElement") as UIElement;                
  18:      ChangeVisualState(false);
  19:  }

當SrollViewer爲Null訂閱事件操做時.若是在不一樣SDK版本[WP7 Or WP8]執行過程發現訂閱的ManipulationCompleted沒有被觸發. 能夠採用以下方式來強制添加處理事件[在WP7 And WP8 均測試有效] :

   1:  ElementScrollViewer.AddHandler(ScrollViewer.ManipulationCompletedEvent, 
   2:                                 new EventHandler<ManipulationCompletedEventArgs>(viewer_ManipulationCompleted), true);               

在MouseMove事件中.經過判斷ListBox的VerticalOffset 當它等於0;既在頂部.當下拉超過必定距離是開始提示下拉刷新更新RealseElement元素中提示信息:

   1:  private void viewer_MouseMove(object sender, MouseEventArgs e)
   2:  {
   3:      if (VerticalOffset == 0)
   4:      {
   5:          var p = this.TransformToVisual(ElementRelease).Transform(new Point());
   6:          if (p.Y < -VerticalPullToRefreshDistance) //Passed thresdhold : In pulling state area
   7:          {            
   8:              //TODO: Update layout//visual states
   9:          }
  10:          else //Is not pulling
  11:          {
  12:              //TODO: Update layout/visual states
  13:          }
  14:      }
  15:  }

一樣的邏輯.在ManipulationCompleted事件中當用戶完成手勢操做時觸發.若是當前ListBox VerticalOffset 等於0 也就是位於頂部時. 鬆開時手勢時 listBox回到頂部並開始加載最新列表數據並更新列表:

   1:  private void viewer_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
   2:  {
   3:      var p = this.TransformToVisual(ElementRelease).Transform(new Point());
   4:      if (p.Y < -VerticalPullToRefreshDistance)
   5:      {
   6:          //TODO: Raise Polled to refresh event
   7:      }
   8:  }

這樣整個下拉刷新的基本邏輯實現思路已經很明朗.能夠完整重寫整個ListBox實現.

當第一次進來加載數據:

image

下拉是效果:

image (1)

剛鬆開效果:

image (2)

這樣下拉刷新結合ListBox自己上下滑動刷新基本實現咱們如上三個需求.

源碼下載[https://github.com/chenkai/LoadData]

Contact: @chenkaihome

相關文章
相關標籤/搜索