11

I'm running into a bit of a problem. What I'm doing: I've got a ListView which has got some images in it. To make the scrolling smoother I've disabled the images to show up when scrolling. Now there seems to be a bug in Android which sometimes causes the scroll state to not change back from SCROLL_STATE_FLING back to SCROLL_STATE_IDLE, which causes my images to not show up again.

My first thought was to set an onTouchListener and check when I get ACTION_UP, but that doesn't help because the SCROLL_STATE_FLING state is obviously being set after that. So now I've thought I could start a timer when the SCROLL_STATE_FLING state is being set and check after some time if the state is still in fling mode and then invalidate my view. But I don't think that's a very good solution.

Does anyone have a better idea on how I could do that? I've seen this reply but I need a solution for API level < 9 (plus it also sometimes happen when it's not overscrolling)

Here's my code for that:

    mList.setOnScrollListener(new OnScrollListener() {

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            mListAdapter.setIsScrolling(scrollState != SCROLL_STATE_IDLE);
            Log.i(this, "scrollStateChanged" + scrollState);
            if (scrollState == SCROLL_STATE_IDLE) {
                mList.invalidateViews();
            }
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        }
    });

Thanks, Maria

Community
  • 1
  • 1
Maria Neumayer
  • 3,299
  • 3
  • 25
  • 43

3 Answers3

18

I had the same problem, so my solution was to just detect if the scrollview position has reached the last page and in that case always load the images regardless of the scroll state (since the problem seems to always occur when the user flings to the end of the listview). So modifying your code you would have:

mList.setOnScrollListener(new OnScrollListener() {

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mListAdapter.setIsScrolling(scrollState != SCROLL_STATE_IDLE);
        Log.i(this, "scrollStateChanged" + scrollState);

        int first = view.getFirstVisiblePosition();
        int count = view.getChildCount();

        if (scrollState == SCROLL_STATE_IDLE || (first + count > mListAdapter.getCount()) ) {
            mList.invalidateViews();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    }
});
Jason Burke
  • 206
  • 2
  • 6
  • 1
    You're welcome. I figured out that its when you hit an end of the listview while your finger is still touching the screen. So this could happen at the top of the listview as well. The code I have could be modified to also always load the first page as well. – Jason Burke Mar 05 '12 at 02:59
  • yes it was also happening on the top but that's an easy change. – Maria Neumayer Mar 05 '12 at 11:33
  • @MariaNeumayer What would be this easy change? – S. ten Brinke Mar 26 '18 at 08:05
1

I ran into the same issue with a RecyclerView.

Jason Burke's answer is correct, but I'll provide an alternate way of checking if you are at the beginning/end of the RecyclerView/ListView.

I do it in onScrolled instead of onScrollStateChanged since I don't trust that I get a state changed event in the case when you scroll/fling to the edge of the RecyclerView.

recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {

    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)

        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            updateUi()
        }
    }

    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        /* If the user scrolls to the edges of the recyclerview, we can't trust that we get the SCROLL_STATE_IDLE state.
         * Therefore we have to update the view here in onScrolled for these cases
         */
        if (!recyclerView.canScrollVertically(1) || !recyclerView.canScrollVertically(-1)) {
            updateUi()
        }
    }
}

In my case it was actually a horizontal RecyclerView, so I had to do this instead:

if (!recyclerView.canScrollHorizontally(1) || !recyclerView.canScrollHorizontally(-1)) {
    updateUi()
}
Anders Ullnæss
  • 441
  • 4
  • 5
1

I have had this same problem and posted a workaround on the bug list:

For anybody still running into this problem (as I was last week) a workaround that works for me is the following: If android SDKInt == 7 set a onTouchListener on the (Abs)ListView

In that onTouchListener when the OnTouch event action is MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL you force a onScrollStateChanged with first a SCROLL_STATE_FLING and then a SCROLL_STATE_IDLE

Code example: In the onCreate:

  if(androidSDKInt <= 7){
      listViewDateSelector.setOnTouchListener(new FingerTracker(onScrollListener));       }

Then add a private class with:

  private class FingerTracker implements View.OnTouchListener {
      private OnScrollListener myOnScrollListener;

      public FingerTracker(OnScrollListener onScrollListener){
          myOnScrollListener = onScrollListener;          }

      public boolean onTouch(View view, MotionEvent event) {
          final int action = event.getAction();
          boolean mFingerUp = action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL;
          if (mFingerUp) {
              myOnScrollListener.onScrollStateChanged((AbsListView) view, OnScrollListener.SCROLL_STATE_FLING);
              myOnScrollListener.onScrollStateChanged((AbsListView) view, OnScrollListener.SCROLL_STATE_IDLE);
          }
          return false;           }       }
Pratik Butani
  • 51,868
  • 51
  • 228
  • 375
Kasium
  • 945
  • 10
  • 24
  • 1
    But that would cause the idle state to be set while it might still be flinging, correct? – Maria Neumayer Feb 17 '12 at 17:02
  • True. Hadn't thought about that. It will call the idle method also while flinging... This is no problem for my implementation as the flinging will still continue, and when that finishes it will automatically reset the idle state, but I can imagine that this might not work for everybody. – Kasium Feb 21 '12 at 13:42