I have the following requirement:
At first, data for page no: 2 is fetched from the server & the items
are populated in a ListView.
Considering that both the prev page & next page are available in a scenario, the following code has been added:
if(prevPageNo > 0){
mListViewActual.setOnScrollListener(this);
}
if(nextPageNo > 0){
mListViewActual.setOnScrollListener(this);
}
What conditions should I put to detect scroll up & scroll down on the following methods:
void onScroll(AbsListView view, int firstVisibleItem, int
visibleItemCount, int totalItemCount)
void onScrollStateChanged(AbsListView view, int scrollState)
After the action: scroll up & scroll down is detected , accordingly a service will be called with either the prev page no or next page no , to fetch the items to be populated in the Listview.
Any inputs will be helpful.
Gone through the following links but its not returning the correct scroll up / scroll down action:
link 1
link 2
try using the setOnScrollListener and implement the onScrollStateChanged with scrollState
setOnScrollListener(new OnScrollListener(){
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
final ListView lw = getListView();
if(scrollState == 0)
Log.i("a", "scrolling stopped...");
if (view.getId() == lw.getId()) {
final int currentFirstVisibleItem = lw.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
mIsScrollingUp = false;
Log.i("a", "scrolling down...");
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
mIsScrollingUp = true;
Log.i("a", "scrolling up...");
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
Here is a working modified version from some of the above-indicated solutions.
Add another class ListView:
package com.example.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
public class ListView extends android.widget.ListView {
private OnScrollListener onScrollListener;
private OnDetectScrollListener onDetectScrollListener;
public ListView(Context context) {
super(context);
onCreate(context, null, null);
}
public ListView(Context context, AttributeSet attrs) {
super(context, attrs);
onCreate(context, attrs, null);
}
public ListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
onCreate(context, attrs, defStyle);
}
#SuppressWarnings("UnusedParameters")
private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
setListeners();
}
private void setListeners() {
super.setOnScrollListener(new OnScrollListener() {
private int oldTop;
private int oldFirstVisibleItem;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if (onDetectScrollListener != null) {
onDetectedListScroll(view, firstVisibleItem);
}
}
private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
View view = absListView.getChildAt(0);
int top = (view == null) ? 0 : view.getTop();
if (firstVisibleItem == oldFirstVisibleItem) {
if (top > oldTop) {
onDetectScrollListener.onUpScrolling();
} else if (top < oldTop) {
onDetectScrollListener.onDownScrolling();
}
} else {
if (firstVisibleItem < oldFirstVisibleItem) {
onDetectScrollListener.onUpScrolling();
} else {
onDetectScrollListener.onDownScrolling();
}
}
oldTop = top;
oldFirstVisibleItem = firstVisibleItem;
}
});
}
#Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
this.onDetectScrollListener = onDetectScrollListener;
}
}
And an interface:
public interface OnDetectScrollListener {
void onUpScrolling();
void onDownScrolling();
}
And finally how to use:
com.example.view.ListView listView = (com.example.view.ListView) findViewById(R.id.list);
listView.setOnDetectScrollListener(new OnDetectScrollListener() {
#Override
public void onUpScrolling() {
/* do something */
}
#Override
public void onDownScrolling() {
/* do something */
}
});
In your XML layout:
<com.example.view.ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
This is my first topic, do not judge me harshly. =)
this is a simple implementation:
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;
}
});
and if you need more precision, you can use this custom ListView class:
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
/**
* Created by root on 26/05/15.
*/
public class ScrollInterfacedListView extends ListView {
private OnScrollListener onScrollListener;
private OnDetectScrollListener onDetectScrollListener;
public ScrollInterfacedListView(Context context) {
super(context);
onCreate(context, null, null);
}
public ScrollInterfacedListView(Context context, AttributeSet attrs) {
super(context, attrs);
onCreate(context, attrs, null);
}
public ScrollInterfacedListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
onCreate(context, attrs, defStyle);
}
#SuppressWarnings("UnusedParameters")
private void onCreate(Context context, AttributeSet attrs, Integer defStyle) {
setListeners();
}
private void setListeners() {
super.setOnScrollListener(new OnScrollListener() {
private int oldTop;
private int oldFirstVisibleItem;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
if (onDetectScrollListener != null) {
onDetectedListScroll(view, firstVisibleItem);
}
}
private void onDetectedListScroll(AbsListView absListView, int firstVisibleItem) {
View view = absListView.getChildAt(0);
int top = (view == null) ? 0 : view.getTop();
if (firstVisibleItem == oldFirstVisibleItem) {
if (top > oldTop) {
onDetectScrollListener.onUpScrolling();
} else if (top < oldTop) {
onDetectScrollListener.onDownScrolling();
}
} else {
if (firstVisibleItem < oldFirstVisibleItem) {
onDetectScrollListener.onUpScrolling();
} else {
onDetectScrollListener.onDownScrolling();
}
}
oldTop = top;
oldFirstVisibleItem = firstVisibleItem;
}
});
}
#Override
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public void setOnDetectScrollListener(OnDetectScrollListener onDetectScrollListener) {
this.onDetectScrollListener = onDetectScrollListener;
}
public interface OnDetectScrollListener {
void onUpScrolling();
void onDownScrolling();
}
}
an example for use:
(don't forget to add it as an Xml Tag in your layout.xml)
scrollInterfacedListView.setOnDetectScrollListener(new ScrollInterfacedListView.OnDetectScrollListener() {
#Override
public void onUpScrolling() {
//Do your thing
}
#Override
public void onDownScrolling() {
//Do your thing
}
});
With all the method posted, there are problems recognizing when the user is scrolling up from the first element or down from the last.
Here is another approach to detect scroll up/down:
listView.setOnTouchListener(new View.OnTouchListener() {
float height;
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
float height = event.getY();
if(action == MotionEvent.ACTION_DOWN){
this.height = height;
}else if(action == MotionEvent.ACTION_UP){
if(this.height < height){
Log.v(TAG, "Scrolled up");
}else if(this.height > height){
Log.v(TAG, "Scrolled down");
}
}
return false;
}
});
ListView listView = getListView();
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
view.setOnTouchListener(new OnTouchListener() {
private float mInitialX;
private float mInitialY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
final float x = event.getX();
final float y = event.getY();
final float yDiff = y - mInitialY;
if (yDiff > 0.0) {
Log.d(tag, "SCROLL DOWN");
scrollDown = true;
break;
} else if (yDiff < 0.0) {
Log.d(tag, "SCROLL up");
scrollDown = true;
break;
}
break;
}
return false;
}
});
My solution works perfectly giving the exact value for each scroll direction.
distanceFromFirstCellToTop contains the exact distance from the first cell to the top of the parent View. I save this value in previousDistanceFromFirstCellToTop and as I scroll I compare it with the new value. If it's lower then I scrolled up, else, I scrolled down.
private int previousDistanceFromFirstCellToTop;
listview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View firstCell = listview.getChildAt(0);
int distanceFromFirstCellToTop = listview.getFirstVisiblePosition() * firstCell.getHeight() - firstCell.getTop();
if(distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
{
//Scroll Up
}
else if(distanceFromFirstCellToTop > previousDistanceFromFirstCellToTop)
{
//Scroll Down
}
previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
}
});
For Xamarin developers, the solution is the following:
Note: don't forget to run on UI thread
listView.Scroll += (o, e) =>
{
View firstCell = listView.GetChildAt(0);
int distanceFromFirstCellToTop = listView.FirstVisiblePosition * firstCell.Height - firstCell.Top;
if (distanceFromFirstCellToTop < previousDistanceFromFirstCellToTop)
{
//Scroll Up
}
else if (distanceFromFirstCellToTop > previousDistanceFromFirstCellToTop)
{
//Scroll Down
}
previousDistanceFromFirstCellToTop = distanceFromFirstCellToTop;
};
Just set scroll listener to your listview.
If you have a header or footer you should check the visible count too. If it increases it means you are scrolling down. (Reverse it if there is a footer instead of header)
If you don't have any header or footer in your listview you can remove the lines which cheks the visible item count.
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mLastFirstVisibleItem > firstVisibleItem) {
Log.e(getClass().toString(), "scrolling up");
} else if (mLastFirstVisibleItem < firstVisibleItem) {
Log.e(getClass().toString(), "scrolling down");
} else if (mLastVisibleItemCount < visibleItemCount) {
Log.e(getClass().toString(), "scrolling down");
} else if (mLastVisibleItemCount > visibleItemCount) {
Log.e(getClass().toString(), "scrolling up");
}
mLastFirstVisibleItem = firstVisibleItem;
mLastVisibleItemCount = visibleItemCount;
}
public void onScrollStateChanged(AbsListView listView, int scrollState) {
}
});
and have this variables
int mLastFirstVisibleItem;
int mLastVisibleItemCount;
I've used this much simpler solution:
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) {
}
}
To also detect scrolling with larger elements, I prefere an onTouch Listener:
listview.setOnTouchListener(new View.OnTouchListener() {
int scrollEventListSize = 5;
float lastY;
// Used to correct for occasions when user scrolls down(/up) but the onTouchListener detects it incorrectly. We will store detected up-/down-scrolls with -1/1 in this list and evaluate later which occured more often
List<Integer> downScrolledEventsHappened;
#Override
public boolean onTouch(View v, MotionEvent event) {
float diff = 0;
if(event.getAction() == event.ACTION_DOWN){
lastY = event.getY();
downScrolledEventsHappened = new LinkedList<Integer>();
}
else if(event.getAction() == event.ACTION_MOVE){
diff = event.getY() - lastY;
lastY = event.getY();
if(diff>0)
downScrolledEventsHappened.add(1);
else
downScrolledEventsHappened.add(-1);
//List needs to be filled with some events, will happen very quickly
if(downScrolledEventsHappened.size() == scrollEventListSize+1){
downScrolledEventsHappened.remove(0);
int res=0;
for(int i=0; i<downScrolledEventsHappened.size(); i++){
res+=downScrolledEventsHappened.get(i);
}
if (res > 0)
Log.i("INFO", "Scrolled up");
else
Log.i("INFO", "Scrolled down");
}
}
return false; // don't interrupt the event-chain
}
});
Store the firstVisibleItem and on the next onScroll check if the new firstVisibleItem is smaller or greater than the previous one.
Example pseudocode (not tested):
int lastVisibleItem = 0;
boolean isScrollingDown = false;
void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem > lastVisibleItem) {
isScrollingDown = true;
}
else {
isScrollingDown = false;
}
lastVisibleItem = firstVisibleItem;
}
For some reason the Android doc doesnt cover this, and the method used isnt even in the docs... took me a while to find it.
To detect if your scroll is at the top you would use this.
public boolean checkAtTop()
{
if(listView.getChildCount() == 0) return true;
return listView.getChildAt(0).getTop() == 0;
}
This will check if your scroller is at the top. Now, in order to do it for the bottom, you would have to pass it the number of children that you have, and check against that number. You might have to figure out how many are on the screen at one time, and subtract that from your number of children. I've never had to do that. Hope this helps
Those methods cannot be used to detect scrolling directions directly. There are many ways of getting the direction. A simple code(untested) for one such method is explained below :
public class ScrollTrackingListView extends ListView {
private boolean readyForMeasurement = false;
private Boolean isScrollable = null;
private float prevDistanceToEnd = -1.0;
private ScrollDirectionListener listener = null;
public ScrollTrackingListView(Context context) {
super(context);
init();
}
public ScrollTrackingListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollTrackingListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
ViewTreeObserver observer = getViewTreeObserver();
observer.addOnGlobalLayoutListener(globalLayoutListener);
setOnScrollListener(scrollListener);
}
private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener
= new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
readyForMeasurement = true;
calculateDistanceToEnd();
}
};
public void registerScrollDirectionListener(ScrollDirectionListener listener) {
scrollDirectionListener = listener;
}
public void unregisterScrollDirectionListener() {
scrollDirectionListener = null;
}
private OnScrollListener scrollListener
= new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int i) {
calculateDistanceToEnd();
}
#Override
public void onScroll(AbsListView absListView, int i, int i1, int i2) {
// Do nothing
}
};
private void calculateDistanceToEnd() {
if (readyForMeasurement) {
// I'm using the height of the layout, horizontal scrollbar and
// content along with scroll down offset
// computeVerticalScrollExtent is used to compute the length of the thumb within the scrollbar's track.
// The length of the thumb is a function of the view height and the content length.
int verticalScrollExtent = computeVerticalScrollExtent();
int verticalScrollOffset = computeVerticalScrollOffset();
int verticalScrollRange = computeVerticalScrollRange();
int horizontalScrollBarHeight = getHorizontalScrollbarHeight();
/**
* 1. Let "R" represent the range of the vertical scrollbar. This corresponds to the length of the content
* in the view.
* 2. Let "E" represent the extent of the vertical scrollbar. The extent is a constant value and is
* (probably) equal to a value proportional to the height of the view.
* 3. Offset "o" represents the current position in the range that is visible to the user. It can take
* values from "0 to E".
*
* Now the DistanceToEnd is calculated using these three values as follows :
*
* DistanceToEnd = (R - o) / E
*
* DistanceToEnd will hold the value in NumberOfScreenToEnd units.
*
*/
float distanceToEnd =
((float)(verticalScrollRange - verticalScrollOffset))/((float)(verticalScrollExtent));
if(prevDistanceToEnd == -1) {
prevDistanceToEnd = distanceToEnd;
} else {
if(listener != null) {
if(distanceToEnd > prevDistanceToEnd) {
// User is scrolling up
listener.onScrollingUp();
} else {
// User is scrolling up
listener.onScrollingDown();
}
}
prevDistanceToEnd = distanceToEnd;
}
if(isScrollable == null) {
// Check if the view height is less than a screen (i.e., no scrolling is enabled)
if((horizontalScrollBarHeight + verticalScrollExtent) >= verticalScrollRange) {
isScrollable = Boolean.FALSE;
} else {
isScrollable = Boolean.TRUE;
}
}
}
}
public interface ScrollDirectionListener {
public void onScrollingUp();
public void onScrollingDown();
}
}
The idea is to calculate the distanceToEnd. If distanceToEnd increases, the user is scrolling up and if it decreases, the user is scrolling down. That will also give you the exact distance to the end of the list.
If you are just trying to know whether the user is scrolling up or down you can override the onInterceptTouchEvent to detect the scrolling direction like below :
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
final float x = event.getX();
final float y = event.getY();
final float yDiff = y - mInitialY; // yDiff less than 0.0 implies scrolling down while yDiff greater than 0.0 implies scrolling up. If I try to add the less than or greater than symbols, the preview refuses to display it.
if(yDiff less than 0.0) listener.onScrollingDown();
else if(yDiff greater than 0.0) listener.onScrollingUp();
break;
}
return super.onInterceptTouchEvent(event);
}
Trick about detect scroll up or down in listview, you just call this function on onScroll function in OnScrollListener of ListView.
private int oldFirstVisibleItem = -1;
private protected int oldTop = -1;
// you can change this value (pixel)
private static final int MAX_SCROLL_DIFF = 5;
private void calculateListScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == oldFirstVisibleItem) {
int top = view.getChildAt(0).getTop();
// range between new top and old top must greater than MAX_SCROLL_DIFF
if (top > oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
// scroll up
} else if (top < oldTop && Math.abs(top - oldTop) > MAX_SCROLL_DIFF) {
// scroll down
}
oldTop = top;
} else {
View child = view.getChildAt(0);
if (child != null) {
oldFirstVisibleItem = firstVisibleItem;
oldTop = child.getTop();
}
}
}
Simple way to detect scroll up/down on android listview
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
if(prevVisibleItem != firstVisibleItem){
if(prevVisibleItem < firstVisibleItem)
//ScrollDown
else
//ScrollUp
prevVisibleItem = firstVisibleItem;
}
dont forget
yourListView.setOnScrollListener(yourScrollListener);
Simple way to load more items on scroll up/down event in android GridView
grid.setOnScrollListener(new AbsListView.OnScrollListener() {
private int mLastFirstVisibleItem;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
Log.d("state",String.valueOf(scrollState));
if(scrollState == 0)
Log.i("a", "scrolling stopped...");
if (view.getId() == grid.getId()) {
final int currentFirstVisibleItem = grid.getLastVisiblePosition();
mLastFirstVisibleItem = grid.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
mIsScrollingUp = false;
if(!next.contains("null")){
//Call api to get products from server
}
Log.i("a", "scrolling down...");
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
mIsScrollingUp = true;
Log.i("a", "scrolling up...");
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
Log.d("on scroll","");
}
});
Here's what I would try first:
1) Create an interface (let's call it OnScrollTopOrBottomListener) with these methods:
void onScrollTop();
void onScrollBottom();
2) In your list's adapter, add a member instance, typed as the interface you created and supply a setter and getter.
3) In the getView() implementation of your adapter, check if the position parameter is either 0 or getCount() - 1. Also check that your OnScrollTopOrBottomListener instance is not null.
4) If the position is 0, call onScrollTopOrBottomListener.onScrollTop(). If position is getCount() - 1, call onScrollTopOrBottomListener.onScrollBottom().
5) In your OnScrollTopOrBottomListener implementation, call the appropriate methods to get the desired data.
Hope that helps in some way.
-Brandon
I have encountered problems using some example where the cell size of ListView is great. So I have found a solution to my problem which detects the slightest movement of your finger . I've simplified to the minimum possible and is as follows:
private int oldScrolly;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View view = absListView.getChildAt(0);
int scrolly = (view == null) ? 0 : -view.getTop() + absListView.getFirstVisiblePosition() * view.getHeight();
int margin = 10;
Log.e(TAG, "Scroll y: " + scrolly + " - Item: " + firstVisibleItem);
if (scrolly > oldScrolly + margin) {
Log.d(TAG, "SCROLL_UP");
oldScrolly = scrolly;
} else if (scrolly < oldScrolly - margin) {
Log.d(TAG, "SCROLL_DOWN");
oldScrolly = scrolly;
}
}
});
PD: I use the MARGIN to not detect the scroll until you meet that margin . This avoids problems when I show or hide views and avoid blinking of them.
Related
I have a simple ListView inside a ViewPager and a Floating Action Bar at the bottom right corner of the screen.
I want to implement the Quick Return pattern to it so that when I scroll the ListView the FAB goes down the screen and comes up on opposite scroll.
Thanks in advance.
EDIT:
list.setAdapter(new QuickReturnAdapter(adapter));
quickReturnAttacher = QuickReturnAttacher.forView(list);
quickReturnAttacher.addTargetView(floatMenu, AbsListViewScrollTarget.POSITION_BOTTOM, dpToPx(context, 400));
if (quickReturnAttacher instanceof AbsListViewQuickReturnAttacher) {
// This is the correct way to register an OnScrollListener.
// You have to add it on the QuickReturnAttacher, instead
// of on the viewGroup directly.
final AbsListViewQuickReturnAttacher attacher = (AbsListViewQuickReturnAttacher) quickReturnAttacher;
attacher.addOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
Then I am using quickReturnAttacher.setOnItemClickListener and this ain't working.
There is already an open source library for this feature.
https://github.com/felipecsl/QuickReturn
Try this:
final AbsListViewQuickReturnAttacher attacher = (AbsListViewQuickReturnAttacher) quickReturnAttacher;
attacher.addOnScrollListener(this);
attacher.setOnItemClickListener(this);
});
For me this is working, the position passed in the callback is correct.
You have to register your click listener to the attacher, not to the list.
How do you cannot find the method setOnItemClickListener in the class AbsListViewQuickReturnAttacher?
You can try this solution for Quick Return Pattern for ListViews:
yourListView.setOnScrollListener(new AbsListView.OnScrollListener() {
#SuppressLint("NewApi")
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int scrollOffset = 0;
float transitionY;
if (firstVisibleItem > 0) {
scrollOffset += headerHeight;
if (firstVisibleItem > 1) {
scrollOffset += (firstVisibleItem - 1) * cellHeight;
}
}
if (yourListView.getChildCount() > 0) {
scrollOffset += -yourListView.getChildAt(0).getTop();
scrollOffset = -scrollOffset;
}
float scrollDelta = scrollOffset - prevOffset;
float nextY = mQuickReturnView.getY() + scrollDelta;
if (nextY < minRawY) {
transitionY = minRawY;
}
else if (nextY > qReturnDelta) {
transitionY = qReturnDelta;
}
else {
transitionY = nextY;
}
mQuickReturnView.setY(transitionY);
prevOffset = scrollOffset;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
});
Everytime I scroll the list up or down I hide (or unhide) some views with OnScrollListener. Here is the code attached to my ListView.
lv.setOnScrollListener(new OnScrollListener() {
private int mLastFirstVisibleItem;
private boolean mIsScrollingUp = true;
private LinearLayout ll = (LinearLayout) getActivity()
.findViewById(R.id.llSearchPlaces);
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (view.getId() == lv.getId()) {
final int currentFirstVisibleItem = lv
.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
if (mIsScrollingUp == true) {
mIsScrollingUp = false;
Log.i("a", "scrolling down...");
floatingActionButton.hide();
Animation animation = new TranslateAnimation(0, 0,
0, 200);
animation.setDuration(300);
animation
.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(
Animation animation) {
ll.setVisibility(View.INVISIBLE);
}
#Override
public void onAnimationRepeat(
Animation animation) {
}
#Override
public void onAnimationStart(
Animation animation) {
}
});
ll.startAnimation(animation);
}
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
if (mIsScrollingUp == false) {
mIsScrollingUp = true;
floatingActionButton.show();
Log.i("a", "scrolling up...");
Animation animation = new TranslateAnimation(0, 0,
200, 0);
animation.setDuration(400);
animation
.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(
Animation animation) {
}
#Override
public void onAnimationRepeat(
Animation animation) {
}
#Override
public void onAnimationStart(
Animation animation) {
ll.setVisibility(View.VISIBLE);
}
});
ll.startAnimation(animation);
}
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
Layout:
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ListView
android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fadeScrollbars="true"
android:listSelector="#00000000"
android:scrollbars="none" />
</android.support.v4.widget.SwipeRefreshLayout>
Ever since I added the SwipeRefreshLayout, I do not get anything when I Log inside the listener above. How can I use both of these items together?
EDIT: It seems like this is what I need but I can't make it work still
As part of that article I added this in onScroll, tho it doesn't seem to work.
if (firstVisibleItem == 0) {
swipeLayout.setEnabled(true);
} else {
swipeLayout.setEnabled(false);
}
EDIT2: THIS IS THE HEART OF THE ISSUE: It seems the onScroll method fires when the Activity first starts and the list loads and then never more again.
I had the same issue with RecyclerView and ListView. Scrolling downwards for whatever amount of items, it was impossible to return to the top of the list.
This will disable the SwipeRefreshLayout until the first visible item or any item position is visible. You can also bind different scroll listeners along this one. Make sure you enable (if previously disabled) SwipeRefreshLayout whenever you repopulate the listivew.
public class SwipeRefreshLayoutToggleScrollListenerListView implements AbsListView.OnScrollListener {
private List<AbsListView.OnScrollListener> mScrollListeners = new ArrayList<AbsListView.OnScrollListener>();
private int mExpectedVisiblePosition = 0;
public SwipeRefreshLayoutToggleScrollListenerListView(SwipeRefreshLayout mSwipeLayout) {
this.mSwipeLayout = mSwipeLayout;
}
private SwipeRefreshLayout mSwipeLayout;
public void addScrollListener(AbsListView.OnScrollListener listener){
mScrollListeners.add(listener);
}
public boolean removeScrollListener(AbsListView.OnScrollListener listener){
return mScrollListeners.remove(listener);
}
public void setExpectedFirstVisiblePosition(int position){
mExpectedVisiblePosition = position;
}
private void notifyOnScrolled(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount){
for(AbsListView.OnScrollListener listener : mScrollListeners){
listener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
private void notifyScrollStateChanged(AbsListView view, int scrollState){
for(AbsListView.OnScrollListener listener : mScrollListeners){
listener.onScrollStateChanged(view, scrollState);
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
notifyScrollStateChanged(view, scrollState);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
notifyOnScrolled(view, firstVisibleItem, visibleItemCount, totalItemCount);
if(firstVisibleItem != RecyclerView.NO_POSITION)
mSwipeLayout.setEnabled(firstVisibleItem == mExpectedVisiblePosition);
}
}
Edit:
lv.setOnScrollListener(new SwipeRefreshLayoutToggleScrollListenerListView(mSwiperLayout){
//override methods here, don't forget the super calls.
});
yourListview.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
boolean enable = false;
if(timelogListView != null && timelogListView.getChildCount() > 0){
boolean firstItemVisible = timelogListView.getFirstVisiblePosition() == 0;
boolean topOfFirstItemVisible = timelogListView.getChildAt(0).getTop() == 0;
enable = firstItemVisible && topOfFirstItemVisible;
}
swipeLayout.setEnabled(enable);
if(firstVisibleItem+visibleItemCount == totalItemCount && totalItemCount!=0)
{
if(flag_loading == false)
{
flag_loading = true;
ConnectionDetector conn = new ConnectionDetector(getActivity());
if(conn.isConnectingToInternet()){
// call for your pagination async
}else{
}
}
}
}
});
By default : private boolean flag_loading = false; and in your pagination async if(YourListview.size() < 20) { flag_loading = true; } else{ flag_loading = false; }
You should override canChildScrollUp of SwipeRefreshView. This method is polled for the purpose of knowing whether the contained View wants to scroll up:
An extension with a settable interface for any arbritrary contained view:
public class SwipeRefreshWrapper extends SwipeRefreshLayout {
private ScrollResolver mScrollResolver;
public SwipeRefreshWrapper(Context context) {
super(context);
}
public SwipeRefreshWrapper(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScrollResolver(ScrollResolver scrollResolver) {
mScrollResolver = scrollResolver;
}
#Override
public boolean canChildScrollUp() {
if(mScrollResolver != null){
return mScrollResolver.canScrollUp();
}else {
return super.canChildScrollUp();
}
}
public static interface ScrollResolver{
public boolean canScrollUp();
}
}
Usage (ListView also has a nice method: canScrollVertically()):
final SwipeRefreshWrapper wrapper = (SwipeRefreshWrapper) rootView.findViewById(R.id.wrapper);
final ListView list = (ListView) rootView.findViewById(R.id.list);
wrapper.setScrollResolver(new SwipeRefreshWrapper.ScrollResolver() {
#Override
public boolean canScrollUp() {
return list.canScrollVertically(-1);
}
});
Is there a way to get a listview's scroll distance, and exactly how far in pixel it scrolls?
Thank you
See this snippet written by me. It can calculate how far in pixel it scrolls, and listen scroll change.
/**
* Created by mariotaku on 14/10/22.
*/
public class ListScrollDistanceCalculator implements OnScrollListener {
private ScrollDistanceListener mScrollDistanceListener;
private boolean mListScrollStarted;
private int mFirstVisibleItem;
private int mFirstVisibleHeight;
private int mFirstVisibleTop, mFirstVisibleBottom;
private int mTotalScrollDistance;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (view.getCount() == 0) return;
switch (scrollState) {
case SCROLL_STATE_IDLE: {
mListScrollStarted = false;
break;
}
case SCROLL_STATE_TOUCH_SCROLL: {
final View firstChild = view.getChildAt(0);
mFirstVisibleItem = view.getFirstVisiblePosition();
mFirstVisibleTop = firstChild.getTop();
mFirstVisibleBottom = firstChild.getBottom();
mFirstVisibleHeight = firstChild.getHeight();
mListScrollStarted = true;
mTotalScrollDistance = 0;
break;
}
}
}
public int getTotalScrollDistance() {
return mTotalScrollDistance;
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (totalItemCount == 0 || !mListScrollStarted) return;
final View firstChild = view.getChildAt(0);
final int firstVisibleTop = firstChild.getTop(), firstVisibleBottom = firstChild.getBottom();
final int firstVisibleHeight = firstChild.getHeight();
final int delta;
if (firstVisibleItem > mFirstVisibleItem) {
mFirstVisibleTop += mFirstVisibleHeight;
delta = firstVisibleTop - mFirstVisibleTop;
} else if (firstVisibleItem < mFirstVisibleItem) {
mFirstVisibleBottom -= mFirstVisibleHeight;
delta = firstVisibleBottom - mFirstVisibleBottom;
} else {
delta = firstVisibleBottom - mFirstVisibleBottom;
}
mTotalScrollDistance += delta;
if (mScrollDistanceListener != null) {
mScrollDistanceListener.onScrollDistanceChanged(delta, mTotalScrollDistance);
}
mFirstVisibleTop = firstVisibleTop;
mFirstVisibleBottom = firstVisibleBottom;
mFirstVisibleHeight = firstVisibleHeight;
mFirstVisibleItem = firstVisibleItem;
}
public void setScrollDistanceListener(ScrollDistanceListener listener) {
mScrollDistanceListener = listener;
}
public static interface ScrollDistanceListener {
void onScrollDistanceChanged(int delta, int total);
}
}
I think that getScrollY() may help you:
Return the scrolled top position of this view. This is the top edge of the displayed part of your view.
See http://developer.android.com/reference/android/view/View.html#getScrollY()
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?
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.
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;
}
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
}
}
});
More complex solution (working with long items height in listview)
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();
}
}
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;
}
}
});
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
}
});
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;
}
});
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()
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) {
}
}
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.
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]
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.
Is there a way to identify if listview is being scroll up or down?
OnScrollListener doens't help me in this case.
Thanks in advance!
this is a simple implementation:
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){
// Scrolling down
}
if(mLastFirstVisibleItem > firstVisibleItem){
// scrolling up
}
mLastFirstVisibleItem = firstVisibleItem;
}
});
There is a method in ScrollViews that reports the shifting of scrolls. It is called onScrollChanged(). However, it is declared protected, so you must create a wrapper class to gain access. For the usage of the method, please check android docs.
First, create an interface to expose the protected method
public interface OnScrollListener {
void onScrollChanged(int x, int y, int oldx, int oldy);
}
Then, create your wrapper and extend ScrollView
public class ReportingScrollView extends ScrollView {
private OnScrollListener onScrollListener = null;
public ReportingScrollView(Context context) {
super(context);
}
public ReportingScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ReportingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
#Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (onScrollListener != null) {
onScrollListener.onScrollChanged(x, y, oldx, oldy);
}
}
}
Finally, include it in your XML layout like a custom view, and attach listeners like usual.
<your.package.ReportingScrollView />
scrollingandtrolling.setOnScrollListener(new OnScrollListener() {...});
for whom ever still looking for an answer for kotlin, this works for it
MainListView.setOnScrollListener(object :AbsListView.OnScrollListener {
// var VisibleItem: Int = 0
override fun onScroll(p0: AbsListView?, FirstVisibleItem: Int, i2: Int, i3: Int) {
/*
if(VisibleItem < FirstVisibleItem)
{
Toast.makeText(applicationContext,"Scrolling Down",Toast.LENGTH_SHORT).show()
fab.hide()
}
if(VisibleItem >FirstVisibleItem)
{
fab.show()
Toast.makeText(applicationContext,"Scrolling Up",Toast.LENGTH_SHORT).show()
}
VisibleItem=FirstVisibleItem;
*/
}
override fun onScrollStateChanged(p0: AbsListView?, p1: Int) {
if (p1 == 1) {
fab.hide()
} else {
fab.show()
}
}
})
}
Uncomment to use an alternative method.
An easy and working solution.
private int lastPosition = -1;
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
if(lastPosition == firstVisibleItem)
{
return;
}
if(firstVisibleItem > lastPosition)
{
Logger.print("Going Down");
}
else
{
Logger.print("Going Up");
}
lastPosition = firstVisibleItem;
}
The solution given by Some Noob Student is good: you have to create a wrapper to use the protected onScrollChanged(int x, int y, int oldx, int oldy) method, which is triggered more often then the standard AbsListView.OnScrollListener.onScrollStateChanged(AbsListView view, int scrollState).
Unfortunately the onScrollChanged() always returns 0 as parameters, no matter how I scroll, and seems to detect only when the scroll ends, reaching the top or the bottom of the list.
So I used the following code to detect the right direction of the scrolling: it uses the getTop() of the first visible item but uses also its position to understand when the first item is no more visible; during this item transition, the scroll direction is given by the item position itself.
#Override
public void onScrollChanged(int x, int y, int oldx, int oldy) {
if(listView.getChildAt(0) != null) {
int scrollingDir = NO_SCROLLING;
int firstChildTop = listView.getChildAt(0).getTop();
if (listView.getFirstVisiblePosition() == mLastFirstVisiblePosition) {
if (mLastFirstChildTop < firstChildTop) {
//Scrolling DOWN
scrollingDir = SCROLLING_DOWN;
} else if (mLastFirstChildTop > firstChildTop) {
//Scrolling UP
scrollingDir = SCROLLING_UP;
}
} else if(listView.getFirstVisiblePosition() > mLastFirstVisiblePosition) {
//Scrolling UP
scrollingDir = SCROLLING_UP;
} else {
//Scrolling DOWN
scrollingDir = SCROLLING_DOWN;
}
mLastFirstVisiblePosition = listView.getFirstVisiblePosition();
mLastFirstChildTop = firstChildTop;
switch(scrollingDir) {
case SCROLLING_DOWN:
//Your DOWN scrolling code here
break;
case SCROLLING_UP:
//Your UP scrolling code here
break;
default:
case NO_SCROLLING:
break;
}
}
}
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;
}
});
Here based on the last visible item i decide whether the list is going up or down