飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现

2024-03-12 15:10

本文主要是介绍飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现
原文: 飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现

在淘宝UWP中,搜索结果列表是用户了解宝贝的重要一环,其中的图片效果对吸引用户点击搜索结果,查看宝贝详情有比较大的影响。为此手机淘宝特意在搜索结果列表上采用了2种表现方式:一种就是普通的列表模式,而另一种则是突出宝贝图片的瀑布流模式。

如果用户搜索某些关键字,如女装类的情况下,淘宝的搜索结果会自动切换到瀑布流模式,让宝贝的美图更加冲击用户的视觉。

但是UWP默认的列表控件并没有这种效果,listview控件中虽然子元素可以不一样大小,但是只能有1列,gridview控件虽然有多列,但每个子元素都只能取相同大小。经过一番搜索,也只有元素由固定大小的不同倍数构成的gridview控件可以使用,但效果并不理想。那么我们有没有办法能得到瀑布流的效果的控件呢?答案是肯定的。我们可能记得在listview中,如果我们要改变列表的扩展方向,需要在xaml中定义listview的itemspanel:

 <ListView><ListView.ItemsPanel><ItemsPanelTemplate><ItemsWrapGrid Orientation="Horizontal"></ItemsWrapGrid></ItemsPanelTemplate></ListView.ItemsPanel></ListView> 
View Code

 

在gridview中设置最大的行数或列数时,我们也要定义ItemsWrapGrid。

这里的ItemsStackPanel,ItemsWrapGrid与我们之前在淘宝UWP--自定义Panel中所提到的panel有什么关系呢?

实际上它们都是继承自panel的FrameworkElement,也就是说它们都可以对内部的子元素进行布局。不管listview还是gridview,他们列表的形式都是由itemsPanel决定的,listview只有1列,可以纵向或者横向扩展,是由它使用的itemsPanel- ItemsStackPanel确定的,gridview可以有多列,可以纵向或者横向扩展,也是由它使用了ItemsWrapGrid作为itemsPanel来决定的。那么如果我们根据淘宝UWP--自定义Panel中提到的方法,自定义一个panel,就可以实现瀑布流中形式的列表了。

整理需求

确定了要实现一个瀑布流的布局panel,我们接下来考虑一下我们的具体有哪些需求呢?在淘宝的搜索结果瀑布流中,只用了2列。但是考虑到我们的淘宝UWP可能运行在PC或者平板等横向屏幕的设备上,如果也用2列的话会有很多图只能在屏幕中显示一部分。所以在PC或者平板等横向屏幕的设备上,我们要让瀑布流的列数增加,也就是说我们的panel需要能自定义列数。

在淘宝的搜索结果瀑布流中,宝贝的搜索结果是纵向扩展的,那么有没有可能有情况需要使用横向扩展的瀑布流呢?想想似乎是比较酷的,那么就为我们的panel加上扩展方向的选择吧。

着手实现

在确定了具体需求之后就可以开始着手实现我们的自定义panel了。

我们的面板的名字就叫WaterfallPanel吧,需要继承panel类型,能定义行数或者列数NumberOfColumnsOrRows,能定义扩展方向WaterfallOrientation,并实现MeasureOverride和ArrangeOverride方法:

public class WaterfallPanel :Panel{public int NumbersOfColumnsOrRows{get { return (int)GetValue(NumbersOfColumnsOrRowsProperty); }set { SetValue(NumbersOfColumnsOrRowsProperty, value); }}// Using a DependencyProperty as the backing store for NumbersOfColumnsOrRows. This enables animation, styling, binding, etc...public static readonly DependencyProperty NumbersOfColumnsOrRowsProperty =DependencyProperty.Register("NumbersOfColumnsOrRows", typeof(int), typeof(WaterfallPanel), new PropertyMetadata(2));public Orientation WaterfallOrientation{get { return (Orientation)GetValue(WaterfallOrientationProperty); }set { SetValue(WaterfallOrientationProperty, value); }}// Using a DependencyProperty as the backing store for WaterfallOrientation. This enables animation, styling, binding, etc...public static readonly DependencyProperty WaterfallOrientationProperty =DependencyProperty.Register("WaterfallOrientation", typeof(Orientation), typeof(WaterfallPanel), new PropertyMetadata(Orientation.Vertical));protected override Size MeasureOverride(Size availableSize){return base.MeasureOverride(availableSize);}protected override Size ArrangeOverride(Size finalSize){return base.ArrangeOverride(finalSize);}}
View Code

 

这就是我们的panel的雏形了,需要注意的是我们的NumberOfColumnsOrRows,和WaterfallOrientation属性需要能在xaml中调用,因此必须写成DependencyProperty的形式。在写的时候可以用先输入propdp,再按tab键,在vs自动生成的模板上进行修改的方法,能方便很多。考虑到用户也可能会不输入行列数或者扩展方向,我们给了它们默认值显示2行或列,纵向扩展。

首先我们来实现MeasureOverride方法。MeasureOverride方法接受一个panel可以占据的空间大小availableSize,再根据这个availableSize给内部的子元素分配可以占据的空间大小。在瀑布流中,以纵向扩展为例,每个元素的最大宽度都是相等的,都是panel宽度的列数分之一。而每个元素的高度则可以自由扩展。因此根据这样的思路我们的MeasureOverride方法的实现应该是:

protected override Size MeasureOverride(Size availableSize){if (NumberOfColumnsOrRows < 1){throw (new ArgumentOutOfRangeException("NumberOfColumnsOrRows", "NumberOfColumnsOrRows must >0"));//太窄

}var LenList = new List<double>();for (int i = 0; i < NumberOfColumnsOrRows; i++){LenList.Add(0);}if (WaterfallOrientation == Orientation.Vertical){double maxWidth = availableSize.Width / NumberOfColumnsOrRows;Size maxSize = new Size(maxWidth, double.PositiveInfinity);foreach (var item in Children){item.Measure(maxSize);var itemHeight = item.DesiredSize.Height;var minLen = LenList[0];int minP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] < minLen){minLen = LenList[i];minP = i;}}LenList[minP] += itemHeight;}var maxLen = LenList[0];int maxP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] > maxLen){maxLen = LenList[i];maxP = i;}}return new Size(availableSize.Width, LenList[maxP]);}else{double maxHeight = availableSize.Height / NumberOfColumnsOrRows;Size maxSize = new Size(double.PositiveInfinity, maxHeight);foreach (var item in Children){item.Measure(maxSize);var itemWidth = item.DesiredSize.Width;var minLen = LenList[0];int minP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] < minLen){minLen = LenList[i];minP = i;}}LenList[minP] += itemWidth;}var maxLen = LenList[0];int maxP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] > maxLen){maxLen = LenList[i];maxP = i;}}return new Size(LenList[maxP], availableSize.Height);}} 
View Code

 

