欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

uwp ListView列表滑动特效

程序员文章站 2022-06-24 09:28:48
在看过一篇文章 WPF自定义控件之列表滑动特效 PowerListBox http://www.cnblogs.com/ShenNan/p/4993374.html#3619585 实现了滑动的特效(就是动画)之后 ,觉得很有趣 也想在 UWP里面实现。最好效果如下 接下来就说说是怎么实现的吧: 一 ......

   在看过一篇文章  

WPF自定义控件之列表滑动特效 PowerListBox  http://www.cnblogs.com/ShenNan/p/4993374.html#3619585 实现了滑动的特效(就是动画)之后 ,觉得很有趣 也想在 UWP里面实现。最好效果如下uwp ListView列表滑动特效

接下来就说说是怎么实现的吧:

一. 添加 Behaviors

  右键项目 选中 “管理 NuGet 程序包”uwp ListView列表滑动特效

然后搜索 Behaviors 添加uwp ListView列表滑动特效

 

 二 . 添加一个新的类

      这个类就叫

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 这两个方法里面修改其动画效果可以变得更加酷点,
第一次写博客。。。。。。。呜呜呜