zoukankan      html  css  js  c++  java
  • windowsphone 瀑布流&ui虚拟化

    瀑布流已经有点年代了吧,不过wp上还真是挺少资料的。今天抽空把自己之前搞过的东西写出来,避免大家重复劳动。

    一、简单的瀑布流排版加入ui虚拟化。

    最近看了 段博琼  ui虚拟化的一篇博文,链接:http://www.cnblogs.com/hebeiDGL/p/3410575.html

    觉得还不错,于是下载了他的demo稍微改了一下瀑布流效果。

    demo截图如下:

          

    主要改动:

    1:自定义WaterFallPanel继承Panel,用于实现瀑布流排版,并保持容器children距离顶部高度的信息:

    public class WaterFallPanel : Panel
        {
            public WaterFallPanel()
            {
                /**默认2列**/
                ColumnCount = 2;
                ColumnHeight = new double[ColumnCount];
                childwidth = 480 / ColumnCount;
            }
    
            double childwidth;
            static double[] ColumnHeight;
    
            /// <summary>
            /// 列数
            /// </summary>
            public int ColumnCount
            {
                get { return (int)this.GetValue(ColumnCountProperty); }
                set { this.SetValue(ColumnCountProperty, value); }
            }
    
    
            public static DependencyProperty ColumnCountProperty = DependencyProperty.Register("WaterFallPanel", typeof(int), typeof(WaterFallPanel), new PropertyMetadata(new PropertyChangedCallback((o, e) =>
            {
                ColumnHeight = new double[(int)e.NewValue];
                if (o == null || e.NewValue == e.OldValue)
                    return;
                o.SetValue(ColumnCountProperty, e.NewValue);
            })));
    
    
            protected override Size MeasureOverride(Size availableSize)
            {
    
                Size resultSize = new Size(0, 0);
                //List<DependencyObject> Children = this.GetVisualChildren().ToList();
                for (int i = 0; i < Children.Count; i++)
                {
                    Children[i].Measure(availableSize);
                }
                #region 测量值
                for (int i = 0; i < ColumnHeight.Count(); i++)
                {
                    ColumnHeight[i] = 0;
                }
                heightlist.Clear();
                for (int i = 0; i < Children.Count; i++)
                {
                    double miniheight = ColumnHeight.Min();
                    int h = 0;
                    for (; h < ColumnHeight.Length; h++)
                    {
                        if (ColumnHeight[h] == miniheight)
                        {
                            break;
                        }
                    }
                    ColumnHeight[h] += Children[i].DesiredSize.Height;
    
                    heightlist.Add(ColumnHeight.Min());
                }
                #endregion
    
                resultSize.Height = ColumnHeight.Max();
                if (Children.Count == 0)
                {
                    resultSize.Height = 0;
                    resultSize.Width = 480;
                }
                else
                {
                    resultSize.Width = (Children.Count + 1) * Children[0].DesiredSize.Width;
                }
    
                return resultSize;
            }
    
            protected override Size ArrangeOverride(Size finalSize)
            {
                for (int i = 0; i < ColumnHeight.Count(); i++)
                {
                    ColumnHeight[i] = 0;
                }
    
                #region 排列值
                heightlist.Clear();
                for (int i = 0; i < Children.Count; i++)
                {
                    double miniheight = ColumnHeight.Min();
                    int h = 0;
                    for (; h < ColumnHeight.Length; h++)
                    {
                        if (ColumnHeight[h] == miniheight)
                        {
                            break;
                        }
                    }
                    Children[i].Arrange(new Rect(new Point(Children[i].DesiredSize.Width * h, ColumnHeight[h]), Children[i].DesiredSize));
                    ColumnHeight[h] += Children[i].DesiredSize.Height;
                    if (h == 0)
                    {
                        if (Children[i].DesiredSize.Width > childwidth)
                        {
                            ColumnHeight[h + 1] += Children[i].DesiredSize.Height;
                        }
                    }
                    heightlist.Add(ColumnHeight.Min());
                }
                if (Children.Count < ColumnCount)
                    finalSize.Width = childwidth * (Children.Count + 1);
                else
                    finalSize.Width = childwidth * (ColumnCount + 1);
    
    
                #endregion
    
    
                return finalSize;
            }
            private List<double> heightlist = new List<double>();
            public double GetItemHeight(int item)
            {
                if (item >= 0 && item < heightlist.Count)
                {
                    return heightlist[item];
                }
                else
                {
                    return 0;
                }
            }
        }
    View Code

    2:MainPage.xaml中修改itemspanl

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <LocalControl:WaterFallPanel x:Name="test" Loaded="test_Loaded"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>    

    3:MainPage.cs中修改虚拟化的计算方式:

     void Visualizition()
            {
                // 自定义一个“字典”,用来保存每项的 Y坐标 和它“本身”的引用
                Dictionary<double, StackPanel> dic = new Dictionary<double, StackPanel>();
    
                double height_sum = 0;
                for (int i = 0; i < listbox.Items.Count; i++)
                {
                    // 因为 ListBox 是通过数据绑定生成的列表,所以需要通过 ItemContainerGenerator 获得
                    // 每一项的 StackPanel 这个父控件
                    FrameworkElement fe = listbox.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
                    StackPanel sp = FindFirstElementInVisualTree<StackPanel>(fe);
                    
                    dic.Add(height_sum, sp);
    
                    // 累加 Y高度
                    if (height_sum <= waterpanel.GetItemHeight(i))
                    {
                        height_sum = waterpanel.GetItemHeight(i)+1;
                    }
                    else
                    {
                        height_sum = waterpanel.GetItemHeight(i);
                    }
                    
                    // 设置它的高度为自己的实际高度
                    sp.Height = sp.ActualHeight; 
                }
    
                // 每0.5秒钟,循环检查一次列表中,哪些项在屏幕内,如果在屏幕内,则显示,如果
                // 在屏幕外,则隐藏
                Observable.Interval(TimeSpan.FromSeconds(.5)).ObserveOnDispatcher().Subscribe((_) =>
                    {
                        foreach (var keyValue in dic)
                        {
                            if (((scrollViewer.VerticalOffset - 700) > keyValue.Key || keyValue.Key > (scrollViewer.VerticalOffset + 900)))
                            {
                                keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Collapsed;
                                keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Collapsed;
                            }
                            else
                            {
                                keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Visible;
                                keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Visible;
                            }
                        }
                    });
            }
    View Code

      其中WaterFallPanel的获取用了比较奇葩的一个方式。

    这样做的好处是代码简单明了,适合新手了解ui虚拟化。不好的地方则是每次想实现瀑布流排版,都得手动写入虚拟化计算代码。代码耦合过高。

    二、通过自定义listbox实现瀑布流&虚拟化。降低代码耦合度。

    demo截图:

    1、自定义PowerListBox继承listbox

     public class PowerListBox:ListBox
        {
            // 自定义一个“字典”,用来保存每项的 Y坐标 和它“本身”的引用
            Dictionary<double, Border> dic = new Dictionary<double, Border>();
    
            public PowerListBox()
            {
                DefaultStyleKey = typeof(PowerListBox);
                Loaded += PowerListBox_Loaded;
                Unloaded += PowerListBox_Unloaded;
            }
            private ScrollViewer _scrollView;
            private bool _isScrolling;
            void PowerListBox_Loaded(object sender, System.Windows.RoutedEventArgs e)
            {
                if (DesignerProperties.GetIsInDesignMode(this))
                    return;
                ApplyTemplate();
                _scrollView.ScrollToVerticalOffset(_scrollView.VerticalOffset);
    
                var border = _scrollView.Descendants<Border>().FirstOrDefault();
                Debug.Assert(border != null);
                var vsGroup = VisualStateManager.GetVisualStateGroups(border).OfType<VisualStateGroup>().FirstOrDefault(s => s.Name == "ScrollStates");
                Debug.Assert(vsGroup != null);
                vsGroup.CurrentStateChanging += vsGroup_CurrentStateChanging;
    
    
    
                if (ItemsRootPanel != null && ItemsRootPanel.Children != null)
                {
                    double height_sum=0;
                    dic.Clear();
                    for (int i = 0; i < ItemsRootPanel.Children.Count; i++)
                    {
                        // 因为 ListBox 是通过数据绑定生成的列表,所以需要通过 ItemContainerGenerator 获得
                        // 每一项的 StackPanel 这个父控件
                        Border fe = VisualTreeHelper.GetChild(ItemsRootPanel.Children[i], 0) as Border;
                        dic.Add(height_sum, fe);
    
                        // 累加 Y高度
                        //var height = .GetItemHeight(i);
                        if (height_sum <= (ItemsRootPanel as WaterFallPanel).GetItemHeight(i))
                        {
                            height_sum = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i) + 1;
                        }
                        else
                        {
                            height_sum = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i);
                        }
    
                        // 设置它的高度为自己的实际高度
                        fe.Height = fe.ActualHeight;
                    }
    
                }
    
            }
            void PowerListBox_Unloaded(object sender, System.Windows.RoutedEventArgs e)
            {
                var border = _scrollView.Descendants<Border>().FirstOrDefault();
                Debug.Assert(border != null);
                var vsGroup = VisualStateManager.GetVisualStateGroups(border).OfType<VisualStateGroup>().FirstOrDefault(s => s.Name == "ScrollStates");
                Debug.Assert(vsGroup != null);
                vsGroup.CurrentStateChanging -= vsGroup_CurrentStateChanging;
            }
    
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                _scrollView = GetTemplateChild("ScrollViewer") as ScrollViewer;
                Debug.Assert(_scrollView != null);
            }
    
            void vsGroup_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)
            {
                if (e.NewState.Name == "NotScrolling")
                {
                    _isScrolling = false;
                    CompositionTarget.Rendering -= CompositionTarget_Rendering;
                }
                else
                {
                    _isScrolling = true;
                    CompositionTarget.Rendering += CompositionTarget_Rendering;
                }
            }
            void CompositionTarget_Rendering(object sender, EventArgs e)
            {
                RefreshDisplayArea();
            }
    
            void RefreshDisplayArea()
            {
                foreach (var keyValue in dic)
                {
                    if (((_scrollView.VerticalOffset - 20) > keyValue.Key || keyValue.Key > (_scrollView.VerticalOffset + 600)))
                    {
                        keyValue.Value.Child.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        keyValue.Value.Child.Visibility = Visibility.Visible;
                    }
                }
                //for (int i = 0; i < ItemsRootPanel.Children.Count; i++)
                //{
                //    var item = GetItem(i);
                //    var height = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i) ;
                //    if (((_scrollView.VerticalOffset - 20) > height || height > (_scrollView.VerticalOffset + 607)))
                //    {
                //        FrameworkElement fe = VisualTreeHelper.GetChild(item, 0) as FrameworkElement;
                //        fe.Visibility = Visibility.Collapsed;
                //        //keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Collapsed;
                //        //keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Collapsed;
                //    }
                //    else
                //    {
                //        FrameworkElement fe = VisualTreeHelper.GetChild(item, 0) as FrameworkElement;
                //        fe.Visibility = Visibility.Visible;
    
                //        //keyValue.Value.Children[0].Visibility = System.Windows.Visibility.Visible;
                //        //keyValue.Value.Children[1].Visibility = System.Windows.Visibility.Visible;
                //    }
                //}
    
            }
    
            private ListBoxItem GetItem(int index)
            {
                if (index < 0 || index >= Items.Count)
                    return null;
                if (ItemsSource != null)
                {
                    var isVirtualizing = VirtualizingStackPanel.GetIsVirtualizing(this);
                    if (!isVirtualizing)
                    {
                        Debug.Assert(ItemsRootPanel != null);
                        return ItemsRootPanel.Children[index] as ListBoxItem;
                    }
                    Debug.Assert(ItemContainerGenerator != null);
                    return ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
                }
                return Items[index] as ListBoxItem;
            }
           
            #region FirstVisibleIndex
            public static readonly DependencyProperty FirstVisibleIndexProperty =
                DependencyProperty.Register("FirstVisibleIndex", typeof(int), typeof(PowerListBox), new PropertyMetadata(0));
    
            public int FirstVisibleIndex
            {
                get { return (int)GetValue(FirstVisibleIndexProperty); }
                private set { SetValue(FirstVisibleIndexProperty, value); }
            }
            #endregion
    
            #region LastVisibleIndex
            public static readonly DependencyProperty LastVisibleIndexProperty =
                DependencyProperty.Register("LastVisibleIndex", typeof(int), typeof(PowerListBox), new PropertyMetadata(0));
    
            public int LastVisibleIndex
            {
                get { return (int)GetValue(LastVisibleIndexProperty); }
                private set { SetValue(LastVisibleIndexProperty, value); }
            }
            #endregion
            private Panel _itemsRootPanel;
            private Panel ItemsRootPanel
            {
                get
                {
                    if (_itemsRootPanel == null && ItemsPresenter != null)
                    {
                        _itemsRootPanel = ItemsPresenter.ChildrenEx().FirstOrDefault() as Panel;
                    }
                    return _itemsRootPanel;
                }
            }
    
            private ItemsPresenter _itemsPresenter;
            private ItemsPresenter ItemsPresenter
            {
                get { return _itemsPresenter ?? (_itemsPresenter = _scrollView.Descendants<ItemsPresenter>().FirstOrDefault()); }
            }
    
        }
    View Code

    其他见demo。

    备注:

    1、虚拟化过程中:注意容器的高度为自己的实际高度

    // 设置它的高度为自己的实际高度
    // 很重要,如果不为父容器指定固定高度,当子元素隐藏后,父容器高度变为0px sp.Height = sp.ActualHeight;

    2、计算虚拟化区域有很多种方法,demo中只是简单粗暴的计算,有很多可以进行优化。

    3、瀑布流排版只是见当实现两行的,需要多行的话请自行改动,详见附加链接,当然林政的瀑布流排版有点问题,改改更健康。

    4、demo2中 Dictionary<double, Border> dic的表只是在加载过程中进行一次初始化,在listbox动态加载过程中并没有修改,为此listbox可将虚拟化计算改为

     void RefreshDisplayArea()
            {
                for (int i = 0; i < ItemsRootPanel.Children.Count; i++)
                {
                    var item = GetItem(i);
                    Border fe = VisualTreeHelper.GetChild(item, 0) as Border;
                    fe.Height = fe.ActualHeight;
    
                    var height = (ItemsRootPanel as WaterFallPanel).GetItemHeight(i);
                    if (((_scrollView.VerticalOffset - fe.Height ) > height || height > (_scrollView.VerticalOffset + 820)))
                    {
                        fe.Child.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        fe.Child.Visibility = Visibility.Visible;
                    }
                }
    
            }
    View Code

        其他代码敬请发挥。

    demo链接:

    1:http://files.cnblogs.com/fatlin/VirtualizationListBoxDemo.rar

    2:http://files.cnblogs.com/fatlin/TextListbox.rar

    其他相关链接:

    瀑布流:http://www.cnblogs.com/Smallcode/archive/2012/10/19/2730810.html

  • 相关阅读:
    【C&数据结构】---关于链表结构的前序插入和后序插入
    【LC_Overview1_5】---学会总结回顾
    【LC_Lesson5】---求最长的公共前缀
    xorm -sum 系列方法实例
    xorm -Alias,Asc,Desc方法实例
    xorm -Find方法实例
    xorm -Exist方法实例
    xorm -Get方法实例
    xorm-创建时间created
    xorm插入数据实例
  • 原文地址:https://www.cnblogs.com/fatlin/p/3612528.html
Copyright © 2011-2022 走看看