Android的ListView/ScrollView支持回弹效果

看到iOS里面的Table和ScrollView都能在拉到头的时候,还能继续滑出来,松手就弹回去,感觉确实很爽。但是Android上默认是没有这个效果的,很多第三方rom或者app都支持这种效果。

搜索了一下才发现这篇博客,原来Android2.3中就支持overscroll功能。但是实现机制和iOS不一样,可能是专利的问题。Android上实现很简单,如果拉到底了,就会在边上出现发光效果。我们可以很容易修改,达到像iOS上面那样的效果。代码如下:

public class BounceListView extends ListView{  
    private static final int MAX_Y_OVERSCROLL_DISTANCE = 200;

    private Context mContext;
    private int mMaxYOverscrollDistance;

    public BounceListView(Context context){
        super(context);
        mContext = context;
        initBounceListView();
    }

    public BounceListView(Context context, AttributeSet attrs){
        super(context, attrs);
        mContext = context;
        initBounceListView();
    }

    public BounceListView(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
        mContext = context;
        initBounceListView();
    }

    private void initBounceListView(){
        //get the density of the screen and do some maths with it on the max overscroll distance
        //variable so that you get similar behaviors no matter what the screen size

        final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
        final float density = metrics.density;

        mMaxYOverscrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);
    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent){ 
        //This is where the magic happens, we have replaced the incoming maxOverScrollY with our own custom variable mMaxYOverscrollDistance; 
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxYOverscrollDistance, isTouchEvent);  
    }

}

这里的关键是重载了方法overScrollBy(),传入一个之mMaxYOverscrollDistance。默认是0,所以拉到头就拉不动了。

上面的效果已经接近iOS上的效果了,但是看体验起来开始有点生硬,我们加入一些摩擦里,获得更好的体验,修改overScrollBy()方法如下:

protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,  
                               int scrollY, int scrollRangeX, int scrollRangeY,
                               int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
    if (!isTouchEvent) { // 禁止惯性滑动
        if ((scrollY < 0 && deltaX < 0)
             || (scrollY > getHeight() && deltaX > 0)) {
            deltaY = 0;
        }
    }
    return super.overScrollBy(deltaX, (deltaY + 1) / 2, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxScroll, isTouchEvent);
}

加入摩擦力就是这里:(deltaY + 1) / 2。上面那个条件判断,是禁用惯性运动。这样效果就自然多了。