Detecting the scrolling direction in the adapter (up/down)
Asked Answered
F

11

21

I am trying to mimic the Google Plus application in my project, as it seems to be the reference now.

The listview effect when scrolling is really nice and I would like to do something similar.

I have started with the LayoutAnimationController http://android-er.blogspot.be/2009/10/listview-and-listactivity-layout.html

LayoutAnimationController controller 
   = AnimationUtils.loadLayoutAnimation(
     this, R.anim.list_layout_controller);
  getListView().setLayoutAnimation(controller);

and that seems bad, as not all the elements are animated:

So I ended up by using the getView of the adapter and using this:

        AnimationSet set = new AnimationSet(true);

        Animation animation = new AlphaAnimation(0.0f, 1.0f);
        animation.setDuration(800);
        set.addAnimation(animation);

        animation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, 1.0f,Animation.RELATIVE_TO_SELF, 0.0f
        );
        animation.setDuration(600);
        set.addAnimation(animation);

        row.startAnimation(set);

The result is awesome and I am really happy with it!

Unfortunately, it only works when I scroll from top to bottom of the list!

I want to make it work when scrolling on the other side, I need to change a little bit the TranslateAnimation.

So my question, is there a way to detect if I scroll upwards or downwards in my adapter?

Fascinate answered 24/8, 2012 at 18:41 Comment(3)
What's the "row" variable in your example?Bipinnate
You can see full solution in this post: #16791600Deli
another working solution https://mcmap.net/q/276235/-how-to-detect-if-a-listview-is-scrolling-up-or-down-in-androidTendency
R
6

This is the easiest and simplest method I came across. And it works like a charm.

view.addOnScrollListener(new View.OnScrollListener() {
                @Override
                public void onScrolled(@NonNull View view, int dx, int dy) {
                    if (dy > 0) {
                        //Scrolling down
                    } else if (dy < 0) {
                        //Scrolling up
                    }
                }
            });
Ragen answered 21/3, 2020 at 11:14 Comment(0)
L
23

Assign an OnScrollListener to your ListView. Create a flag which indicates whether the user is scrolling up or down. Set an appropriate value to the flag by checking if the current first visible item position equals to more or less than the previous first visible item position. Put that check inside onScrollStateChanged().

Sample code:

private int mLastFirstVisibleItem;
private boolean mIsScrollingUp;

public void onScrollStateChanged(AbsListView view, int scrollState) {
    final ListView lw = getListView();

    if (view.getId() == lw.getId()) {
        final int currentFirstVisibleItem = lw.getFirstVisiblePosition();

        if (currentFirstVisibleItem > mLastFirstVisibleItem) {
            mIsScrollingUp = false;
        } else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
            mIsScrollingUp = true;
        }

        mLastFirstVisibleItem = currentFirstVisibleItem;
    } 
}

Check if mIsScrollingUp is true or false in getView(), and assign the animations accordingly.

Livia answered 24/8, 2012 at 18:58 Comment(6)
I wrote my solution, but your seems quite correct too.. I wonder which one is "better", I think I need less code, but maybe using the position is more "dirty"Fascinate
@Waza_Be: Yeah, it is not much difference besides my answer adds more code. But anyway, if you, for example, extend and want to skip the animations if the user is flinging the ListView away, that would probably be most appropriate to control inside an OnScrollListener. :-)Livia
This doesn't really tell you if you are scrollING up or down, only if you have scrollED up or down because the first visible item doesn't change until after it isn't visible. In my situation, I actually need a constant callback as the list is scrollING.Puente
To add tho Christopher's comment. Here's a simple way to get more immediate feedback on whether you've scrolled. You can do it based on the position of the first child of the listview. it also works with grid views. listView.getChildAt(0).getTop()Shalloon
nice. although if the height if the list item is large this may not be the most elegant solution.Opium
@Christopher Perry: for the scrolliING direction, please take a look at my reply here: #9630372Equi
F
20

I ended up by doing this:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.i("",position+" - "+lastposition);

    if (position >= lastposition)
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, 1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);
    else
        animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF,
                0.0f, Animation.RELATIVE_TO_SELF, 0.0f,
                Animation.RELATIVE_TO_SELF, -1.0f,
                Animation.RELATIVE_TO_SELF, 0.0f);

    animation.setDuration(600);
    set.addAnimation(animation);

    row.startAnimation(set);

    lastposition = position;

}
Fascinate answered 24/8, 2012 at 19:21 Comment(2)
Sweet. This is a better solution.Sophist
Where does lastposition come from?Dinadinah
R
6

This is the easiest and simplest method I came across. And it works like a charm.

view.addOnScrollListener(new View.OnScrollListener() {
                @Override
                public void onScrolled(@NonNull View view, int dx, int dy) {
                    if (dy > 0) {
                        //Scrolling down
                    } else if (dy < 0) {
                        //Scrolling up
                    }
                }
            });
Ragen answered 21/3, 2020 at 11:14 Comment(0)
E
5

More complex solution (working with long items height in listview)

  1. Create custom listview

    public class ScrollDetectingListView extends ListView {
        public ScrollDetectingListView(Context context) {
            super(context);
        }
    
        public ScrollDetectingListView(Context context, AttributeSet attrs) {
            super(context,attrs);
        } 
    
        public ScrollDetectingListView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        //we need this protected method for scroll detection
        public int getVerticalScrollOffset() {
            return computeVerticalScrollOffset();
        }
    }
    
  2. Override onScroll

        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    
        private int mInitialScroll = 0;
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
    
        }
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            int scrolledOffset = listView.getVerticalScrollOffset();
            if (scrolledOffset!=mInitialScroll) {
                //if scroll position changed
                boolean scrollUp = (scrolledOffset - mInitialScroll) < 0;
                mInitialScroll = scrolledOffset;
            }
        }
        });
    
