onScroll gets called when I set listView.onScrollListener(this), but without any touch
Asked Answered
L

5

36

When I set the onScrollListener for my ListView, it calls onScroll. This causes a crash because certain things haven't been initialized.

Is this normal? Note: this is happening without me even touching the phone.

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();
    }
}
Limp answered 18/4, 2013 at 2:7 Comment(5)
there is something wrong . oncreate is the first method which is called from an activityAlsoran
my bad, it actually gets called when onCreate calls setOnScrollListner. is this normal?Limp
what hasn't been initialized? The stuff in pullContactList()?Coming
Yes. And also my adapter. I know I can call setonscrolllistner at the end of my oncreate. Or use a variable in onscroll to skip over its contents on the first call to it. But these are all patches. Is there a way to force setonscrolllistner to not call onscroll? Do I need to over ride the constructor?Limp
Best one https://mcmap.net/q/127200/-android-listview-illegalstateexception-quot-the-content-of-the-adapter-has-changed-but-listview-did-not-receive-a-notification-quotScut
C
23

It's normal because the source code for setOnScrollListener in AbsListView (the superclass of ListView) does this:

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

and invokeOnItemScrollListener does this:

/**
     * 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.
    }

depending on what it is you're trying to do, there are a number of ways to avoid this problem.

EDIT:

Since you only want to do this if the user actually scrolled, I suppose you could do something like:

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

Then..

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

});
Coming answered 18/4, 2013 at 2:41 Comment(6)
I just want onScroll to not get called that first time, I want it to get called only when the user scrolls. Thanks. (i do realize I can move setonscrolllistner to later in the code, but I want to learn how to how to do it the other way)Limp
for completeness: you forgot a return statement in your onTouch listenerMisteach
I can't believe that behavior isn't mentioned in the documentation.Potence
Does it cause battery leakage? I mean is it normal?Lignocellulose
The answer below is simple and correct. No idea why the OP selected this one.Georgeta
thumbsup for mentioning what android is doing at the back when setting the onScrollListenerFurther
B
59

Just a reminder, according to the javadoc of

MotionEvent.ACTION_SCROLL :

This action is always delivered to the window or view under the pointer, which may not be the window or view currently touched.

This action is not a touch event so it is delivered to onGenericMotionEvent(MotionEvent) rather than onTouchEvent(MotionEvent).

Hence, motionEvent.getAction() will never gets the SCROLL event. Check for MOVE will do the job

You can also do the similar thing in the onScrollStateChanged method of the OnScrollListener

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL){
            userScrolled = true;
        }   
}
Borne answered 18/4, 2013 at 2:8 Comment(3)
hi sometime the method onScrollStateChanged() be not invoke,example when invoke method adapter.notificationDataSetChange(),it will be skip this method and into method onScrollStateChanged().so this method is not suitablySanctify
I know this an old answer, but I just stumbled upon this issue myself. I understand why it's happening as I read the sourcecode for the AbsListView, but I don't understand why it should happen. Could anyone enlighten me?Bareback
If you edit your answer such that it is a standalone answer I will accept it as best answer. Currently it seems to be an appendix to the other top answer.Limp
C
23

It's normal because the source code for setOnScrollListener in AbsListView (the superclass of ListView) does this:

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

and invokeOnItemScrollListener does this:

/**
     * 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.
    }

depending on what it is you're trying to do, there are a number of ways to avoid this problem.

EDIT:

Since you only want to do this if the user actually scrolled, I suppose you could do something like:

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

Then..

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

});
Coming answered 18/4, 2013 at 2:41 Comment(6)
I just want onScroll to not get called that first time, I want it to get called only when the user scrolls. Thanks. (i do realize I can move setonscrolllistner to later in the code, but I want to learn how to how to do it the other way)Limp
for completeness: you forgot a return statement in your onTouch listenerMisteach
I can't believe that behavior isn't mentioned in the documentation.Potence
Does it cause battery leakage? I mean is it normal?Lignocellulose
The answer below is simple and correct. No idea why the OP selected this one.Georgeta
thumbsup for mentioning what android is doing at the back when setting the onScrollListenerFurther
J
3

I use this solution and it works fine for me :

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;

            }
        }
Jerad answered 14/5, 2016 at 5:46 Comment(0)
M
1

Worked solution for me!!! combination of above answer i made the solution!! thanks to @LuxuryMode and @CrazyGreenHand

My TouchListener: since MotionEvent.ACTION_SCROLL not trigged i used MOVE action

 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;
        }
    });

My Scroll Listener:

  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);
        }
    });
Moorland answered 1/12, 2016 at 6:11 Comment(0)
E
0

You can do the needful task only when visibleItemCount > 0;

public void onScroll(AbsListView view, int firstVisibleItem,
        int visibleItemCount, int totalItemCount){
    if (visibleItemCount > 0 ){
        //perform the task to be done
    }
}
Extraterritoriality answered 14/1, 2015 at 7:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.