通常情况下,Android默认的ListView 在滚动到顶端或底端的时候,并没有很好的提示。在IOS系统的交互中,列表是有弹性的,当列表滑动到顶部或者底部时会继续往上或者往下滑动一段距离。但是在Android系统中,Google并没有赋予listview这种良好的交互效果,仅在Android 5.X版本的系统中,给这种行为添加了一个半月型的波纹阴影。
搜了一下网上的解决方案,基本上都是通过重写ListView来实现弹性效果的。比如增加HeaderView 或者嵌套scrollView,方法很多,这里不再赘述。不过这篇博文主要给大家介绍一个非常简单的方法来实现这种效果。
查看ListView的源代码时候可以发现,其中一个方法,可以控制ListView滑动到边缘的处理方法,源码如下:
/**
* Scroll the view with standard behavior for scrolling beyond the normal
* content boundaries. Views that call this method should override
* {@link #onOverScrolled(int, int, boolean, boolean)} to respond to the
* results of an over-scroll operation.
*
* Views can use this method to handle any touch or fling-based scrolling.
*
* @param deltaX Change in X in pixels
* @param deltaY Change in Y in pixels
* @param scrollX Current X scroll value in pixels before applying deltaX
* @param scrollY Current Y scroll value in pixels before applying deltaY
* @param scrollRangeX Maximum content scroll range along the X axis
* @param scrollRangeY Maximum content scroll range along the Y axis
* @param maxOverScrollX Number of pixels to overscroll by in either direction
* along the X axis.
* @param maxOverScrollY Number of pixels to overscroll by in either direction
* along the Y axis.
* @param isTouchEvent true if this scroll operation is the result of a touch event.
* @return true if scrolling was clamped to an over-scroll boundary along either
* axis, false otherwise.
*/
@SuppressWarnings({"UnusedParameters"})
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
final int overScrollMode = mOverScrollMode;
final boolean canScrollHorizontal =
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
final boolean canScrollVertical =
computeVerticalScrollRange() > computeVerticalScrollExtent();
final boolean overScrollHorizontal = overScrollMode == OVER_SCROLL_ALWAYS ||
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
int newScrollX = scrollX + deltaX;
if (!overScrollHorizontal) {
maxOverScrollX = 0;
}
int newScrollY = scrollY + deltaY;
if (!overScrollVertical) {
maxOverScrollY = 0;
}
// Clamp values if at the limits and record
final int left = -maxOverScrollX;
final int right = maxOverScrollX + scrollRangeX;
final int top = -maxOverScrollY;
final int bottom = maxOverScrollY + scrollRangeY;
boolean clampedX = false;
if (newScrollX > right) {
newScrollX = right;
clampedX = true;
} else if (newScrollX < left) {
newScrollX = left;
clampedX = true;
}
boolean clampedY = false;
if (newScrollY > bottom) {
newScrollY = bottom;
clampedY = true;
} else if (newScrollY < top) {
newScrollY = top;
clampedY = true;
}
onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
return clampedX || clampedY;
}
从注释当中可以看到这样一个参数
maxOverScrollY
Number of pixels to overscroll by in either >direction along the Y axis.
这个参数标识了ListView 沿Y轴方向超出范围滚动的像素数,我们可以修改这个值,就可以让ListView具有弹性。只要重写ListView中的这个方法,设置maxOverScrollY 为我们自己定义的偏移值maxOverOffset.代码如下:
public class FlexibleListView extends ListView {
private static int maxOverOffset = 50;
private Context mContext;
public FlexibleListView(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
}
public FlexibleListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
initView();
}
public FlexibleListView(Context context) {
super(context);
this.mContext = context;
initView();
}
private void initView() {
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
float density = metrics.density;
//Log.d("TAG", "屏幕密度比例因子为" + density);
maxOverOffset = (int) (density * maxOverOffset);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY,
scrollX, scrollY,
scrollRangeX, scrollRangeY,
maxOverScrollX, maxOverOffset,
isTouchEvent);
}
}
注意,为了保持不同分辨率屏幕下弹性距离保持基本保持一致,偏移值 maxOverOffset 根据屏幕密度比例进行了重新计算。
效果如下图所示:
完整demo读者自己写下,比较简单,这里不再给出,这个例子提醒我们,平常在开发过程中注意阅读源码,往往能得到意想不到的结果。