ScrollView实现滚动到顶部和底部的判断和监听,消除滚动回弹效果

  最近项目中用到了ScrollView,需要实现判断ScrollView是否滚动到顶部和底部,以进行相应的数据采集,下文将记录本人在实现ScrollView是否滚动到顶部和底部这个需求的一些过程,希望对大家有参考意义,如有不正确之处,望大家多多指教。

一、原理

对于ScrollView的滑动操作,ScrollViewView)提供了两个很重要的方法:

1)getScrollY(): 滑动ScrollViewY轴(即垂直)方向上滑动的距离,个人觉得可以理解为ScrollView顶部已经滑出屏幕的距离;

2)onScrollChanged(int l, int t, int oldl, int oldt):当ScrollView滑动时,会触发该方法的执行,可以用来监听ScrollView的滑动变化;

同时,如果需要实现ScrollView滑动到顶部或底部的监听,需要找出ScrollView刚好滑动到顶部或底部时的临界值(或条件):

1、监听滑动到顶部:

getScrollY() == 0;

(注:此处不能设置为getScrollY() <= 0,下面会给出原因说明)

这个很好理解,当ScrollView滑动到顶部时,在Y轴(垂直)方向上的滑动距离getScrollY()值为0,即ScrollView顶部刚刚好没有滑出屏幕的距离;

2、监听滑动到底部:

View contentView = getChildAt(0);

contentView.getMeasuredHeight()== getScrollY() + getHeight();

(注:此处不能设置为<=,下面会给出原因说明)

由于ScrollView只能有一个(直接)子View,通过getChildAt(0)方法可以获取ScrollView的唯一子View对象,通过getMeasuredHeight()可以获取子View的测量高度,正如上面所说的,getScrollY()可以获取ScrollView顶部滑出屏幕的距离,getHeight()可以获取ScrollView的可见高度,如果contentView.getMeasuredHeight()== getScrollY() + getHeight()这个条件成立,即可以判断ScrollView滑动到底部了

二、遇到的问题及解决

1)底部回弹效果:

 

上图是在一次快速拖动页面滑动到底部时的ScrollView控件getScrollY()值的输出,其中红色的输出值是ScrollView控件滑动刚到底部时的输出值,在ScrollView快速拖拽滑动到底部时,会存在回弹效果,会产生如下两种情况:

a.ScrollView的滑出屏幕距离scrollY值会先大于真正到底部时的值(如上图显示的是1944),然后在短时间内回弹到真正到底部时的距离值(如上图显示的是1944),像上文所说的,所以如果在监听滑动到底部时,设置的成立条件为contentView.getMeasuredHeight() <= getScrollY() + getHeight(),在短时间内会存在多次回调

b.ScrollView真正回弹到底部时会触发两次操作(如上面的两个红色箭头所示),可以通过Handler+标志位的方案过滤第一次回调操作,只监听第一次回调操作,下面会给出代码实现。

2)顶部回弹效果:

 

同时,如上图所示,在ScrollView快速拖拽滑动到顶部时,也会存在回弹效果,产生如下两种情况:

a.ScrollView的滑出屏幕距离scrollY值会先小于真正到顶部时的值(先出现负值),然后在短时间内回弹到真正到顶部时的距离值(如上图显示的是0),像上文所说的,所以如果在监听滑动到顶部时,设置的成立条件为getScrollY() <= 0,在短时间内会存在多次回调

b.ScrollView真正回弹到顶部时会触发两次操作(如上面的两个红色箭头所示),同样,也可以通过Handler+标志位的方案过滤第一次回调操作,只监听第一次回调操作,下面会给出代码实现

三、实现代码

1)自定义ScrollView,并在内部定义滑动到顶部和底部时的回调接口和方法

public class CustomScrollView extends ScrollView {

    //回调监听接口
    private OnScrollChangeListener mOnScrollChangeListener;
    //标识是否滑动到顶部
    private boolean isScrollToStart = false;
    //标识是否滑动到底部
    private boolean isScrollToEnd = false;
    private static final int CODE_TO_START = 0x001;
    private static final int CODE_TO_END = 0x002;
    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case CODE_TO_START:
                    //重置标志“滑动到顶部”时的标志位
                    isScrollToStart = false;
                    break;
                case CODE_TO_END:
                    //重置标志“滑动到底部”时的标志位
                    isScrollToEnd = false;
                    break;
                default:
                    break;
            }
        }
    };

    public CustomScrollView(Context context) {
        super(context);
    }

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (mOnScrollChangeListener != null) {
            Log.i("CustomScrollView", "scrollY:" + getScrollY());
            //滚动到顶部,ScrollView存在回弹效果效应(这里只会调用两次,如果用<=0,会多次触发)
            if (getScrollY() == 0) {
                //过滤操作,优化为一次调用
                if (!isScrollToStart) {
                    isScrollToStart = true;
                    mHandler.sendEmptyMessageDelayed(CODE_TO_START, 200);
                    Log.e("CustomScrollView", "toStart");
                    mOnScrollChangeListener.onScrollToStart();
                }
            } else {
                View contentView = getChildAt(0);
                if (contentView != null && contentView.getMeasuredHeight() == (getScrollY() + getHeight())) {
                    //滚动到底部,ScrollView存在回弹效果效应
                    //优化,只过滤第一次
                    if (!isScrollToEnd) {
                        isScrollToEnd = true;
                        mHandler.sendEmptyMessageDelayed(CODE_TO_END, 200);
                        Log.e("CustomScrollView", "toEnd,scrollY:" + getScrollY());
                        mOnScrollChangeListener.onScrollToEnd();
                    }

                }
            }
        }

    }

    //滑动监听接口
    public interface OnScrollChangeListener {

        //滑动到顶部时的回调
        void onScrollToStart();
        
        //滑动到底部时的回调
        void onScrollToEnd();
    }

    public void setOnScrollChangeListener(OnScrollChangeListener onScrollChangeListener) {
        mOnScrollChangeListener = onScrollChangeListener;
    }
}

