基于Android平台的刷新加载形式初探

        接触Android开发有一段时间,经常会遇到数据加载形式的设计,现在最常见的是下拉刷新上拉加载以及滚动到底部自动加载,这些往往是基于ListViewGridViewExpandableListView。此文主要简单讨论这些加载形式的设计实现。

不妨先去理解基本的思想(大量图参考自网络,引用地址放在文章最后):

•         滑动动画设计

    

左图为传统的下拉动画,右图方案随着SwipeRefreshLayout的出现也大量流行。

•         有效拉伸距离

表示下拉刷新之后列表第一个item可以被允许下拉最大距离,经调研开源库,大部分采用选择屏幕高度的1/2作为阈值。

•         伸缩速率

在下拉时,较好的体验是:列表下滚的速度要小于手指触摸点下拉的速度。因为没有严格的标准,速率比值为50%-80%较为合理(调研的库列于文章最后)。

•         数据更新

最后是数据更新时机和策略的选择,在请求初识数据列表、刷新、以及加载更多等时候都要进行数据的更新。加载策略一般为异步任务:HandlerAsyncTask,选用后者的为多。


具体的实现方式可以有多种,以两种为例:

  •        LinearLayout+Padding/Margin

                              

如图所示,充分使用布局的动态性来完成,采用线性布局,Header View  Footer View。宽、高分别为match_parentwrap_content,相应Padding设置为负。Content View宽高分别为match_parent。在用户滑动时,监听滑动事件,顺势改变Content View的padding值,实现下拉、上拉的展示。这种实现方式有个很明显的缺点:不够流畅,完全依赖View的重绘。

  •        ListView+SwipeRefreshLayout

这种方式本质上这种方法使用了ListView的HeaderView和FooterView,下拉通过设计HeaderView的高度来展示,同理,上拉时增加FooterView的高度,这样在形式上就完成了整个界面的响应。具体以上拉和到底部自动加载为例。当ListView滚动到底部时自动触发加载操作。同时,根据用户触摸事件判断来判断是否在执行上拉加载更多,如下

    

  在onScroll事件中判断,是否达到了加载条件,加载条件也较为简单:判断1、是否到达底部2、目前没有处于加载状态;3、当前正在进行上拉操作,在这样的情况下可以执行数据加载。

那么在数据加载时候需要对isLoading进行更新,不在加载时说明FooterView已经达到了最大高度或是最小高度(上拉完毕和并没有上拉两种情况),那么就对其进行消除。isLoading刚刚被设置为True时,则是上拉事件刚刚触发时,所以需要对FooterView进行添加。下拉事件是对HeaderView同样的控制机制。isLoading是一个控制状态的变量,在整个流程中转化状态显得至关重要,在情形稍微复杂点时,我们需要添加其他一些变量和状态进行控制,也就形成状态机。

       

•          ViewGroup+Scroller
这种方法是用一个ViewGroup来管理所有需要动态显示的View,这可以说是兼容性最好的一种实现策略,因为ContentView可以是任何View: ListViewGridView甚至 ExpandableListView等等。这里是充分利用Android的事件分发机制(隧道传递、冒泡处理),以下图为例:

        •在初始时通过滚动,使得该组件在Y轴方向上滚动HeaderView的高度的距离,这样HeaderView就被隐藏掉了。

        •当组件被滚动到顶端时,如果用户继续下拉,那么拦截触摸事件,然后通过Scroller来滚动y轴的偏移量,实现逐步的显示HeaderView,从而到达下拉的效果,当用户滑动到最底部时会触发加载更多的操作。

这种策略可以抛弃对ListView的HeaderView、FooterView的依赖,实现上更为灵活。

 


一些开源组件介绍
  •    XlistView:继承ListView
    •    处理屏幕点击事件
    •   控制滑动实现效果
    •  HeaderView & FooterView
    •   重写onRefresh()方法
    •   重写onLoadMore()方法
    • stopRefresh:停止刷新
    • stopLoadMore:停止加载更多

 https://github.com/Maxwin-z/XListView-Android.git

 值得一提的是,我最喜欢的虎扑Android客户端也是使用了该库。

