ScrollView的时候:
使用场景:
1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动到底部或者顶部
2. ScrollView滚动到顶部或者底部时主动触发一些操作(典型的就是滚动到底部触发自动加载操作)
两种方式:
1. onScrollChanged方式,自己计算
2. onOverScrolled使用系统计算的结果,api >= 9才支持
可能忽视的细节1:
如果是手势滑动,上面两种方式都对,但是如果是调用ScrollView的smoothScrollTo和scrollTo方法来滚动的话,
只有onScrollChanged监听对,onOverScrolled监听不对,因为通过代码来滚动话是精确滚动,onOverScrolled方法没处理这种情况
两种方式如何选择?
一般来说如果系统有实现的东西,就用系统的,我们毕竟是基于Android系统来做开发,别人做的考虑很多适配兼容问题
所以原则就是:系统有就用它的,没有才用我们自己的,具体实现仔细看代码,记得看注释
如果不考虑smoothScrollTo和scrollTo滚动,上面这个原则就是对的,如果要考虑的话,这里只能使用onScrollChanged
滚动到顶部和底部时对应的计算关系:
备注:无padding的情况可以转换为有padding的情况,即tp,bp=0
mScrollY + H – tp – bp = h ===> mScrollY + H = h
代码实现,自定义View,可以直接拷贝就可以使用
public class CalculateHeightScrollView extends ScrollView { private boolean isScrolledToTop = true; // 初始化的时候设置一下值 private boolean isScrolledToBottom = false; int percentRate = 0; //计算百分比用 public CalculateHeightScrollView(Context context, AttributeSet attrs) { super(context, attrs); } private ISmartScrollChangedListener mSmartScrollChangedListener; /** * 定义监听接口 */ public interface ISmartScrollChangedListener { void onScrolledToBottom(); void onScrolledToTop(); void getScrollPercentRate(int percentRate); } public void setScanScrollChangedListener(ISmartScrollChangedListener smartScrollChangedListener) { mSmartScrollChangedListener = smartScrollChangedListener; } @Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); if (scrollY == 0) { isScrolledToTop = clampedY; isScrolledToBottom = false; percentRate = 0; } else { isScrolledToTop = false; isScrolledToBottom = clampedY; percentRate = (scrollY + getHeight() - getPaddingTop() - getPaddingBottom()) * 100 / getChildAt(0).getHeight(); } notifyScrollChangedListeners(); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (android.os.Build.VERSION.SDK_INT < 9) { if (getScrollY() == 0) { // 小心踩坑1: 这里不能是getScrollY() <= 0 isScrolledToTop = true; isScrolledToBottom = false; percentRate = 0; } else if (getScrollY() + getHeight() - getPaddingTop() - getPaddingBottom() == getChildAt(0).getHeight()) { // 小心踩坑2: 这里不能是 >= // 小心踩坑3(可能忽视的细节2):这里最容易忽视的就是ScrollView上下的padding isScrolledToBottom = true; isScrolledToTop = false; percentRate = (getScrollY() + getHeight() - getPaddingTop() - getPaddingBottom()) * 100 / getChildAt(0).getHeight(); } else { isScrolledToTop = false; isScrolledToBottom = false; percentRate = (getScrollY() + getHeight() - getPaddingTop() - getPaddingBottom()) * 100 / getChildAt(0).getHeight(); } notifyScrollChangedListeners(); } // 有时候写代码习惯了,为了兼容一些边界奇葩情况,上面的代码就会写成<=,>=的情况,结果就出bug了 // 我写的时候写成这样:getScrollY() + getHeight() >= getChildAt(0).getHeight() // 结果发现快滑动到底部但是还没到时,会发现上面的条件成立了,导致判断错误 // 原因:getScrollY()值不是绝对靠谱的,它会超过边界值,但是它自己会恢复正确,导致上面的计算条件不成立 // 仔细想想也感觉想得通,系统的ScrollView在处理滚动的时候动态计算那个scrollY的时候也会出现超过边界再修正的情况 } private void notifyScrollChangedListeners() { if (isScrolledToTop) { if (mSmartScrollChangedListener != null) { mSmartScrollChangedListener.onScrolledToTop(); } } else if (isScrolledToBottom) { if (mSmartScrollChangedListener != null) { mSmartScrollChangedListener.onScrolledToBottom(); } } if (mSmartScrollChangedListener != null) { mSmartScrollChangedListener.getScrollPercentRate(percentRate); } } public boolean isScrolledToTop() { return isScrolledToTop; } public boolean isScrolledToBottom() { return isScrolledToBottom; } }
WebView的时候:
public class ScrollWebView extends WebView {
public OnScrollChangeListener listener;
public ScrollWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ScrollWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollWebView(Context context) {
super(context);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float webcontent = getContentHeight() * getScale();// webview的高度
float webnow = getHeight() + getScrollY();// 当前webview的高度
Log.i("TAG1", "webview.getScrollY()====>>" + getScrollY());
if (Math.abs(webcontent - webnow) < 1) {
// 已经处于底端
// Log.i("TAG1", "已经处于底端");
listener.onPageEnd(l, t, oldl, oldt);
} else if (getScrollY() == 0) {
// Log.i("TAG1", "已经处于顶端");
listener.onPageTop(l, t, oldl, oldt);
} else {
listener.onScrollChanged(l, t, oldl, oldt);
}
}
public void setOnScrollChangeListener(OnScrollChangeListener listener) {
this.listener = listener;
}
public interface OnScrollChangeListener {
public void onPageEnd(int l, int t, int oldl, int oldt);
public void onPageTop(int l, int t, int oldl, int oldt);
public void onScrollChanged(int l, int t, int oldl, int oldt);
}
}