Enidenigma answered 29/8, 2014 at 8:45 Comment(2)
By far the best answer. This solves the problem when the Listview recycles its views making "listview.getChildAt(0).getTop()" unreliableAntifouling
listView.getVerticalScrollOffset() is not foundMillepede
L
5

The accepted answer doesn't really "detect" scrolling up or down. It won't work if the current visible item is really huge. Using onTouchListener is the way to go.

This is the code snippet I used:

listView.setOnTouchListener(new View.OnTouchListener() {
    float initialY, finalY;
    boolean isScrollingUp;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = MotionEventCompat.getActionMasked(event);

        switch(action) {
            case (MotionEvent.ACTION_DOWN):
                initialY = event.getY();
            case (MotionEvent.ACTION_UP):
                finalY = event.getY();

                if (initialY < finalY) {
                    Log.d(TAG, "Scrolling up");
                    isScrollingUp = true;
                } else if (initialY > finalY) {
                    Log.d(TAG, "Scrolling down");
                    isScrollingUp = false;
                }
            default:
        }

        if (isScrollingUp) {
            // do animation for scrolling up
        } else {
            // do animation for scrolling down
        }

        return false; // has to be false, or it will freeze the listView
    }
});
Lux answered 23/5, 2015 at 16:20 Comment(1)
Imagine the user scrolls up and down without detaching their "tool" from the screen, in that case, initial values won't be updated and you will get wrong results for isScrollingUp flag. You should always check current and previous points. Not final final and initial pointsRollandrollaway
K
4

Try this . I hope it helps you . Logic From @Gal Rom Answer .

lv.setOnScrollListener(new OnScrollListener() {
        private int mLastFirstVisibleItem;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

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

            if(mLastFirstVisibleItem<firstVisibleItem)
            {
                Log.i("SCROLLING DOWN","TRUE");
            }
            if(mLastFirstVisibleItem>firstVisibleItem)
            {
                Log.i("SCROLLING UP","TRUE");
            }
            mLastFirstVisibleItem=firstVisibleItem;

        }
    });
Kanaka answered 17/3, 2015 at 5:12 Comment(0)
S
2

Here's my approach: It gets you more immediate feedback on how much you've scrolled: OnScroll, you can just get the Top position of the first item in your list. It's a pretty reliable to get actual scroll position information immediately.

listView.getChildAt(0).getTop()
Shalloon answered 6/5, 2013 at 18:25 Comment(1)
In adapter's getView you can find the lisView as a parent paramater.Dinadinah
K
0

I've used this much simpler solution:

public class ScrollDetectingListView extends ListView

...

setOnScrollListener( new OnScrollListener() 
{

    private int mInitialScroll = 0;

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

        boolean scrollUp = scrolledOffset > mInitialScroll;
        mInitialScroll = scrolledOffset;
    }


    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {


    }

}
Killam answered 31/5, 2014 at 13:21 Comment(2)
What is computeVerticalScroll()?Explosive
A protected method in developer.android.com/reference/android/widget/AbsListView.htmlKillam
J
0
list.setOnScrollListener(new OnScrollListener() {
        int last_item;
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {

        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem,
                int visibleItemCount, int totalItemCount) {
            if(last_item<firstVisibleItem+visibleItemCount-1){
                System.out.println("List is scrolling upwards");
            }
            else if(last_item>firstVisibleItem+visibleItemCount-1){
                System.out.println("List is scrolling downwards");
            }
             last_item = firstVisibleItem+visibleItemCount-1;
        }
    });

Based on the position of the last visible item i decide whether Listview is going up or down.

Japeth answered 14/4, 2015 at 12:21 Comment(0)
F
0

General solution that doesn't rely on positions of views/etc. Just check the vertical scroll offset and compare it to the previous scroll offset. If the new value is greater than the old the user is scrolling down, and vice-versa.

// [START check vertical scroll direction]
int oldScrollOffset = 0;

listView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
        @Override
        public void onScrollChange(View view, int i, int i1, int i2, int i3) {

            Boolean scrollDirectionDown;

            int newScrollOffset = listView.computeVerticalScrollOffset();

            if (newScrollOffset > oldScrollOffset) {
                scrollDirectionDown = true;
            } else {
                scrollDirectionDown = false;
            }

            oldScrollOffset = newScrollOffset;

            if (scrollDirectionDown) {
                // Update accordingly for scrolling down
                Log.d(TAG, "scrolling down");
            } else {
                // Update accordingly for scrolling up
                Log.d(TAG, "scrolling up");
            }

    });
// [END check vertical scroll direction]
Fuselage answered 8/2, 2019 at 2:56 Comment(0)
N
0
view.setOnTouchListener(new View.OnTouchListener() {

        private long startClickTime;
        float y0 = 0;
        float y1 = 0;
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {

            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                y0 = motionEvent.getY();
                startClickTime = System.currentTimeMillis();

            } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                if (System.currentTimeMillis() - startClickTime < ViewConfiguration.getTapTimeout()) {

                    // Touch was a simple tap. Do whatever.

                } else {
                    y1 = motionEvent.getY();
                    // Touch was a not a simple tap.
                    if (y1 - y0 > 50) {
                        // this is down
                    } else if (y1 - y0 < 50) {
                        Log.d("daniY", "-");
                        // this is up
                    }
                }

            }

            return true;
        }

    });

this worked for me, and this will work on detecting the direction of scrolling on all views i think.

Natalianatalie answered 19/11, 2020 at 23:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.