在看過一篇文章 WPF自定義控制項之列表滑動特效 PowerListBox http://www.cnblogs.com/ShenNan/p/4993374.html#3619585 實現了滑動的特效(就是動畫)之後 ,覺得很有趣 也想在 UWP裡面實現。最好效果如下 接下來就說說是怎麼實現的吧: 一 ...
在看過一篇文章
WPF自定義控制項之列表滑動特效 PowerListBox http://www.cnblogs.com/ShenNan/p/4993374.html#3619585 實現了滑動的特效(就是動畫)之後 ,覺得很有趣 也想在 UWP裡面實現。最好效果如下
接下來就說說是怎麼實現的吧:
一. 添加 Behaviors
右鍵項目 選中 “管理 NuGet 程式包”
然後搜索 Behaviors 添加
二 . 添加一個新的類
這個類就叫
ListViewBehavior
吧 繼承 DependencyObject, 繼承介面 IBehavior 然後顯示繼承這個介面的兩個方法
public class ListViewBehavior : DependencyObject, IBehavior { public DependencyObject AssociatedObject { get; set; } public void Attach(DependencyObject associatedObject) { throw new NotImplementedException(); } public void Detach() { throw new NotImplementedException(); } }
然後在這個類裡面添加一些屬性,等會會用到
public DependencyObject AssociatedObject { get; set; } /// <summary> /// 需要做動畫的列表 /// </summary> public ListView ListView; /// <summary> /// listView 裡面的滾動條 /// </summary> public ScrollViewer scroll;
/// <summary>
/// 容器的佈局方向
/// </summary>
private Orientation _panelOrientation;
/// <summary> /// 當前可視化視圖的第一項 /// </summary> private int firstVisibleIndex; /// <summary> /// 當前可視化視圖的最後一項 /// </summary> private int lastVisibleIndex; /// <summary> /// 上次滾動時可視化視圖的第一項 /// </summary> private int oldFirstVisibleIndex; /// <summary> /// 上次滾動時可視化視圖的最後一項 /// </summary> private int oldLastVisibleIndex; /// <summary> /// 標識,是否已找到第一項 /// </summary> private bool isFindFirst; /// <summary> /// 當前累計已遍歷過的Item高度或寬度的值,用於尋找第一項和最後一項 /// </summary> private double cumulativeNum; public void Attach(DependencyObject associatedObject) { } public void Detach() { }
屬性都準備好了那我們現在開始進入正式的後續吧 , 在 Attach 方法裡面添加代碼
public void Attach(DependencyObject associatedObject) { //獲取 ListView 列表對象 ListView = associatedObject as ListView; if (ListView ==null ) { return; } //傳進來的 對象不為空,我們就對這個對象註冊一個事件 ListView.Loaded += ListView_Loaded; }
ListView_Lodaded 代碼:
/// <summary> /// 列表對象載入完成後的邏輯代碼 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ListView_Loaded(object sender, RoutedEventArgs e) { //查找滾動視圖,並賦值到我們剛剛添加的屬性里 scroll = FindVisualChild<ScrollViewer>(ListView, "ScrollViewer"); //判斷是否為空,不為空的話就添加一個事件 if (scroll == null) { return; } else { //監聽滾動事件 scroll.ViewChanged += Scroll_ViewChanged; }
ItemsPresenter v = FindVisualChild<ItemsPresenter>(ListView, "");
ItemsStackPanel items = FindVisualChild<ItemsStackPanel>(v,"");
_panelOrientation = items.Orientation;
}
/// <summary> /// 獲取模板控制項 /// </summary> /// <typeparam name="T">獲取的類型</typeparam> /// <param name="obj">控制項對象</param> /// <returns></returns> protected T FindVisualChild<T>(DependencyObject obj, string name) where T : DependencyObject { //獲取控制項可視化樹中的子對象數量 int count = VisualTreeHelper.GetChildrenCount(obj); //根據索引遍歷每一個對象 for (int i = 0; i < count; i++) { var child = VisualTreeHelper.GetChild(obj, i); //根據參數判斷是不是我們要找的對象,如果是 就返回,並退出該方法, //如果不是則再遞歸到下一層查找 if (child is T && ((FrameworkElement)child).Name == name) { return (T)child; } else { var child1 = FindVisualChild<T>(child, name); if (child1 != null) { return (T)child1; } } } return null; }
現在已經獲取到了 列表的 ScrollViewer 對象了 就可以執行這個 動畫最核心的部分了,就是通過監聽 滾動事件 來獲取我們需要做動畫的控制項了。
/// <summary> /// 監聽滾動事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Scroll_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) { //每次滾動時都計算當前可視化區域的首尾項 CalculationIndex(); }
再添加兩個欄位
/// <summary> /// 列表子控制項的高度 /// </summary> private double itemsHeight; //平移特效對象 private TranslateTransform tt;
/// <summary> /// 計算可視化區域的第一項和最後一項 /// </summary> private void CalculationIndex() { //賦值舊的第一個可視化視圖 oldFirstVisibleIndex = firstVisibleIndex; //賦值最後一個可視化視圖 oldLastVisibleIndex = lastVisibleIndex; ; //標記第一項是否找到了 isFindFirst = false; //判斷列表的方向 if (_panelOrientation == Orientation.Vertical) { cumulativeNum = 0.0; //遍歷列表的全部可視化視圖,尋找第一項和最後一項 for (int i = 0; i < ListView.Items.Count; i++) { //轉換成 ListViewItem 對象,用於操作 var _item = ListView.ContainerFromIndex(i) as ListViewItem; //這裡有個坑,應為 ListView 支持虛擬化的,所以每次獲取列表的 //子項最多只會有20項左右,所以我們要記錄一下 高度,將所遍歷到的 //可視化視圖的高度累加。 if (_item == null) { cumulativeNum += itemsHeight; } else { itemsHeight = _item.ActualHeight; cumulativeNum += _item.ActualHeight + _item.Margin.Top + _item.Margin.Bottom; } //判斷當前所累加的高度大於我們滾動的距離找到 現在顯示在屏幕上的第一項 if (!isFindFirst && cumulativeNum >= scroll.VerticalOffset) { //記錄第一項的索性 firstVisibleIndex = i; //表明第一項已經找到了 isFindFirst = true; Up(); } //當前所累加的高度 大於 當前移動的距離和 滾動視圖的可見高度,找出最後一項 if (cumulativeNum >= (scroll.VerticalOffset + scroll.ViewportHeight)) { //記錄最後一項的索引 lastVisibleIndex = i; Down(); //已經找到的第一項和最後一項了 跳出迴圈 break; } ; } } }
最後 兩個進行動畫的方法:
/// <summary> /// 滾動條向下,類容向上移動 /// </summary> private void Down() { if ((firstVisibleIndex == oldFirstVisibleIndex && lastVisibleIndex == oldLastVisibleIndex) || oldFirstVisibleIndex == 0 && oldLastVisibleIndex == 0) return; //判斷 當前最後一項 是否大於上次移動的最後一項 if (lastVisibleIndex > oldLastVisibleIndex) { //獲取可視化對象 var _item = ListView.ContainerFromIndex(lastVisibleIndex) as ListViewItem; // tt = new TranslateTransform(); //這裡要判斷一下 當前可視化是否為空,如果你移動得比較快的化,列表的虛擬化會給不到對象來的。 if (_item == null) { return; } _item.RenderTransform = tt; Storyboard board = new Storyboard(); //創建一個 double 動畫 DoubleAnimation animation = new DoubleAnimation() { AutoReverse = false, RepeatBehavior = new RepeatBehavior(1), EnableDependentAnimation = true, To = 0, From = _item.ActualWidth / 2, Duration = TimeSpan.FromSeconds(0.2) }; Storyboard.SetTarget(animation, tt); Storyboard.SetTargetProperty(animation, nameof(TranslateTransform.X)); board.Children.Add(animation); board.Begin(); } } /// <summary> /// 滾動條向上,內容向下 /// </summary> private void Up() { if (firstVisibleIndex < oldFirstVisibleIndex) { var _item = ListView.ContainerFromIndex(firstVisibleIndex) as ListViewItem; tt = new TranslateTransform(); if (_item == null) { return; } _item.RenderTransform = tt; Storyboard board = new Storyboard(); DoubleAnimation animation = new DoubleAnimation() { AutoReverse = false, RepeatBehavior = new RepeatBehavior(1), EnableDependentAnimation = true, To = 0, From = _item.ActualWidth / 2, Duration = TimeSpan.FromSeconds(0.3) }; Storyboard.SetTarget(animation, tt); Storyboard.SetTargetProperty(animation, nameof(TranslateTransform.X)); board.Children.Add(animation); board.Begin(); } }
這個了類已經完成了最後我們在前臺調試一下吧:前臺代碼:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ListView x:Name="listView" > <ListView.ItemContainerStyle> <Style TargetType="ListViewItem" > <Setter Property="HorizontalContentAlignment" Value="Stretch" ></Setter> </Style> </ListView.ItemContainerStyle> <ListView.ItemTemplate> <DataTemplate> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <Rectangle Height="100" Fill="Red" HorizontalAlignment="Stretch" Margin="5" ></Rectangle> <TextBlock Text="{Binding }" FontSize="50" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="20" ></TextBlock> </Grid> </DataTemplate> </ListView.ItemTemplate> <interactivity:Interaction.Behaviors> <local:ListViewBehavior></local:ListViewBehavior> </interactivity:Interaction.Behaviors> </ListView> </Grid>
就這樣我們就完成了一個滾動列表特效了,大家可以在 Down 和 Up 這兩個方法裡面修改其動畫效果可以變得更加酷點,
第一次寫博客。。。。。。。嗚嗚嗚