ListView滚动方向和滚动位置的探索
来源:互联网 发布:淘宝卖假货举报中心 编辑:程序博客网 时间:2024/06/05 20:06
在开发的过程中,有时候会需要知道可以滚动的视图当前的滚动方向、是否滚动到顶部或底部等信息,ScrollView因为在新API中才加入了滚动回调接口,在之前都是之定义回调接口,通过onScrollChanged方法来回调,相比之下,ListView就要简单得多,因为其自带滚动回调接口。
但是往往有时候,系统的API返回的数据不能满足我们的需求,比如下面这种效果:
1.内容向上滚动时,滚动到底部时按钮显示,其他状态则隐藏
2.内容向下滚动时,按钮显示
那么问题就来了,如何知道ListView是在向上或向下滚动,如何知道ListView是否滚动到了顶部或底部。网上有很多判断ListView是否滚动到顶部或底部的方法,但是仅凭firstVisibleItem或LastVisibleItem判断是不准确的,因为“看见了并不代表显示完全”。当然,还有人认为可以用定时器去获取getScrollY来判断,不过这个值永远是0。
首先要解决的是getScrollY,如果了解ListView的两个方法或许下面就好理解的多:getCount、getChildCount。我们只需要知道Listview当前“所有可见”item的第一个的顶部坐标,即scrollY,这个是用来判断ListView是否有必要继续回调滚动状态,获取方式如下:
/** * scrollY * * @return scrollY */ private int getFirstViewScrollY() { View c = scrollView.getChildAt(0);//第一个可见的view if (c == null) { return 0; } int top = c.getTop() + scrollView.getPaddingTop(); return -top; }
其次是获取Listview当前的位置,是在顶部、底部或其他位置。这个就需要综合getLastVisiblePosition和getFirstVisiblePosition来判断了:
/** * 判断当前滚动内容的位置 * * @return */ private int getPosition() { //滑动到底部,最后可见的item为list最后一个数据,且自后一个item已完全显示,底部padding也完全显示 if (scrollView.getLastVisiblePosition() == scrollView.getCount() - 1 && scrollView.getChildAt(scrollView.getChildCount() - 1).getBottom() + scrollView.getPaddingBottom() == scrollView.getBottom()) { return OnScrollCallback.SCROLL_POSITION_BOTTOM; } //滑动到顶部 else if (scrollView.getFirstVisiblePosition() == 0 && scrollView.getChildAt(0).getTop() == scrollView.getPaddingTop()) { return OnScrollCallback.SCROLL_POSITION_TOP; } //其他 else { return OnScrollCallback.SCROLL_POSITION_OTHER; } }
都有注释,我就不在赘述了。
最后,获取到的值要怎么用呢?在Listview的OnScrollListener的两个方法里去回调滚动位置、滚动状态和滚动方向即可:
/** * 设置scroll回调 */ private void setUpScroll() { scrollView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (null == callback) { return; } switch (scrollState) { //滑动停止 case AbsListView.OnScrollListener.SCROLL_STATE_IDLE: int position = getPosition(); lastScrollY = getFirstViewScrollY(); SLog.d("position : " + position + " lastScrollY : " + lastScrollY); callback.onScrollChanged(OnScrollCallback.STATE_STOPPED, OnScrollCallback.SCROLL_DIRECTION_NOTHING, position); break; //手指在滑动 case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: fromTouch = true; break; //手指移开 case AbsListView.OnScrollListener.SCROLL_STATE_FLING: break; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (!fromTouch) { return; } if (null == callback) { return; } final int tempY = getFirstViewScrollY(); if (lastScrollY == tempY) { return; } SLog.d("scrollY : " + tempY + " lastScrollY :" + lastScrollY); lastScrollY = tempY; View childAt = scrollView.getChildAt(0); int[] location = new int[2]; childAt.getLocationOnScreen(location); SLog.d("firstVisibleItem= " + firstVisibleItem + " , y=" + location[1]); int direction = OnScrollCallback.SCROLL_DIRECTION_NOTHING; int state = OnScrollCallback.STATE_SCROLLING; if (firstVisibleItem != lastVisibleItem) { if (firstVisibleItem > lastVisibleItem) { SLog.d("向上滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_UP; } else if (firstVisibleItem < lastVisibleItem) { SLog.d("向下滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_DOWN; } lastVisibleItem = firstVisibleItem; mTouchY = location[1]; } else { if (mTouchY > location[1]) { SLog.d("->向上滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_UP; } else if (mTouchY < location[1]) { SLog.d("->向下滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_DOWN; } else { SLog.d("->未滑动"); state = OnScrollCallback.STATE_STOPPED; } mTouchY = location[1]; } callback.onScrollChanged(state, direction, getPosition()); } }); }
上面方法里要说的就是 getLocationOnScreen(int[2]),这是View自带的方法,获取当前视图锚点在屏幕中的绝对位置。还有就是if|else判断,当firstVisibleItem 发生变化时,直接通过变化的差值来判断滚动的方向:大于0向上滑动,小于0向下滑动;当firstVisibleItem 未发生变化时,通过获取Listview的第一个item位置的变化来判断滚动的方向和状态:如果新位置小于原位置(mTouchY > location[1])向上滑动,反之则向下滑动,如果位置相等则未滑动。
再来看看滚动回调接口的定义:
public interface OnScrollCallback { int STATE_SCROLLING = 1; int STATE_STOPPED = 2; int SCROLL_DIRECTION_NOTHING = 0; int SCROLL_DIRECTION_UP = 1; int SCROLL_DIRECTION_DOWN = 2; int SCROLL_POSITION_TOP = 1; int SCROLL_POSITION_BOTTOM = 2; int SCROLL_POSITION_OTHER = 0; void onScrollChanged(int state, int direction, int position); }
最后,我把它整理成了一个帮助类,希望对你有帮助。
package com.ykbjson.demo.customview.otherview;import android.view.View;import android.widget.AbsListView;import com.ykbjson.demo.tools.SLog;/** * 包名:com.ykbjson.demo.customview.otherview * 描述:设置AbsListView有滚动回调 * 创建者:yankebin * 日期:2016/5/20 */public class AbsListViewCompat<T extends AbsListView> { public interface OnScrollCallback { int STATE_SCROLLING = 1; int STATE_STOPPED = 2; int SCROLL_DIRECTION_NOTHING = 0; int SCROLL_DIRECTION_UP = 1; int SCROLL_DIRECTION_DOWN = 2; int SCROLL_POSITION_TOP = 1; int SCROLL_POSITION_BOTTOM = 2; int SCROLL_POSITION_OTHER = 0; void onScrollChanged(int state, int direction, int position); } /** * 滚动回调接口 */ private OnScrollCallback callback; /** * 最后一次滚动值 */ private int lastScrollY; /** * 滚动视图最后一个可见的item */ private int lastVisibleItem; /** * 手指在屏幕的y值 */ private int mTouchY; /** * 滚动视图 */ private T scrollView; /** * 是否是手动滑动,排除setselection */ private boolean fromTouch; /** * 设置需要滚动的view * * @param scrollView * @return */ public AbsListViewCompat setScrollView(T scrollView) { if (null == scrollView) { return this; } this.scrollView = scrollView; setUpScroll(); return this; } /** * 获取当前滚动的view * * @return */ public T getScrollView() { return scrollView; } public AbsListViewCompat setOnScrollCallback(OnScrollCallback callback) { this.callback = callback; return this; } /** * 设置scroll回调 */ private void setUpScroll() { scrollView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (null == callback) { return; } switch (scrollState) { //滑动停止 case AbsListView.OnScrollListener.SCROLL_STATE_IDLE: int position = getPosition(); lastScrollY = getFirstViewScrollY(); SLog.d("position : " + position + " lastScrollY : " + lastScrollY); callback.onScrollChanged(OnScrollCallback.STATE_STOPPED, OnScrollCallback.SCROLL_DIRECTION_NOTHING, position); break; //手指在滑动 case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: fromTouch = true; break; //手指移开 case AbsListView.OnScrollListener.SCROLL_STATE_FLING: break; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (!fromTouch) { return; } if (null == callback) { return; } final int tempY = getFirstViewScrollY(); if (lastScrollY == tempY) { return; } SLog.d("scrollY : " + tempY + " lastScrollY :" + lastScrollY); lastScrollY = tempY; View childAt = scrollView.getChildAt(0); int[] location = new int[2]; childAt.getLocationOnScreen(location); SLog.d("firstVisibleItem= " + firstVisibleItem + " , y=" + location[1]); int direction = OnScrollCallback.SCROLL_DIRECTION_NOTHING; int state = OnScrollCallback.STATE_SCROLLING; if (firstVisibleItem != lastVisibleItem) { if (firstVisibleItem > lastVisibleItem) { SLog.d("向上滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_UP; } else if (firstVisibleItem < lastVisibleItem) { SLog.d("向下滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_DOWN; } lastVisibleItem = firstVisibleItem; mTouchY = location[1]; } else { if (mTouchY > location[1]) { SLog.d("->向上滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_UP; } else if (mTouchY < location[1]) { SLog.d("->向下滑动"); direction = OnScrollCallback.SCROLL_DIRECTION_DOWN; } else { SLog.d("->未滑动"); state = OnScrollCallback.STATE_STOPPED; } mTouchY = location[1]; } callback.onScrollChanged(state, direction, getPosition()); } }); } /** * scrollY * * @return scrollY */ private int getFirstViewScrollY() { View c = scrollView.getChildAt(0);//第一个可见的view if (c == null) { return 0; } int top = c.getTop() + scrollView.getPaddingTop(); return -top; } /** * 判断当前滚动内容的位置 * * @return */ private int getPosition() { //滑动到底部,最后可见的item为list最后一个数据,且自后一个item已完全显示,底部padding也完全显示 if (scrollView.getLastVisiblePosition() == scrollView.getCount() - 1 && scrollView.getChildAt(scrollView.getChildCount() - 1).getBottom() + scrollView.getPaddingBottom() == scrollView.getBottom()) { return OnScrollCallback.SCROLL_POSITION_BOTTOM; } //滑动到顶部 else if (scrollView.getFirstVisiblePosition() == 0 && scrollView.getChildAt(0).getTop() == scrollView.getPaddingTop()) { return OnScrollCallback.SCROLL_POSITION_TOP; } //其他 else { return OnScrollCallback.SCROLL_POSITION_OTHER; } }}
- ListView滚动方向和滚动位置的探索
- 记录和恢复ListView滚动的位置
- 记录和恢复listView的滚动位置
- 水平方向滚动的listview
- listView 和scrollView滚动位置
- Android 如何判断ListView的滚动方向
- Android 记录和恢复ListView滚动的位置的方法
- android记录和恢复ListView滚动的位置
- android记录和恢复ListView滚动的位置
- android 设置listview滚动条的位置,颜色和宽度
- 记录和恢复listView滚动位置的2种方法
- ListView 滚动条位置
- ListView滚动指定位置
- 记录和恢复 ListView 滚动位置
- ListView滚动位置的记录回复
- 获取listView滚动时候的位置
- listview记录当前滚动位置和还原位置
- iOS开发 - 渐变导航条升级版(判断滚动的方向和改变方向时的位置)
- javaNIO学习笔记之缓冲区Buffer
- charles使用教程指南
- React Native跨平台代码说明
- 简单动画
- Android Studio中如何使用Git和Github来管理项目
- ListView滚动方向和滚动位置的探索
- 【Linux入门学习之】vi/vim编辑器必知必会
- ListView解决嵌套冲突3种办法
- ExecutorService——shutdown方法和awaitTermination方法
- Uinty利用XML来本地保存场景中的数据
- 只知编程
- AndroidStudio的一些实用技巧
- 【SHELL】一个非常有趣的问题
- MySql表结构修改详解