接下来实现我们的ArrangeOverride方法。在ArrangeOverride方法中,会接受一个可以进行布局的空间大小finalSize,在这个空间中将子元素逐个定位在合适的位置。在我们的瀑布流panel中,我们要将子元素定位成瀑布流的效果。那么如何实现瀑布流的效果呢?以纵向的情况为例,瀑布流中每个元素的宽度一致而长度不一,排成一定数量的列,每列长度虽然参差但差距不大,并列排在panel中形成瀑布的样子。我们可以将panel分成若干列,将子元素分配到这些列中按纵向扩展的顺序排布,每次分配时都挑总长最短的列,将新元素分配到这列。这样就能让各个列的长度差距不大,满足瀑布流的效果。按照这个思路,我们实现了ArrangeOverride方法:

protected override Size ArrangeOverride(Size finalSize){if (NumberOfColumnsOrRows < 1){throw (new ArgumentOutOfRangeException("NumberOfColumnsOrRows", "NumberOfColumnsOrRows must >0"));//太窄

}var LenList = new List<double>();var posXorYList = new List<double>();if (WaterfallOrientation == Orientation.Vertical){double maxWidth = finalSize.Width / NumberOfColumnsOrRows;//列的长度和左上角的x值for (int i = 0; i < NumberOfColumnsOrRows; i++){LenList.Add(0);posXorYList.Add(i * maxWidth);}foreach (var item in Children){var itemHeight = item.DesiredSize.Height;var minLen = LenList[0];int minP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] < minLen){minLen = LenList[i];minP = i;}}item.Arrange(new Rect(posXorYList[minP], LenList[minP], item.DesiredSize.Width, item.DesiredSize.Height));LenList[minP] += item.DesiredSize.Height;}var maxLen = LenList[0];int maxP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] > maxLen){maxLen = LenList[i];maxP = i;}}return new Size(finalSize.Width, LenList[maxP]);}else{double maxHeight = finalSize.Height / NumberOfColumnsOrRows;//行的长度和左上角的y值for (int i = 0; i < NumberOfColumnsOrRows; i++){LenList.Add(0);posXorYList.Add(i * maxHeight);}foreach (var item in Children){var itemWidth = item.DesiredSize.Width;var minLen = LenList[0];int minP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] < minLen){minLen = LenList[i];minP = i;}}item.Arrange(new Rect(LenList[minP], posXorYList[minP], item.DesiredSize.Width, item.DesiredSize.Height));LenList[minP] += item.DesiredSize.Width;}var maxLen = LenList[0];int maxP = 0;for (int i = 1; i < NumberOfColumnsOrRows; i++){if (LenList[i] > maxLen){maxLen = LenList[i];maxP = i;}}return new Size(LenList[maxP], finalSize.Height);}}
View Code

 

在MeasureOverride方法和ArrangeOverride方法实现之后,我们的瀑布流panel就可以说初步完成了。实际的运行效果和我们的淘宝UWP版中是基本一致的,只不过在淘宝UWP版的不断迭代中,我们又对一些细节做了优化。另外需要注意的是如果使用横向瀑布流,需要把WaterfallPanel所属的listview或gridview的scrollviewer相关的值进行设置:

ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Disabled"

否则会由于listview或gridview的默认设置是纵向扩展,从而在MeasureOverride方法传入的availableSize的height是无限大,最终导致计算错误而应用崩溃。

这样看来只要掌握了方法和思路,自定义panel也并没有想象中那么困难。小伙伴们也可以尝试创建自己独有的列表控件,如果你有一些奇思妙想的话,也欢迎分享出来。

让我们共同进步,让UWP应用更加完善。

posted on 2017-09-20 13:57 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/7561158.html

这篇关于飞流直下的精彩 -- 淘宝UWP中瀑布流列表的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/801676

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给