Android 我可以用onScrollListener查看滚动视图吗?
我正在布局中使用Android 我可以用onScrollListener查看滚动视图吗?,android,listener,scrollview,Android,Listener,Scrollview,我正在布局中使用水平滚动视图,我需要确定用户是否已到达滚动的起点和终点 对于ListView,我尝试了onscrollstener上的滚动,可以找到滚动的起点和终点 我尝试在我的滚动视图中执行相同的操作,但似乎不可能。是否有其他可能的方法来实现我所需要的。视图的每个实例都调用getViewTreeObserver()。现在,当持有一个ViewTreeObserver的实例时,您可以使用addOnScrollChangedListener()方法向其添加一个OnScrollChangedListe
水平滚动视图
,我需要确定用户是否已到达滚动的起点和终点
对于ListView
,我尝试了onscrollstener
上的滚动,可以找到滚动的起点和终点
我尝试在我的
滚动视图中执行相同的操作,但似乎不可能。是否有其他可能的方法来实现我所需要的。视图的每个实例都调用getViewTreeObserver()
。现在,当持有一个ViewTreeObserver
的实例时,您可以使用addOnScrollChangedListener()
方法向其添加一个OnScrollChangedListener()
您可以查看有关该类的更多信息
它可以让你知道每一个滚动事件,但没有坐标。您可以通过在侦听器中使用getScrollY()
或getScrollX()
来获取它们
scrollView.getViewTreeObserver().addOnScrollChangedListener(new OnScrollChangedListener() {
@Override
public void onScrollChanged() {
int scrollY = rootScrollView.getScrollY(); // For ScrollView
int scrollX = rootScrollView.getScrollX(); // For HorizontalScrollView
// DO SOMETHING WITH THE SCROLL COORDINATES
}
});
这是我编写的一个派生的HorizontalScrollView,用于处理有关滚动和滚动结束的通知。当用户停止主动滚动时,以及当用户放开后,它完全减速时,它会正确处理:
public class ObservableHorizontalScrollView extends HorizontalScrollView {
public interface OnScrollListener {
public void onScrollChanged(ObservableHorizontalScrollView scrollView, int x, int y, int oldX, int oldY);
public void onEndScroll(ObservableHorizontalScrollView scrollView);
}
private boolean mIsScrolling;
private boolean mIsTouching;
private Runnable mScrollingRunnable;
private OnScrollListener mOnScrollListener;
public ObservableHorizontalScrollView(Context context) {
this(context, null, 0);
}
public ObservableHorizontalScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ObservableHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
if (action == MotionEvent.ACTION_MOVE) {
mIsTouching = true;
mIsScrolling = true;
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mIsTouching && !mIsScrolling) {
if (mOnScrollListener != null) {
mOnScrollListener.onEndScroll(this);
}
}
mIsTouching = false;
}
return super.onTouchEvent(ev);
}
@Override
protected void onScrollChanged(int x, int y, int oldX, int oldY) {
super.onScrollChanged(x, y, oldX, oldY);
if (Math.abs(oldX - x) > 0) {
if (mScrollingRunnable != null) {
removeCallbacks(mScrollingRunnable);
}
mScrollingRunnable = new Runnable() {
public void run() {
if (mIsScrolling && !mIsTouching) {
if (mOnScrollListener != null) {
mOnScrollListener.onEndScroll(ObservableHorizontalScrollView.this);
}
}
mIsScrolling = false;
mScrollingRunnable = null;
}
};
postDelayed(mScrollingRunnable, 200);
}
if (mOnScrollListener != null) {
mOnScrollListener.onScrollChanged(this, x, y, oldX, oldY);
}
}
public OnScrollListener getOnScrollListener() {
return mOnScrollListener;
}
public void setOnScrollListener(OnScrollListener mOnEndScrollListener) {
this.mOnScrollListener = mOnEndScrollListener;
}
}
这可能非常有用。
使用NestedScrollView
而不是ScrollView
。支持库23.1将OnScrollChangeListener
引入到NestedScrollView
。
所以你可以这样做
myScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
Log.d("ScrollView","scrollX_"+scrollX+"_scrollY_"+scrollY+"_oldScrollX_"+oldScrollX+"_oldScrollY_"+oldScrollY);
//Do something
}
});
如果您想知道视图的滚动位置,则可以在view类上使用以下扩展功能:
fun View?.onScroll(callback: (x: Int, y: Int) -> Unit) {
var oldX = 0
var oldY = 0
this?.viewTreeObserver?.addOnScrollChangedListener {
if (oldX != scrollX || oldY != scrollY) {
callback(scrollX, scrollY)
oldX = scrollX
oldY = scrollY
}
}
}
您可以使用NestedScrollView
而不是ScrollView
。然而,当使用Kotlin Lambda时,它不会知道您想要的是NestedScrollView的setOnScrollChangeListener
,而不是视图中的一个(API级别23)。可以通过将第一个参数指定为NestedScrollView来解决此问题
nestedScrollView.setOnScrollChangeListener { _: NestedScrollView, scrollX: Int, scrollY: Int, _: Int, _: Int ->
Log.d("ScrollView", "Scrolled to $scrollX, $scrollY")
}
您可以定义一个自定义ScrollView类,并添加一个在滚动时调用的接口,如下所示:
public class ScrollChangeListenerScrollView extends HorizontalScrollView {
private MyScrollListener mMyScrollListener;
public ScrollChangeListenerScrollView(Context context) {
super(context);
}
public ScrollChangeListenerScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScrollChangeListenerScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setOnMyScrollListener(MyScrollListener myScrollListener){
this.mMyScrollListener = myScrollListener;
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(mMyScrollListener!=null){
mMyScrollListener.onScrollChange(this,l,t,oldl,oldt);
}
}
public interface MyScrollListener {
void onScrollChange(View view,int scrollX,int scrollY,int oldScrollX, int oldScrollY);
}
}
除了被接受的答案之外,您还需要保留一个侦听器的引用,并在不需要时将其删除。否则,您的ScrollView和内存泄漏(在accepted answer的注释中提到)将得到一个空指针异常
您可以在活动/片段中实现OnScrollChangedListener
MyFragment : ViewTreeObserver.OnScrollChangedListener
当视图准备就绪时,将其添加到scrollView
scrollView.viewTreeObserver.addOnScrollChangedListener(this)
不再需要时删除侦听器(即onPause())
这个答案应该是正确的<代码>hsv.getViewTreeObserver().addOnScrollChangedListener(新ViewTreeObserver.OnScrollChangedListener(){@Override public void onScrollChanged(){Log.i(标记,“scroll:+hsv.getScrollX());})其中hsv
是一个水平滚动视图
工作。我怀疑这同样适用于ScrollView。这是最好的答案。无需扩展水平滚动视图。刚刚用ScrollView测试过,效果很好:final ScrollView sv=(ScrollView)findViewById(R.id.scrollViewArticle);sv.getViewTreeObserver().addOnScrollChangedListener(新ViewTreeObserver.OnScrollChangedListener(){@Override public void onScrollChanged(){Log.d(“onScrollChanged Y”,String.valueOf(sv.getScrollY());});您应该首先检查答案中的示例代码是否存在内存泄漏。由于方法是add
而不是set
,因此所有侦听器都将保留,直到显式删除为止。因此,示例中用作侦听器实现的匿名类将泄漏(以及它引用的任何对象,即外部类)。这可能是一个愚蠢的问题,但什么是rootScrollView?这是可能的。请参阅user2695685的答案。简而言之,onStart
中的以下内容将起作用:hsv.getViewTreeObserver().addOnScrollChangedListener(新的ViewTreeObserver.OnScrollChangedListener(){@Override public void onScrollChanged(){Log.i(标记,“scroll:+hsv.getScrollX());})
在onStart()中,hsv
是一个水平滚动视图
可以工作。接受任何有用的答案..如果其他人发布你自己的答案..为什么在Android中用滚动视图检测滚动事件如此困难?这是胡说八道。这似乎比使用ViewTreeObserver方法要好。这仅仅是因为当您有一个活动,其中加载了多个带有ListView和ScrollView的片段(例如3个带有滑动选项卡的片段),并且您需要为特定视图注册事件。然而,如果ViewTreeObserver已注册,即使它不是活动视图,它也会触发事件。@ZaBlanc-您能告诉我如何从保存ScrollView的活动中初始化此类和侦听器吗?我已经很好地实现了它,但是监听器还不会返回任何值。这确实有效!而且不需要sdk>23:)这肯定比使用getViewTreeObserver()跟踪是否添加了侦听器更容易。谢谢但是如何使用NestedScrollView作为水平滚动视图呢?找不到任何资源如果我想使用水平滚动怎么办?@dazed'n'fuzzleNestedScrollView
是支持库的一部分,它的setOnScrollChangeListener
方法没有最低版本要求。不要与View
的setOnScrollChangeListener
混淆,后者需要API级别23。请尝试添加一些描述来支持您的代码,这将使每个人都理解此代码与另一代码非常相似,不是吗?这应该是公认的答案,因为使用viewTreeObserver的方法会导致内存泄漏,必须手动删除,否则将添加多个滚动观察者。我完全同意。切换到NestedScrollView
并将其OnScrollChangeListener
解决了我的问题。当我在ScrollView的ViewTreeObserver上设置OnScrollChangedListener
时,侦听器被无故调用了多次。
scrollView.viewTreeObserver.addOnScrollChangedListener(this)
scrollView.viewTreeObserver.removeOnScrollChangedListener(this)