接上一篇文章,
ScrollView有了下拉和上拉的弹性回弹,这个时候发现有时候下拉的时候界面UI会变得很丑,不想让ScrollView下拉怎么办?
简单啊,只需要在下拉到顶部的时候判定一下,然后maxOverScrollY不改动的直接传给super.overScrollBy就可以了。
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
//增加阻尼效果,使滑动有吃力感
double ratio = 1d;
//在顶部并且是向下拖动
if (deltaY < 0 && scrollY + deltaY < 0) {
return super.overScrollBy(deltaX, (int) (deltaY * ratio), scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY,
isTouchEvent);
} else if (deltaY > 0 && scrollY + deltaY > scrollRangeY) { //滑动到底部并且向下滑动
ratio = 1.05d + (scrollRangeY - scrollY) / (newMaxOverScrollY * 1.2);
}
return super.overScrollBy(deltaX, (int) (deltaY * ratio), scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, newMaxOverScrollY,
isTouchEvent);
}
但是这个时候有个问题,在顶部的时候下拉是不会向下拉了,但是在顶部会出现一个非常令人不爽的EdgeEffect的透明波纹阴影!我就是看这个阴影不爽,想去掉怎么办?好办啊,设置overScrollMode = OVER_SCROLL_NEVER就OK啦,这样就没阴影啦!别急着高兴,是不是觉得有哪些不对?没错,如果你这样设置了,你就会发现,好像不会滑动了?卧槽,那我之前的功夫不是白做了?!难道要我重新去重写onTouchEvent?!不行,这太可怕了。为了偷懒,我们还是看源码吧,感觉还是看源码方便一些XD。
要想了解设置overScrollMode = OVER_SCROLL_NEVER为什么就不能再越出边界的原因,就要从overScrollBy的源码看起,上代码!
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;
}
为什么设置OVER_SCROLL_NEVER就不能越界了,关键就在这里final boolean overScrollVertical = overScrollMode == OVER_SCROLL_ALWAYS ||
,设置了overScrollMode = OVER_SCROLL_NEVER,也就代表着
(overScrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);overScrollVertical = false
,然后maxOverScrollY = 0;
, 也即不能再越界了。弄明白了为什么OVER_SCROLL_NEVER就不能越界的原因了,而且也看到了overScrollBy的源码了,下面就好办了,自己写一个overScrollBy方法不就可以了吗。没错就这么简单。再BounceScrollView中再加一个函数:
/**
* 这里在ScrollView中就不用关心横向滑动问题,可以把原来横向逻辑的代码统统删掉,
* 并且设置默认是可以竖向滑动的
*/
@SuppressWarnings({"UnusedParameters"})
protected boolean customOverScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
int newScrollX = scrollX + deltaX;
int newScrollY = scrollY + deltaY;
// 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;
}
然后再修改一下之前的dispatchNestedFling的逻辑,最后完整的BounceScrollView代码奉上!
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.ScrollView;
public class BounceScrollView extends ScrollView {
// 这个值控制可以把ScrollView包裹的控件拉出偏离顶部或底部的距离。
private static final int MAX_OVER_SCROLL_Y = 100;
private int newMaxOverScrollY;
public BounceScrollView(Context context) {
super(context);
}
public BounceScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public BounceScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float density = metrics.density;
newMaxOverScrollY = (int) (density * MAX_OVER_SCROLL_Y);
//false:隐藏ScrollView的滚动条。
this.setVerticalScrollBarEnabled(false);
//去掉拉到底部或者顶部的半透明波纹阴影效果。
this.setOverScrollMode(ScrollView.OVER_SCROLL_NEVER);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX,
int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
//增加阻尼效果,使滑动有吃力感
double ratio = 1d;
if (deltaY > 0 && scrollY + deltaY > scrollRangeY) { //滑动到底部并且向下滑动,使用自己的customOverScrollBy
ratio = 1.05d + (scrollRangeY - scrollY) / (newMaxOverScrollY * 1.2);
return customOverScrollBy(deltaX, (int) (deltaY * ratio), scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, newMaxOverScrollY, isTouchEvent);
}
//其他情况还是直接走super的流程
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY,
scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
// Not consumed means it wasn't handled because ScrollView
// doesn't take over scrolling bounds into scroll range,
// so we fling it ourselves to get it bounce back
if (!consumed) {
fling((int) velocityY);
return true;
} else {
return super.dispatchNestedFling(velocityX, velocityY, consumed);
}
}
/**
* 这里在ScrollView中就不用关心横向滑动问题,可以把原来横向逻辑的代码统统删掉,并且设置默认是可以竖向滑动的
*/
@SuppressWarnings({"UnusedParameters"})
protected boolean customOverScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
int newScrollX = scrollX + deltaX;
int newScrollY = scrollY + deltaY;
// 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;
}
}
其实你还可以自己,定义两个变量mCanBounceTop,mCanBounceBottom来控制是否能在顶部和底部实现弹性回缩效果,有兴趣的可以自己实现下。~