一、意义
public int getScrollY() {
View c = mListView.getChildAt(0);
if (c == null) {
return 0;
}
int firstVisiblePosition = mListView.getFirstVisiblePosition();
int top = c.getTop();
return -top + firstVisiblePosition * c.getHeight() ;
}
这是假设列表每项的高度相等,来算y轴方向的滚动距离,这是比较常用的做法,已能解决大多数问题。
2) 开源控件QuickReturnHeader的做法:
if (lastFirstVisibleItem == firstVisibleItem) {
delta = lastTop - top;
} else if (firstVisibleItem > lastFirstVisibleItem) {
skipped = firstVisibleItem - lastFirstVisibleItem - 1;
delta = skipped * height + lastHeight + lastTop - top;
} else {
skipped = lastFirstVisibleItem - firstVisibleItem - 1;
delta = skipped * -height + lastTop - (height + top);
}
这和上面的getScrollY()并无实质不同。
3) 以上的方法的ListView类型比较简单,实际应用中,ListView往往有很多类型,每个类型的高度都不一样,这种情形还有没有比较便捷的方法能准确计算滚动距离?也许会想到扩展一下getScrollY()函数,分配一个数组,有新的item显示在屏幕上就记下它的高度,然后累加。那如果不断往下滑加载新数据,这个数组又得分配多大才够呢?
其实不需要单独存储每个item的高度,只要求出两次onScroll()回调时刻之间滑过的距离并累加,就是y轴方向的滚动距离,具体实现可以根据每个应用自己的需求。比如Lofter每个人的动态都是文字、图片、视频类型,item高度至少在60dp以上,xxhdpi屏幕上,假设要在两次onScroll()之间(假设间隔50ms,实际更快)滑过3个item,指尖速度至少达到10800px/s,这是不可能的,因此只要滑动时存储前3个item高度完全够用了。
if (direction == Direction.SCROLL_UP) {
if (lastFirstVisibleItem == firstVisibleItem) {
dY = Math.abs(lastTop - nowTop);
} else {
dY = lastFirstHeight - Math.abs(lastTop) + Math.abs(nowTop);
}
} else if (direction == Direction.SCROLL_DOWN) {
if (lastFirstVisibleItem == firstVisibleItem) {
dY = Math.abs(nowTop - lastTop);
} else {
dY = firstHeight - Math.abs(nowTop) + Math.abs(lastTop);
}
为了方便以后调用,可以写成一个更通用的库,只要根据需求给出item个数N(如以上N=3),就能按上面思路算出任意ListView的滚动距离了。
这样计算距离虽然麻烦些,但对提高用户体验是有好处的,比如QuickReturnBar操作起来会经常误触发,原因在于没有对滑动做较准确的阈值处理。
四、改写下拉刷新框架
主要的坑如下:
1. 继承PullToRefreshListView,并override掉相应方法,像setRefreshingInternal(), refreshHeader(), scroll2Top(), pullEvent(), setAdapter(), onRefreshComplete()等都要重写。为何连setAdapter()都要改?为了适配魅族机型,flyme的列表比较奇葩,除了下拉悬停,还会反弹滚动,需要禁掉。
2. 滚动条偏移位置,不让ListView Header的也计算在滚动条内,需要重写computeVerticalScrollOffset()。
3. empty view逻辑的特殊处理。
4. 如果顶栏比较高,“正在刷新”条的高度会比较高,产品对视觉要求高,实现细节中需要动态修改ListView Header高度。
5. 主要还是体验的难点:①用动画来优化体验,控制好“顶栏一半以上隐藏”、“一半以上可见”、“全部可见”、“全部隐藏”这4个状态,并对状态的过渡使用动画,还有viewpager里切换时相应动画;②为了防止顶栏频繁的拉出和隐藏,我们还加入了下拉一屏以上的距离才将顶栏拉出的逻辑,这样用户在下拉浏览时仍可以全屏浏览。
现在下拉刷新更时髦的似乎是Gmail的ActionBar刷新风格,如果用ActionBar方式,那么滚动并隐藏顶栏的刷新逻辑实现起来会简单得多。
本文来自网易实践者社区,经作者范晨灿授权发布。