弹性ScrollView-仿微信弹性页面
程序员文章站
2022-05-31 09:05:55
...
一、先看效果
二、准备工作
要实现这个效果,我的想法是给ScrollView包一个container,然后判断边界值,看当前的touch事件交给谁去处理。回弹的效果交给Scroller处理,甚至可以写出一个下拉刷新,看代码分析吧。
三、代码分析
在dispatchTouchEvent中去判断边界值,dispatchTouchEvent返回true或者false都是自己消费当前事件,否则super传递下去。代码如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float currentY = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = currentY;
break;
case MotionEvent.ACTION_MOVE:
float distanceY = currentY - mLastY;
if (mIsDraging) {
if (distanceY <= 0) {
if(mScrollView.isScrolledToTop()) {
scrollTo(0, 0);
mIsDraging = false;
return super.dispatchTouchEvent(ev);
}
}else{
if(mScrollView.isScrolledToBottom()){
scrollTo(0, 0);
mIsDraging = false;
return super.dispatchTouchEvent(ev);
}
}
scrollTo(0, (int) (-distanceY * ratio));
return true;
} else {
if (Math.abs(distanceY) > mTouchSlop) {
if (distanceY > 0) { // 向下
if (mScrollView.isScrolledToTop()) {
mLastY = currentY;
mIsDraging = true;
}
}else{
if(mScrollView.isScrolledToBottom()){
mLastY = currentY;
mIsDraging = true;
}
}
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsDraging) {
int scrollY = getScrollY();
mScroller.startScroll(0, scrollY, 0, -scrollY, duration);
mIsDraging = false;
invalidate();
return true;
}
break;
}
return super.dispatchTouchEvent(ev);
}
- 首先DOWN下记录起始位置,MOVE下先判断当前是不是拖拽状态mIsDraing,否,进入else里面,如果手指滑动距离需要响应了,判断方向,如果向下,还要看ScrollView是不是在顶部(为什么?),如果在顶部,那么记录当前 y 坐标(因为在判断这个期间,手指已经向下滑了一段距离了,如果还记录之前的位置,会很突兀),并将拖拽mIsDraing = true,开始向下。
- 这个时候再进入MOVE,已经是拖拽中,这个时候可以直接调用
scrollTo(0, (int) (-distanceY * ratio));
return true;
ratio是弹性系数。但是考虑到一种情况就是,刚开始下滑的时候手指突然向上滑动,这个时候container到顶了,ScrollView也在顶部,这个时候事件需要交给ScrollView而不是container继续滑动。同样的道理在底部回弹的时候,也存在上拉container到底了这个时候手指向下滑动,同样需要把事件交给ScrollView处理。所以在scrollTo之前需要再一次判断distanceY,如果小于0并且ScrollView在底部,事件传递,container需要恢复开始位置,这个时候虽然手指没有抬起,但是也不能是拖拽状态:
scrollTo(0, 0);
mIsDraging = false;
return super.dispatchTouchEvent(ev);
- ScrollView需要能判断有没有到顶或到底,重写一个StretchScrollView:
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (getScrollY() == 0) {
isScrolledToTop = true;
isScrolledToBottom = false;
} else if (getScrollY() + getHeight() - getPaddingTop()-getPaddingBottom() == getChildAt(0).getHeight()) {
isScrolledToTop = false;
isScrolledToBottom = true;
} else {
isScrolledToTop = false;
isScrolledToBottom = false;
}
}
很简单对吧,同样适用于ListView,区别仅仅在于判断顶部底部的方法不一样。最后,可以扩展一点,凡是可以判断是顶部底部的view都可以加进来,抽象一个StretchView出来。附上gayHub地址。有兴趣拓展吧!