您的位置:首页 > 移动开发 > Android开发

Android ScrollView监听滑动到顶部和底部的两种方式

2017-01-06 17:28 477 查看
转自http://www.cnblogs.com/popfisher/p/5709202.html





使用场景:

1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动到底部或者顶部

2. ScrollView滚动到顶部或者底部时主动触发一些操作(典型的就是滚动到底部触发自动加载操作)

两种方式:

1. onScrollChanged方式,自己计算

2. onOverScrolled使用系统计算的结果,api >= 9才支持

可能忽视的细节1:

如果是手势滑动,上面两种方式都对,但是如果是调用ScrollViewsmoothScrollToscrollTo方法来滚动的话,

只有onScrollChanged监听对,onOverScrolled监听不对,因为通过代码来滚动话是精确滚动,onOverScrolled方法没处理这种情况

两种方式如何选择?

一般来说如果系统有实现的东西,就用系统的,我们毕竟是基于Android系统来做开发,别人做的考虑很多适配兼容问题

所以原则就是系统有就用它的,没有才用我们自己的,具体实现仔细看代码,记得看注释

如果不考虑smoothScrollTo和scrollTo滚动,上面这个原则就是对的,如果要考虑的话,这里只能使用onScrollChanged

滚动到顶部和底部时对应的计算关系:



备注:无padding的情况可以转换为有padding的情况,即tp,bp=0

mScrollY + H – tp – bp = h ===> mScrollY + H = h

代码实现,自定义View,可以直接拷贝就可以使用

下面代码不考虑[b]smoothScrollTo和[b]scrollTo方法的影响,要考虑的话,去掉[b][b][b]onOverScrolled方法,去掉[b][b]onScrollChanged的api版本条件限制即可[/b][/b][/b][/b][/b][/b][/b]

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

/**
* 监听ScrollView滚动到顶部或者底部做相关事件拦截
*/
public class SmartScrollView extends ScrollView {

private boolean isScrolledToTop = true;  // 初始化的时候设置一下值
private boolean isScrolledToBottom = false;
public ScanScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}

private ISmartScrollChangedListener mSmartScrollChangedListener;

/** 定义监听接口 */
public interface ISmartScrollChangedListener {
void onScrolledToBottom();
void onScrolledToTop();
}

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;
} else {
isScrolledToTop = false;
isScrolledToBottom = clampedY;
}
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) {  // API 9及之后走onOverScrolled方法监听
if (getScrollY() == 0) {    // 小心踩坑1: 这里不能是getScrollY() <= 0
isScrolledToTop = true;
isScrolledToBottom = false;
} else if (getScrollY() + getHeight() - getPaddingTop()-getPaddingBottom() == getChildAt(0).getHeight()) {
// 小心踩坑2: 这里不能是 >=
         // 小心踩坑3(可能忽视的细节2):这里最容易忽视的就是ScrollView上下的padding 
isScrolledToBottom = true;
isScrolledToTop = false;
} else {
isScrolledToTop = false;
isScrolledToBottom = false;
}
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();
}
}
}

public boolean isScrolledToTop() {
return isScrolledToTop;
}

public boolean isScrolledToBottom() {
return isScrolledToBottom;
}
}


至此已经介绍完了,为了形象的表示smoothScrollTo和scrollTo方法调用时两种监听方式的结果下面加上log,这里不想看的可以不往下看了

@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;
 System.out.println("onOverScrolled isScrolledToTop:" + isScrolledToTop);
} else {
isScrolledToTop = false;
isScrolledToBottom = clampedY;
System.out.println("onOverScrolled isScrolledToBottom:" + isScrolledToBottom);
}
notifyScrollChangedListeners();
}

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
   // 这个log可以研究ScrollView的上下padding对结果的影响
System.out.println("onScrollChanged getScrollY():" + getScrollY() + " t: " + t + " paddingTop: " + getPaddingTop());
if (getScrollY() == 0) {
isScrolledToTop = true;
isScrolledToBottom = false;
System.out.println("onScrollChanged isScrolledToTop:" + isScrolledToTop);
} else if (getScrollY() + getHeight() - getPaddingTop() - getPaddingBottom() == getChildAt(0).getHeight()) {
isScrolledToBottom = true;
 System.out.println("onScrollChanged isScrolledToBottom:" + isScrolledToBottom);
isScrolledToTop = false;
} else {
isScrolledToTop = false;
isScrolledToBottom = false;
}
notifyScrollChangedListeners();
}


下面我们看下具体的执行结果:

1. 手动滑动到底部的情况--->两种方式都监听到了

  


2. 手动滑动到顶部的情况--->两种方式都监听到了

  


3. 调用smoothScrollTo(0, Integer.MAX_VALUE)或者scrollTo(0, Integer.MAX_VALUE)滑动到底部的情况

--->只有onScrollChanged方法监听到滑动到底部

  


4. 调用smoothScrollTo(0, 0)或者scrollTo(0, 0)滑动到顶部的情况

--->只有onScrollChanged方法监听到滑动到底部

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: