(UWP开发)更为合理的一种ListView下拉刷新(PullToRefresh)实现方法
最近在做的一个项目需要用到下拉刷新,但是参考了现在网络上比较普遍的方法,觉得都不太好,因为要在外部套上一个SrollViewer,容易出现滚动错误。于是刚开始的时候就把思路定到了ListView内部的ScrollViewer上。
最初的想法是在ScrollViewer的Manipulation相关事件上下手,确实做好了,效果也不错,如图:

当时得意满满的看着自己的作品,心里是说不出的激动啊,结果放在手机上想试试触屏设备的效果,结果发现好坑爹:在触屏设备上,手指在ListView的上下滑动默认是移动其滚动条,但是改变了ManipulationModes之后,手指在屏幕上滑动就不能够使ListView滚动了(一脸懵逼)。无奈啊,只能放弃这个方法了。
之后又在想新的途径来实现同样的效果,但是发现都没有很好的办法,于是我的思路又绕回了起点:还是像网络上的方法一样,先把刷新的Header事先隐藏起来,当ScrollViewer滚动出Header之后开始刷新。只不过这个ScrollViewer不是外部嵌套的,是在ListView内部的。实现后的效果如下:

好吧,下面言归正传,讲讲详细的实现方法:
1.首先是在Xmal中,我们改一下ListView的Template。我在ScrollViewer最外层放了一个Grid,定义了两行。第一行放的就是Header,也就是刷新的时候会一直转的那个区域。第二行是ItemPresenter,显示ListView的内容。其中值得注意的是,第一行的高度是30。
代码如下:
<ControlTemplate TargetType="ListView">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<ScrollViewer x:Name="ScrollViewer" AutomationProperties.AccessibilityView="Raw" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}" IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" TabNavigation="{TemplateBinding TabNavigation}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid x:Name="refresh_ring" Grid.Row="" HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Height="" Width="" Fill="{Binding Source={StaticResource APPTheme},Path=APP_Color_Brush}" Opacity="0.7" StrokeThickness=""></Ellipse>
<ProgressRing IsActive="True" Foreground="{Binding Source={StaticResource APPTheme},Path=Foreground_Color_Brush}"></ProgressRing>
</Grid>
<ItemsPresenter Grid.Row="" FooterTransitions="{TemplateBinding FooterTransitions}" FooterTemplate="{TemplateBinding FooterTemplate}" Footer="{TemplateBinding Footer}" HeaderTemplate="{TemplateBinding HeaderTemplate}" Header="{TemplateBinding Header}" HeaderTransitions="{TemplateBinding HeaderTransitions}"/>
</Grid>
</ScrollViewer>
</Border>
</ControlTemplate>
2.在ListView的Loaded事件中拿到其内部的SrollViewer,以及订阅相关事件。
private void ListView_Loaded(object sender, RoutedEventArgs e)
{
Get_Child((DependencyObject)sender);
lastest_listview_sc.ViewChanging += Lastest_listview_sc_ViewChanging;
lastest_listview_sc.ViewChanged += Lastest_listview_sc_ViewChanged;
}
其中这个Get_Child是一个实现遍历可视化树的方法,我们这里传递的参数sender就是ListView本身。Get_Child代码如下:
private void Get_Child(DependencyObject o)
{
try
{
int count = VisualTreeHelper.GetChildrenCount(o);
if (count > )
{
for (int i = ; i < count; i++)
{
var child = VisualTreeHelper.GetChild(o, count - );
if (child is ScrollViewer)
{
lastest_listview_sc = child as ScrollViewer;
}
else
{
Get_Child(VisualTreeHelper.GetChild(o, count - ), n);
}
}
}
}
catch (Exception ex)
{
return;
}
}
3.这里就是接下来的重点了。相信自己动手实现过下拉刷新的同学们都能够遇见过这个问题:如果用户只是普通的滚动ListView查看内容,手指往下滑的时候(内容是往下)ScrollViewer会由于惯性偏移量滑到最上部,或者其他的类似操作也会滑动到最上部,但是此时用户并不想要刷新。因此,我们就要在Lastest_listview_sc_ViewChanging方法内部实时监听ScrollViewer的滚动状态。如果此时ScrollViewer是由于惯性偏移量在滚动并且已经滚动超过了指定条件,我们就要让SrollViewer自己滚回30的位置,也就是判错。另外在触屏设备和非触屏设备上面,我们判错的指定条件最好不要一样(因为电脑用的是鼠标滚轮手机Surface之类的用的是手指,要是设定成一样的你会发现可能你怎么样都无法滑到最上面。。。)
因此上面提到的问题我们要在Lastest_listview_sc_ViewChanging方法中解决。其中的形参e的属性IsInertial可以用来获取操作是否具有惯性元素。Lastest_listview_sc_ViewChanging代码如下:
private async void Lastest_listview_sc_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e) //下拉刷新判错 自动加载
{
if (App.DeviceInfo.Device_type != Model.DeviceType.PC)
{
if (lastest_listview_sc.VerticalOffset <= 10.0)
{
if (e.IsInertial)
{
lastest_listview_sc.ChangeView(null, 30.0, null);
}
}
}
else
{
if (lastest_listview_sc.VerticalOffset <= 50.0 && lastest_listview_sc.VerticalOffset > 30.0)
{
if (e.IsInertial)
{
lastest_listview_sc.ChangeView(null, 30.0, null);
}
}
}
}
4.现在我们要用到Lastest_listview_sc_ViewChanged这个方法。其中的形参e的属性IsIntermediate可以告诉我们引发ViewChanged事件的基础操作是否完成,如果完成的话我们就可以来判断是否需要刷新了。代码如下:
private void Lastest_listview_sc_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
if (!e.IsIntermediate)
{
Lastest_Refresh();
}
}
5.最后一步了,就是实现我们的Lastest_Refresh方法,在这个方法中,我们需要判断ScrollViewer是否滚动到了最顶端,如果是的话,就可以开始执行刷新代码。刷新结束之后再将ScrollViewer往下滚30就OK了。代码如下:
private async void Lastest_Refresh()
{
if (lastest_listview_sc.VerticalOffset == 0.0)
{
//刷新相关代码
await Task.Delay(); //移动回顶部
lastest_listview_sc.ChangeView(null, , null);
}
}
至此,下拉刷新的过程到结束。当然,大家看到我的代码还是比较复杂,也不够简洁,毕竟我还是个大一学生。拿出来是跟大家交流的嘛,如果有什么更好的方法或者建议欢迎评论,谢谢。感激不尽!
——2016/08/01编辑——
忘了写一个点,就是进入页面后我们要先把刷新部分的Header隐藏起来,要不然Header就会一直出现。。。可以在页面的OnNavigate方法最后添加,代码如下:
await Task.Delay();
series_listview_sc.ChangeView(null, , null);
第一行的意义是如果直接执行的话可能会没有效果,所以要先Delay。
然后还要感谢网友提出一个很严重的BUG,就是如果ListView内部item过少,Header可能会无法被隐藏掉,这个时候这套刷新逻辑就没有用了。。。希望大家有更好的方法的话可以分享一下,谢谢!
(UWP开发)更为合理的一种ListView下拉刷新(PullToRefresh)实现方法的更多相关文章
- ListView下拉刷新上拉加载更多实现
这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...
- 手把手教你轻松实现listview下拉刷新
很多人觉得自定义一个listview下拉刷新上拉加载更多是一件很牛x的事情,不是大神写不出来,我想大多数童鞋都是做项目用到时就百度,什么pulltorefresh,xlistview...也不看原理, ...
- 自定义ListView下拉刷新上拉加载更多
自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...
- Android—自定义控件实现ListView下拉刷新
这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的 ...
- ListView下拉刷新、上拉载入更多之封装改进
在Android中ListView下拉刷新.上拉载入更多示例一文中,Maxwin兄给出的控件比较强大,前面有详细介绍,但是有个不足就是,里面使用了一些资源文件,包括图片,String,layout,这 ...
- listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖
本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...
- listview下拉刷新上拉加载扩展(二)-仿美团外卖
经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...
- listview下拉刷新上拉加载扩展(一)
前两篇实现了listview简单的下拉刷新和上拉加载,功能已经达到,单体验效果稍简陋,那么在这篇文章里我们来加一点效果,已达到我们常见的listview下拉刷新时的效果: 首先,在headview的x ...
- Android ListView下拉刷新时卡的问题解决小技巧
问题:ListView下拉刷新时看上去非常的卡 解决方案: 在BaseAdapter的getView方法中,有三个参数 public View getView(int position, View c ...
随机推荐
- python 单例模式
单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在 用装饰器方式实现单例模式 #!/usr/bin/python # coding=utf-8 d ...
- JS 循环给li绑定参数不同的点击事
以下内容纯属个人理解,不正确的地方还请大神留言,不胜感激! 源代码:(按个人方式选用一种即可) <ul> <li>1</li> <li>2</li ...
- MongoDB【第二篇】MongoDB逻辑与物理存储结构
基本的操作 一.常用的命令和基础知识 1.进入MongoDB sehll 首先我们进入到MongoDB所在目录执行 cd /work/app/mongodb/bin/ #启动 ./mongo 为了方便 ...
- java内存泄漏的几种情况
转载于http://blog.csdn.net/wtt945482445/article/details/52483944 Java 内存分配策略 Java 程序运行时的内存分配策略有三种,分别是静态 ...
- DateUtils 学习记录1
开发过程中很多时候都需要处理各种各样的日期..有些项目可能还会有自己的DateUtil.... 其实apache commons lang3有一个很好用的日期处理工具类,叫DateUtils... 基 ...
- NOSDK--关于android傻瓜式的分包设想
一直以来,我总是以“够用就好”为理由,很少再维护过自己的一键打包的项目.最近接触了棱镜的sdk,感觉将apk包上传到棱镜服务器,后台来进行分包这种简单的方式很招人待见. 原理似乎不难,apk即zip压 ...
- json-c与树
json是一种轻量级的数据交换格式,因为其灵巧使得其在应用层开发以及接口层开发使用的十分广泛,最常用的莫过于协议交流使用json数据,比xml轻巧,又比二进制数据有规则.无论是各大公司的开放平台或者是 ...
- python爬虫小项目实战
- 测试基础:Bug管理那些事_20160910
1.bug由来 虫子爬进主机引起继电器短路,导致机器故障.真正的缺陷是:主机散热孔少装了块金属丝,这样才能防止虫子爬到主机. 2.什么是bug? bug是缺陷的一种表现形式,而一个缺陷是可以引发多种b ...
- N皇后问题—初级回溯
N皇后问题,最基础的回溯问题之一,题意简单N*N的正方形格子上放置N个皇后,任意两个皇后不能出现在同一条直线或者斜线上,求不同N对应的解. 提要:N>13时,数量庞大,初级回溯只能保证在N< ...