Android ScrollView监听滑动到顶部和底部,虽然网上很多资料都有说,但是不全,而且有些细节没说清楚


使用场景:

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,可以直接拷贝就可以使用

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

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方法监听到滑动到底部

  

感悟:

  很多细小的知识,我们平时总是因为开发的时候太忙来不及去深究,但是作为开发者我们还是要对技术保持严谨,需要通过自己的实战形成自己的经验,有些很细小的知识可能在关键时候起到意向不到的作用,如果平时注意积累,到时候会事半功倍。

  文章写得不专业,如果读者觉得有用,记得点赞,也欢迎指正。

Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)的更多相关文章

  1. ScrollView监听滑动到顶部和底部的方法

    不需要监听滑动位置,只需要重写ScrollView的onOverScrolled和stopNestedScroll方法就可以了 public class ReadScrollView extends ...

  2. Android: ScrollView监听滑动到顶端和底端

    在项目中需要监听ScrollView滑动到顶端和底端的时候以实现自己的ScrollView,那么怎样去监听呢?今天查看了一下ScrollView的源码,找到了一种方法.先看一下源码中的overScro ...

  3. Android实战简易教程-第四十九枪(两种方式实现网络图片异步加载)

    加载图片属于比较耗时的工作,我们需要异步进行加载,异步加载有两种方式:1.通过AsyncTask类进行:2.通过Handler来实现,下面我们就来看一下如何通过这两种方式实现网络图片的异步加载. 一. ...

  4. java 监听文件或者文件夹变化的几种方式

    1.log4j的实现的文件内容变化监听 package com.jp.filemonitor; import org.apache.log4j.helpers.FileWatchdog; public ...

  5. 【转载】java 监听文件或者文件夹变化的几种方式

    1.log4j的实现的文件内容变化监听 package com.jp.filemonitor; import org.apache.log4j.helpers.FileWatchdog; public ...

  6. android listview判断是否滑动到顶部还是底部

    listView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStat ...

  7. Android中EditText显示明文与密码的两种方式

    效果图如下所述: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:and ...

  8. Android来电监听和去电监听

    我觉得写文章就得写得有用一些的,必须要有自己的思想,关于来电去电监听将按照下面三个问题展开 1.监听来电去电有什么用? 2.怎么监听,来电去电监听方式一样吗? 3.实战,有什么需要特别注意地方? 监听 ...

  9. android去掉滑动到顶部和底部的阴影

    android去掉滑动到顶部和底部的阴影 <ListView android:id="@+id/listView" android:layout_width="ma ...

随机推荐

  1. C# 中参数验证方式的演变

    一般在写方法的时候,第一步就是进行参数验证,这也体现了编码者的细心和缜密,但是在很多时候这个过程很枯燥和乏味,比如在拿到一个API设计文档的时候,通常会规定类型参数是否允许为空,如果是字符可能有长度限 ...

  2. 算法与数据结构(十五) 归并排序(Swift 3.0版)

    上篇博客我们主要聊了堆排序的相关内容,本篇博客,我们就来聊一下归并排序的相关内容.归并排序主要用了分治法的思想,在归并排序中,将我们需要排序的数组进行拆分,将其拆分的足够小.当拆分的数组中只有一个元素 ...

  3. 计算机程序的思维逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读和写 不能随机读写,只能从头读到尾,且不能重复读,虽然通过缓冲可以实现部分重读,但 ...

  4. 年度巨献-WPF项目开发过程中WPF小知识点汇总(原创+摘抄)

    WPF中Style的使用 Styel在英文中解释为”样式“,在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,通过设置样式,使其WPF控件外观更加美化同时减少了 ...

  5. C++随笔:从Hello World 探秘CoreCLR的内部(1)

    紧接着上次的问题,上次的问题其实很简单,就是HelloWorld.exe运行失败,而本文的目的,就是成功调试HelloWorld这个控制台应用程序. 通过我的寻找,其实是一个名为TryRun的文件出了 ...

  6. Android如何制作漂亮的自适布局的键盘

    最近做了个自定义键盘,但面对不同分辨率的机型其中数字键盘不能根据界面大小自已铺满,但又不能每种机型都做一套吧,所以要做成自适应,那这里主讲思路. 这里最上面的titlebar高度固定,下面输入的金额高 ...

  7. 火星坐标、百度坐标、WGS-84坐标相互转换及墨卡托投影坐标转经纬度JavaScript版

    火星坐标 火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移得到的坐标,基本国内的电子地图.导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密得到的.火星坐标的真实名称应该是GCJ- ...

  8. JQuery实现表格的增加行和删除行

    利用JQuery实现datatables插件的增加和删除行操作 在学习过程中遇到了这个利用JQuery对表格行的增加和删除,特记录下来以供初学者参考. 下面是主要的代码: <meta http- ...

  9. BZOJ 3894: 文理分科 [最小割]

    3894: 文理分科 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 674  Solved: 392[Submit][Status][Discuss] ...

  10. 通过几个Hello World感受.NET Core全新的开发体验

    2016年6月27日,这是一个特殊的日子,微软全新的.NET开发平台.NET Core的RTM版本正式发布.我个人将.NET Core的核心特性归结为三点,它们的首字母组成一个非常好记的简称——COM ...