滚动监听器设置为listView.onScrollListener(this)后,onScroll方法在没有触摸的情况下被调用。

36

当我为我的ListView设置onScrollListener时,它会调用onScroll方法。由于某些东西尚未初始化,这导致程序崩溃。

这种情况正常吗?注意:这是在我没有触摸手机的情况下发生的。

public class MainActivity1 extends Activity implements OnClickListener, OnScrollListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout1);

    ListView lv = (ListView)findViewById(R.id.listview1);
    lv.setOnScrollListener(this);
    ...
}
...
public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount){
    if( firstVisibleItem+visibleItemCount == totalItemCount ){
        pullContactList();
    }
}

有些问题。onCreate是从活动中调用的第一个方法。 - stinepike
我的错,实际上它在onCreate调用setOnScrollListner时被调用。这正常吗? - Siavash
什么没有被初始化?pullContactList()函数中的内容? - LuxuryMode
是的。还有我的适配器。我知道我可以在oncreate的末尾调用setonscrolllistner。或者在onscroll中使用一个变量,在第一次调用时跳过其内容。但这些都只是临时措施。有没有办法强制setonscrolllistner不调用onscroll?我需要重写构造函数吗? - Siavash
最佳答案:https://dev59.com/MXA75IYBdhLWcg3wv7wj#6357973 - Pratik Butani
5个回答

59

提醒一下,根据 javadoc 的说明:

MotionEvent.ACTION_SCROLL

该操作始终传递给指针下的窗口或视图,该指针可能不是当前触摸的窗口或视图。

此操作不是一个触摸事件,因此会被传递到 onGenericMotionEvent(MotionEvent) 而不是 onTouchEvent(MotionEvent) 中。

因此,motionEvent.getAction() 永远无法获取滚动事件。检查 MOVE 将可以完成工作。

您也可以在 OnScrollListeneronScrollStateChanged 方法中执行类似的操作。

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){
            userScrolled = true;
        }   
}

有时候onScrollStateChanged()方法不会被调用,例如当调用adapter.notificationDataSetChange()方法时,它会跳过这个方法并进入onScrollStateChanged()方法。因此,这个方法不太适合。 - Carl
我知道这是一个旧答案,但我刚刚遇到了这个问题。我阅读了AbsListView的源代码后理解了它发生的原因,但我不明白为什么会发生这种情况。能否有人启示我一下? - Darwind
如果您编辑您的答案,使其成为独立的答案,我将接受它作为最佳答案。目前它似乎是其他顶级答案的附录。 - Siavash

23

这是正常的,因为AbsListViewListView的超类)中setOnScrollListener的源代码进行了如下操作:

 public void setOnScrollListener(OnScrollListener l) {
        mOnScrollListener = l;
        invokeOnItemScrollListener();
    }

invokeOnItemScrollListener 则执行以下操作:

/**
     * Notify our scroll listener (if there is one) of a change in scroll state
*/
    void invokeOnItemScrollListener() {
        if (mFastScroller != null) {
            mFastScroller.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
        }
        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
        }
        onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
    }

根据您所要做的事情,有许多方法可以避免这个问题。

编辑:

既然您只想在用户实际滚动时执行此操作,那么您可以尝试以下方式:

    lv.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    if(view == lv && motionEvent.getAction() == MotionEvent.ACTION_SCROLL) {
                      userScrolled = true;
    }
return false;
                }
            });

那么...

lv.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount){
        if(userScrolled && firstVisibleItem+visibleItemCount == totalItemCount ){
            pullContactList();
        }
    }

});

我只想让onScroll在第一次不被调用,我希望它只在用户滚动时被调用。谢谢。(我知道我可以将setonscrolllistner移动到代码后面,但我想学习如何以另一种方式实现) - Siavash
2
为了完整性:你在onTouch监听器中忘记了一个return语句。 - Geert Bellemans
2
我简直不敢相信文档中没有提到这种行为。 - blahdiblah
它会导致电池泄漏吗?我的意思是这正常吗? - VSB
下面的答案简单而正确。不知道为什么OP选择了这个。 - Sufian
当设置onScrollListener时,提到Android在后台做了什么。 - vida

3

我使用这个解决方案,对我来说效果很好:

public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                canScroll = false;
            } else if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING ||
                    scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
                canScroll = true;

            }
        }

1

对我有用的解决方案!!!结合上面的答案,我做出了解决方案!!感谢@LuxuryMode和@CrazyGreenHand

我的TouchListener:由于MotionEvent.ACTION_SCROLL没有触发,所以我使用了MOVE动作

 expandableListView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            if(view == expandableListView && motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
                userScrolled = true;
            }
            return false;
        }
    });

我的滚动监听器:

  expandableListView.setOnScrollListener(new AbsListView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(AbsListView absListView, int i) {

        }

        @Override
        public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (firstVisibleItem == 0){
                swipeRefreshLayout.setEnabled(true);
            }else {
                swipeRefreshLayout.setEnabled(false);
            }
            int lastVisibleItem = absListView.getLastVisiblePosition();
            if (userScrolled&&!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
                onLoadMore();
                isLoading = true;
            }
            Log.d(TAG, "onScroll: firstVisibleItem=>"+firstVisibleItem+"==>visibleItemCount=>"+visibleItemCount+"==>totalItemCount==>"+totalItemCount+"==>lastVisibleItem==>"+lastVisibleItem);
        }
    });

0

只有当 visibleItemCount > 0 时,您才能执行必要的任务;

public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount){
    if (visibleItemCount > 0 ){
        //perform the task to be done
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接