XListView思路基本上是和ListView类似,充分利用ListView的HeaderView和FooterView,通过高度的调整来展示动态变化。下述源码中,我们上拉加载为例,上拉操作被捕捉,当发现最后可见的Position是mTotalItemCount-1时,说明已经上拉到了列表底部,deltaY<0表示用户仍然在上拉,在此对FooterView的高度进行Update,update方法是对height进行+delta,所以此时delta是负值,传入时候应该传-delta。

XListView虽然简单好用,但是也继承了ListView的缺点:依赖ListView自带的HeaderView和FooterView,只能解决ListView的问题,对于GridView和ExpandableListView等并没有支持。


  • PullToRefresh by Chris Banes  

PullToRefresh是比较经典的下拉刷新组件,大概有这么经典:

具体它的特色可以看git上作者给出的:

  • Supports both Pulling Down from the top, and Pulling Up from the bottom (or even both).
  • Animated Scrolling for all devices.
  • Over Scroll supports for devices on Android v2.3+.
  • Currently works with:
    • ListView
    • ExpandableListView
    • GridView
    • WebView
    • ScrollView
    • HorizontalScrollView
    • ViewPager
  • Integrated End of List Listener for use of detecting when the user has scrolled to the bottom.
  • Maven Support.
  • Indicators to show the user when a Pull-to-Refresh is available.
  • Support for ListFragment!
  • Lots of Customisation options!

从实现上来看,其实现方式是用了ViewGroup来包装内层的ContentView,通过事件控制来展示,所以其支持性非常好。基本思路为我们介绍的实现方法三。具体来看如下:

  • 状态机控制

  • Mode管理
    • DISABLED(0x0)
    • PULL_FROM_START(0x1)
    • PULL_FROM_END(0x2)
    • BOTH(0x3)
    • MANUAL_REFRESH_ONLY(0x4)

主要通过两个变量来控制流程:mMode && mCurrentMode,mMode表示总的支持方式,如果设计为PULL_FROM_START(0x1),则表示整个app只支持下拉,BOTH(0x3)表示上拉下拉都支持,是一个总的开关,选择后只能在设置里面用户手动更改。而mCurrentMode是在具体控制分支中当前处在的状态,是控制状态机走向最为关键的变量。如用户正在执行下拉操作:PULL_FROM_START(0x1),此时有可能mMode的用户选择的是BOTH(0x3)而并非一定要是PULL_FROM_START(0x1),mCurrentMode是不停变化的。如在PullToRefreshBase:

在PullToRefreshAdapterViewBase中:


 
mCurrentMode = (mMode != Mode.BOTH) ? mMode : Mode.PULL_FROM_START; 

  • 事件分发控制

在方法三中我们也提到了,使用ViewGroup的话需要使用Android事件分发机制来完成设计,PullToRefresh正有这样的体现

PullToRefreshBase监听到了用户的上拉下拉事件,对mCurrentMode进行修改以保证状态转化正确。这里返回值是mIsBeingDragged,如果为true表明onInterceptTouchEvent已经可以处理,也就是在滑动ListView 。

在PullToRefreshBase的onTouchEvent()  函数中,先保存最后滑到的位置,并调用pullEvent函数。

在pullEvent函数中,发现用户拉动的条件已经满足了滑动条件(itemDimension<Math.abd(newScrollValue)),则将应用状态转化为刷新状态,启用刷新函数。

newScrollValue和itemDimension的计算如下 (以上拉为例),通过计算滑动的距离,让其除以阈值FRICTION(FRICTION即为开始提到的伸缩速率,这里作者是设置FRICTION为2,即列表滑动速率是用户实际滑动速率的50%,以实现拖拽效果)。

当发现滑动过程已经满足刷新或者加载条件时,则将返回值mIsBeingDragged设置为false,让ListView 停止阻塞上拉下拉事件,而将处理交给ViewGoup,也就是开始滑动整体内容(包涵HeaderView、ContentView、FooterView等,这里的HeaderView和FooterView并不是ListView自带的,而是自定义View)。

  •  数据更新

这里简单列下流程:

  • android-Ultra-Pull-To-Refresh

有篇不错的源码解析:http://a.codekk.com/detail/Android/Grumoon/android-Ultra-Pull-To-Refresh%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值