2)在主界面实现自定义滑动监听接口,实现监听方法,获取自定义ScrollView实例,并设置相关接口即可:

public class MainActivity extends AppCompatActivity implements CustomScrollView.OnScrollChangeListener {

    private CustomScrollView scrollView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        scrollView = findViewById(R.id.scrollView);
        scrollView.setOnScrollChangeListener(this);
    }

    @Override
    public void onScrollToStart() {
        Toast.makeText(this, "滑动到顶部了", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onScrollToEnd() {
        Toast.makeText(this, "滑动到底部了", Toast.LENGTH_SHORT).show();
    }
}

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: Android中的ScrollView滚动监听可以通过设置OnScrollChangeListener来实现。具体步骤如下: 1. 在布局文件中添加ScrollView控件,并设置其id。 2. 在Java代码中获取ScrollView控件,并设置OnScrollChangeListener。 3. 在OnScrollChangeListener中实现滚动监听的逻辑。 示例代码如下: XML布局文件: <ScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 添加需要滚动的内容 --> </ScrollView> Java代码: ScrollView scrollView = findViewById(R.id.scrollView); scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() { @Override public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { // 在此处实现滚动监听的逻辑 } }); 在onScrollChange方法中,可以获取ScrollView滚动位置scrollX和scrollY,以及上一次的滚动位置oldScrollX和oldScrollY。通过这些参数,可以实现各种滚动监听的逻辑,例如:滚动底部加载更多数据、滚动顶部显示“回到顶部”按钮等。 ### 回答2: AndroidScrollView控件是一个可以放置多个子控件的视图容器,用于实现滚动的UI界面。在Android中,我们可以通过设置ScrollView滚动监听监听ScrollView控件的滚动状态,从而对UI界面进行动态的调整和处理。 ScrollView控件的滚动监听主要包括两个方面:滚动状态的监听滚动位置的监听滚动状态的监听可以通过设置ScrollView的setOnScrollChangeListener()方法来实现。该方法会在ScrollView滚动状态改变时被调用,并传递当前ScrollView滚动状态、滚动距离以及滚动速度等相关参数。我们可以根据这些参数来动态地调整UI界面的布局和显示效果。 例如,我们可以利用滚动状态的监听实现下拉刷新的效果:当用户下拉ScrollView时,我们可以通过监听ScrollView滚动状态来判断用户已经下拉到了一定的距离,并在这个状态下展示一个带有下拉箭头的刷新提示框。当用户松开手指,ScrollView返回到原来的位置时,我们可以通过滚动状态的监听判断滚动是否已经停止,并执行数据更新的操作。 滚动位置的监听可以通过重写ScrollView的onScrollChanged()方法来实现。该方法会在ScrollView滚动位置发生改变时被调用,并传递当前ScrollView相对于顶部的偏移量。我们可以根据这个偏移量来实现一些动态的效果,比如浮动的导航栏、滚动到一定位置后自动展开的菜单等。 总之,ScrollView滚动监听Android中是非常常用的功能,尤其是对于需要实现滚动界面的应用程序。通过设置滚动监听,我们可以实现一些非常丰富和动态的UI效果,让应用程序的用户体验更加流畅和便捷。 ### 回答3: Android ScrollView是一个常用的控件,用于在屏幕上展示超过屏幕高度的内容。随着用户向下或向上滑动,这些内容会随之滚动。 在某些情况下,我们希望在用户滚动过程中对ScrollView进行一些操作。这时,就需要使用ScrollView滚动监听功能。 ScrollView滚动监听可以通过setOnScrollChangeListener()方法来实现。该方法需要传入一个OnScrollChangeListener接口,该接口中包含一个onScrollChange()方法,可以在ScrollView滚动时被调用。下面是一个简单的示例: scrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() { @Override public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { // 在此处添加需要执行的操作,例如: if(scrollY > oldScrollY) { Log.d("ScrollView", "向下滚动"); } else { Log.d("ScrollView", "向上滚动"); } } }); 在onScrollChange()方法中,我们可以获取ScrollView当前的滚动位置(即scrollX和scrollY)。如果要执行某些需要在滚动过程中不断改变的操作,例如某个View的透明度或位置,我们可以在此处进行计算并实时更新View的状态。 除此之外,我们还可以通过ScrollView的getScrollX()和getScrollY()方法来获取ScrollView当前的滚动位置。此外,我们可以从onScrollChange()方法中获取被滚动的View对象(即ScrollView本身)。这些方法都可以在我们需要在滚动过程中对ScrollView进行控制时有很大帮助。 总体而言,ScrollView滚动监听功能为我们在ScrollView滚动过程中添加各种操作提供了便利。结合其他Android UI控件,我们可以创建出更灵活、更多样化且物美价廉的用户界面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值