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

Posted clumsypanda

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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












以上是关于基于Android平台的刷新加载形式初探的主要内容,如果未能解决你的问题,请参考以下文章

React Native For Android 架构初探

React Native For Android 架构初探

React Native For Android 架构初探

android Glide图片加载框架的初探

分享吧基于Ansible实现平台自动部署初探

Appnium移动自动化框架初探