Load more data when Scroll reaches bottom - android

I'm new to Android. My requirement is to load extra data & set to the list view, when scroll reaches to the bottom. I am able to call next data but my previous data is hiding. How to correct this?
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view,
int scrollState) {
// TODO Auto-generated method stub
}
#Override
public void onScroll(AbsListView view,
int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
// TODO Auto-generated method stub
if (listView.getLastVisiblePosition() == listView
.getAdapter().getCount() - 1
&& listView.getChildAt(
listView.getChildCount() - 1)
.getBottom() <= listView
.getHeight()) {
// scroll end reached
if (nextCall == null || nextCall == ""
|| nextCall.equals("null")
|| nextCall.equals("")) {
} else {
hostname=nextCall;
new ArticlesList().execute();
}
// Write your code here
}else{
}
}
});
dialog.dismiss();
} else {
Toast.makeText(getActivity(), "Went wrong",
Toast.LENGTH_LONG).show();
dialog.dismiss();
}

visit this which is discribe how to do pagination in listview. PagingListView

What you need is an endless scroll Listener, please take a look at this: https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews
just add this project to yours and use it like this:
yourListView.setOnScrollListener(new EndlessScrollListener() {
#Override
public void onLoadMore(int page, int totalItemsCount) {
... your asynchTask
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
super.onScrollStateChanged(view, scrollState);
}
});

In listView adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (position == items.size() - 1)
{
items.add("new data1");
items.add("new data2");
notificationDataSetChanged();
}
//inject your view here
}
You lose your data, because you are overwriting a link to it. You should add data, not replace it.

I solved the same problem using following code snippet.
private int lastVisibleItem; private LinearLayoutManager linearLayoutManager; private int lastId; // get the last id of your post after first time load data from server. private int flag = 0;
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView_news.setLayoutManager(linearLayoutManager);
recyclerView_news.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState){
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
new Handler().post(new Runnable() {
#Override
public void run() {
if (list_news.size() - 1 == lastVisibleItem) {
if (flag == 0) {
flag = 1;
getMoreData(lastId); // pass lastId to load next posts.
}
} else if (list_news.size() - 1 != lastVisibleItem) {
flag = 0;
}
}
});
}
});
Hope this will help. Thanks.

Related

SwipeRefreshLayout interfering with setOnScrollListener

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

Check if android listview is scrolled down to the last item

I have a listview in which there are several items. I want to check whether the list is scrolled down to the last item, in which case I want to run another method.How to do that?
at first set isLoading = false; in the constructore or onCreate method
mListView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mListView.getAdapter() == null)
return ;
if (mListView.getAdapter().getCount() == 0)
return ;
int l = visibleItemCount + firstVisibleItem;
if (l >= totalItemCount && !isLoading) {
// It is time to add new data. We call the listener
isLoading = true;
loadData();
}
}
});
and in function loadData you do something like:
public void loadData() {
// send your request
// receive it by callback or asynctask
yourDataList.addAll(newItems);
adapter.notifyDataSetChanged();
isLoading = false;
}
Have a look at listView.getLastVisiblePosition().

Android : Hide relativelayout when scroll down on listview

I try to add a relativelayout when the user begin to scroll down in a listview.
I would like to show the relativelayout again when the user scroll to top and when first item of the listview is displayed.
My code works but there is a little "climb" when the user try to scroll.
I think that the problem is located on listAtTop function but i don't know where.
list.setOnDetectScrollListener(new OnDetectScrollListener() {
#Override
public void onUpScrolling() {
/* do something */
Log.d("INFO", "UPPPPPPPPPPP");
if (listIsAtTop()) {
RelativeLayout relative1 = (RelativeLayout) findViewById(R.id.relative1);
relative1.setVisibility(View.VISIBLE);
}
}
#Override
public void onDownScrolling() {
/* do something */
Log.d("INFO", "DOWNNNNNNNN");
if (!listIsAtTop()) {
RelativeLayout relative1 = (RelativeLayout) findViewById(R.id.relative1);
relative1.setVisibility(View.GONE);
}
}
});
private boolean listIsAtTop() {
if (list.getChildCount() == 0) {
return true;
}
return list.getFirstVisiblePosition() == 0 && (list.getChildCount() == 0 || list.getChildAt(0).getTop() == 0);
}
Try this way
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
int mPosition=0;
int mOffset=0;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
int position = list.getFirstVisiblePosition();
View v = list.getChildAt(0);
int offset = (v == null) ? 0 : v.getTop();
if (mPosition < position || (mPosition == position && mOffset < offset)){
// Scrolled up
//search_layout.setVisibility(View.GONE);
} else {
// Scrolled down
//search_layout.setVisibility(View.VISIBLE);
}
}
});
In this code when you scroll down hide layout and when you scroll up and reached to top then Visible layout

Checking if Android Gridview is scrolled to its top

I want to determine if my Gridview is scrolled to its top.
Right now I'm using getChildAt(0).getTop() to do this. I save the value of getChildAt(0).getTop() on first draw and compare to getChildAt(0).getTop() on subsequent draws .
However, this seems hacky and seems to sometimes give me incorrect results.
Any better ideas?
Try using the onScrollListener
setOnScrollListener(this);
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if(firstVisibleItem == 0){
//do stuff here
}
}
The getChildAt(0) returns you the first visible item of the GridView and I guess that it's not what you want.
If you use yourGridView.getFirstVisiblePosition() method it will return the first visible position your data adapter, and that is what you want.
Hope this can help, it works for me, good luck.
yourGridView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(SCROLL_STATE_IDLE == scrollState) {
View view = view.getChildAt(0);
if(view != null) {
float y = view.getY();
Log.d("Tag", "first view Y is " + y);
if(y == 0) {
// do what you want
}
}
}
}
This's my answer:
mLayoutManager = new GridLayoutManager(getActivity(), 2);
mListRV= (RecyclerView).findViewById(R.id.list_rv);
mListRV.setLayoutManager(mLayoutManager);
mListRV.setOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
View v = mLayoutManager.getChildAt(0);
int offsetTop = v.getTop();
int offsetBottom = v.getBottom();
int height = (offsetBottom - offsetTop);
if(offsetTop >= -height/2) {
mListRV.smoothScrollBy(0, offsetTop);
} else {
mListRV.smoothScrollBy(0, height + offsetTop);
}
}
}
});

Find out if ListView is scrolled to the bottom?

Can I find out if my ListView is scrolled to the bottom? By that I mean that the last item is fully visible.
Edited:
Since I have been investigating in this particular subject in one of my applications, I can write an extended answer for future readers of this question.
Implement an OnScrollListener, set your ListView's onScrollListener and then you should be able to handle things correctly.
For example:
private int preLast;
// Initialization stuff.
yourListView.setOnScrollListener(this);
// ... ... ...
#Override
public void onScroll(AbsListView lw, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount)
{
switch(lw.getId())
{
case R.id.your_list_id:
// Make your calculation stuff here. You have all your
// needed info from the parameters of this function.
// Sample calculation to determine if the last
// item is fully visible.
final int lastItem = firstVisibleItem + visibleItemCount;
if(lastItem == totalItemCount)
{
if(preLast!=lastItem)
{
//to avoid multiple calls for last item
Log.d("Last", "Last");
preLast = lastItem;
}
}
}
}
Late answer, but if you simply wish to check whether your ListView is scrolled all the way down or not, without creating an event listener, you can use this if-statement:
if (yourListView.getLastVisiblePosition() == yourListView.getAdapter().getCount() -1 &&
yourListView.getChildAt(yourListView.getChildCount() - 1).getBottom() <= yourListView.getHeight())
{
//It is scrolled all the way down here
}
First it checks if the last possible position is in view. Then it checks if the bottom of the last button aligns with the bottom of the ListView. You can do something similar to know if it's all the way at the top:
if (yourListView.getFirstVisiblePosition() == 0 &&
yourListView.getChildAt(0).getTop() >= 0)
{
//It is scrolled all the way up here
}
The way I did it:
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE
&& (listView.getLastVisiblePosition() - listView.getHeaderViewsCount() -
listView.getFooterViewsCount()) >= (adapter.getCount() - 1)) {
// Now your listview has hit the bottom
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
Something to the effect of:
if (getListView().getLastVisiblePosition() == (adapter.items.size() - 1))
public void onScrollStateChanged(AbsListView view, int scrollState)
{
if (!view.canScrollList(View.SCROLL_AXIS_VERTICAL) && scrollState == SCROLL_STATE_IDLE)
{
//When List reaches bottom and the list isn't moving (is idle)
}
}
This worked for me.
This can be
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
if (scrollState == 2)
flag = true;
Log.i("Scroll State", "" + scrollState);
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
if ((visibleItemCount == (totalItemCount - firstVisibleItem))
&& flag) {
flag = false;
Log.i("Scroll", "Ended");
}
}
canScrollVertically(int direction) works for all Views, and seems to do what you asked, with less code than most of the other answers. Plug in a positive number, and if the result is false, you're at the bottom.
ie:
if (!yourView.canScrollVertically(1)) {
//you've reached bottom
}
It was pretty painful to deal with scrolling, detecting when it is finished and it is indeed at the bottom of the list (not bottom of the visible screen), and triggers my service only once, to fetch data from the web. However it is working fine now. The code is as follows for the benefit of anybody who faces the same situation.
NOTE: I had to move my adapter related code into onViewCreated instead of onCreate and detect scrolling primarily like this:
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (getListView().getLastVisiblePosition() == (adapter.getCount() - 1))
if (RideListSimpleCursorAdapter.REACHED_THE_END) {
Log.v(TAG, "Loading more data");
RideListSimpleCursorAdapter.REACHED_THE_END = false;
Intent intent = new Intent(getActivity().getApplicationContext(), FindRideService.class);
getActivity().getApplicationContext().startService(intent);
}
}
Here RideListSimpleCursorAdapter.REACHED_THE_END is an additional variable in my SimpleCustomAdapter which is set like this:
if (position == getCount() - 1) {
REACHED_THE_END = true;
} else {
REACHED_THE_END = false;
}
Only when both of these conditions meet, it means that I am indeed at the bottom of the list, and that my service will run only once. If I don't catch the REACHED_THE_END, even scrolling backwards triggers the service again, as long as the last item is in view.
To expand a bit on one of the above answers, this is what I had to do to get it working completely. There seems to be about 6dp of built-in padding inside of ListViews, and onScroll() was being called when the list was empty. This handles both of those things. It could probably be optimized a bit, but is written more for clarity.
Side note: I've tried several different dp to pixel conversion techniques, and this dp2px() one has been the best.
myListView.setOnScrollListener(new OnScrollListener() {
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (visibleItemCount > 0) {
boolean atStart = true;
boolean atEnd = true;
View firstView = view.getChildAt(0);
if ((firstVisibleItem > 0) ||
((firstVisibleItem == 0) && (firstView.getTop() < (dp2px(6) - 1)))) {
// not at start
atStart = false;
}
int lastVisibleItem = firstVisibleItem + visibleItemCount;
View lastView = view.getChildAt(visibleItemCount - 1);
if ((lastVisibleItem < totalItemCount) ||
((lastVisibleItem == totalItemCount) &&
((view.getHeight() - (dp2px(6) - 1)) < lastView.getBottom()))
) {
// not at end
atEnd = false;
}
// now use atStart and atEnd to do whatever you need to do
// ...
}
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
});
private int dp2px(int dp) {
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
I can't comment yet because I haven't got enough reputation, but in #Ali Imran and #Wroclai 's answer I think something is missing. With that piece of code, once you update preLast, it will never execute the Log again.
In my specific problem, I want to execute some operation every time I scroll to the bottom, but once preLast is updated to LastItem, that operation is never executed again.
private int preLast;
// Initialization stuff.
yourListView.setOnScrollListener(this);
// ... ... ...
#Override
public void onScroll(AbsListView lw, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount) {
switch(lw.getId()) {
case android.R.id.list:
// Make your calculation stuff here. You have all your
// needed info from the parameters of this function.
// Sample calculation to determine if the last
// item is fully visible.
final int lastItem = firstVisibleItem + visibleItemCount;
if(lastItem == totalItemCount) {
if(preLast!=lastItem){ //to avoid multiple calls for last item
Log.d("Last", "Last");
preLast = lastItem;
}
} else {
preLast = lastItem;
}
}
With that "else" you're now able to execute your code (Log, in this case) every time you scroll to the bottom once again.
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
int lastindex = view.getLastVisiblePosition() + 1;
if (lastindex == totalItemCount) { //showing last row
if ((view.getChildAt(visibleItemCount - 1)).getTop() == view.getHeight()) {
//Last row fully visible
}
}
}
For your list to call when the list reach last and if an error happens, then this will not call the endoflistview again. This code will help this scenario as well.
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
final int lastPosition = firstVisibleItem + visibleItemCount;
if (lastPosition == totalItemCount) {
if (previousLastPosition != lastPosition) {
//APPLY YOUR LOGIC HERE
}
previousLastPosition = lastPosition;
}
else if(lastPosition < previousLastPosition - LIST_UP_THRESHOLD_VALUE){
resetLastIndex();
}
}
public void resetLastIndex(){
previousLastPosition = 0;
}
where the LIST_UP_THRESHOLD_VALUE can be any integer value(I have used 5) where your list is scrolled up and while returning to the end, this will call the end of list view again.
I found a very nice way to automatically load the next page set in a way that doesn't require your own ScrollView (like the accepted answer requires).
On ParseQueryAdapter there is a method called getNextPageView that is there to allow you to supply your own custom view that appears at the end of the list when there is more data to load so it will only trigger when you have reached the end of you current page set (it's the "load more.." view by default). This method is only called when there is more data to load so it's a great place to call loadNextPage(); This way the adapter does all the hard work for you in determining when new data should be loaded and it won't be called at all if you have reached the end of the data set.
public class YourAdapter extends ParseQueryAdapter<ParseObject> {
..
#Override
public View getNextPageView(View v, ViewGroup parent) {
loadNextPage();
return super.getNextPageView(v, parent);
}
}
Then inside your activity/fragment you just have to set the adapter and new data will be automatically updated for you like magic.
adapter = new YourAdapter(getActivity().getApplicationContext());
adapter.setObjectsPerPage(15);
adapter.setPaginationEnabled(true);
yourList.setAdapter(adapter);
To detect whether the last item is fully visible, you can simple add calculation on the view's last visible item's bottom by lastItem.getBottom().
yourListView.setOnScrollListener(this);
#Override
public void onScroll(AbsListView view, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount) {
int vH = view.getHeight();
int topPos = view.getChildAt(0).getTop();
int bottomPos = view.getChildAt(visibleItemCount - 1).getBottom();
switch(view.getId()) {
case R.id.your_list_view_id:
if(firstVisibleItem == 0 && topPos == 0) {
//TODO things to do when the list view scroll to the top
}
if(firstVisibleItem + visibleItemCount == totalItemCount
&& vH >= bottomPos) {
//TODO things to do when the list view scroll to the bottom
}
break;
}
}
I went with:
#Override
public void onScroll(AbsListView listView, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
if(totalItemCount - 1 == favoriteContactsListView.getLastVisiblePosition())
{
int pos = totalItemCount - favoriteContactsListView.getFirstVisiblePosition() - 1;
View last_item = favoriteContactsListView.getChildAt(pos);
//do stuff
}
}
In the method getView() (of a BaseAdapter-derived class) one can check if position of the current view is equal to the list of items in the Adapter. If that is the case, then it means we've reached the end/bottom of the list:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// ...
// detect if the adapter (of the ListView/GridView) has reached the end
if (position == getCount() - 1) {
// ... end of list reached
}
}
I find a better way to detect listview scroll end the bottom, first detect scoll end by this
Implementation of onScrollListener to detect the end of scrolling in a ListView
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.currentFirstVisibleItem = firstVisibleItem;
this.currentVisibleItemCount = visibleItemCount;
}
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.currentScrollState = scrollState;
this.isScrollCompleted();
}
private void isScrollCompleted() {
if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) {
/*** In this way I detect if there's been a scroll which has completed ***/
/*** do the work! ***/
}
}
finally combine Martijn's answer
OnScrollListener onScrollListener_listview = new OnScrollListener() {
private int currentScrollState;
private int currentVisibleItemCount;
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
this.currentScrollState = scrollState;
this.isScrollCompleted();
}
#Override
public void onScroll(AbsListView lw, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub
this.currentVisibleItemCount = visibleItemCount;
}
private void isScrollCompleted() {
if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) {
/*** In this way I detect if there's been a scroll which has completed ***/
/*** do the work! ***/
if (listview.getLastVisiblePosition() == listview.getAdapter().getCount() - 1
&& listview.getChildAt(listview.getChildCount() - 1).getBottom() <= listview.getHeight()) {
// It is scrolled all the way down here
Log.d("henrytest", "hit bottom");
}
}
}
};
Big thanks to posters in stackoverflow! I combined some ideas and created class listener for activities and fragments (so this code is more reusable making code faster to write and much cleaner).
All you have to do when you got my class is to implement interface (and of course create method for it) which is in declared in my class and create object of this class passing arguments.
/**
* Listener for getting call when ListView gets scrolled to bottom
*/
public class ListViewScrolledToBottomListener implements AbsListView.OnScrollListener {
ListViewScrolledToBottomCallback scrolledToBottomCallback;
private int currentFirstVisibleItem;
private int currentVisibleItemCount;
private int totalItemCount;
private int currentScrollState;
public interface ListViewScrolledToBottomCallback {
public void onScrolledToBottom();
}
public ListViewScrolledToBottomListener(Fragment fragment, ListView listView) {
try {
scrolledToBottomCallback = (ListViewScrolledToBottomCallback) fragment;
listView.setOnScrollListener(this);
} catch (ClassCastException e) {
throw new ClassCastException(fragment.toString()
+ " must implement ListViewScrolledToBottomCallback");
}
}
public ListViewScrolledToBottomListener(Activity activity, ListView listView) {
try {
scrolledToBottomCallback = (ListViewScrolledToBottomCallback) activity;
listView.setOnScrollListener(this);
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement ListViewScrolledToBottomCallback");
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.currentFirstVisibleItem = firstVisibleItem;
this.currentVisibleItemCount = visibleItemCount;
this.totalItemCount = totalItemCount;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
this.currentScrollState = scrollState;
if (isScrollCompleted()) {
if (isScrolledToBottom()) {
scrolledToBottomCallback.onScrolledToBottom();
}
}
}
private boolean isScrollCompleted() {
if (this.currentVisibleItemCount > 0 && this.currentScrollState == SCROLL_STATE_IDLE) {
return true;
} else {
return false;
}
}
private boolean isScrolledToBottom() {
System.out.println("First:" + currentFirstVisibleItem);
System.out.println("Current count:" + currentVisibleItemCount);
System.out.println("Total count:" + totalItemCount);
int lastItem = currentFirstVisibleItem + currentVisibleItemCount;
if (lastItem == totalItemCount) {
return true;
} else {
return false;
}
}
}
You need to add a empty xml footer resource to your listView and detect if this footer is visible.
private View listViewFooter;
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_newsfeed, container, false);
listView = (CardListView) rootView.findViewById(R.id.newsfeed_list);
footer = inflater.inflate(R.layout.newsfeed_listview_footer, null);
listView.addFooterView(footer);
return rootView;
}
Then in your listView scroll listener you do this
#
Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem == 0) {
mSwipyRefreshLayout.setDirection(SwipyRefreshLayoutDirection.TOP);
mSwipyRefreshLayout.setEnabled(true);
} else if (firstVisibleItem + visibleItemCount == totalItemCount) //If last row is visible. In this case, the last row is the footer.
{
if (footer != null) //footer is a variable referencing the footer view of the ListView. You need to initialize this onCreate
{
if (listView.getHeight() == footer.getBottom()) { //Check if the whole footer is visible.
mSwipyRefreshLayout.setDirection(SwipyRefreshLayoutDirection.BOTTOM);
mSwipyRefreshLayout.setEnabled(true);
}
}
} else
mSwipyRefreshLayout.setEnabled(false);
}
If you set a tag on a view of the last item of the listview, later you can retrieve the view with the tag, if the view is null it's because the view is not loaded anymore. Like this:
private class YourAdapter extends CursorAdapter {
public void bindView(View view, Context context, Cursor cursor) {
if (cursor.isLast()) {
viewInYourList.setTag("last");
}
else{
viewInYourList.setTag("notLast");
}
}
}
then if you need to know if the last item is loaded
View last = yourListView.findViewWithTag("last");
if (last != null) {
// do what you want to do
}
Janwilx72 is right,but it's min sdk is 21,so i create this method:
private boolean canScrollList(#ScrollOrientation int direction, AbsListView listView) {
final int childCount = listView.getChildCount();
if (childCount == 0) {
return false;
}
final int firstPos = listView.getFirstVisiblePosition();
final int paddingBottom = listView.getListPaddingBottom();
final int paddingTop = listView.getListPaddingTop();
if (direction > 0) {
final int lastBottom = listView.getChildAt(childCount - 1).getBottom();
final int lastPos = firstPos + childCount;
return lastPos < listView.getChildCount() || lastBottom > listView.getHeight() - paddingBottom;
} else {
final int firstTop = listView.getChildAt(0).getTop();
return firstPos > 0 || firstTop < paddingTop;
}
}
for ScrollOrientation:
protected static final int SCROLL_UP = -1;
protected static final int SCROLL_DOWN = 1;
#Retention(RetentionPolicy.SOURCE)
#IntDef({SCROLL_UP, SCROLL_DOWN})
protected #interface Scroll_Orientation{}
Maybe late, just for latecommers。
If you are using a custom adapter with your listview(most people do!) a beautiful solution is given here!
https://stackoverflow.com/a/55350409/1845404
The adapter's getView method detects when the list has been scrolled to the last item. It also adds correction for the rare times when some earlier position is called even after the adapter has already rendered the last view.
I did that and works for me :
private void YourListView_Scrolled(object sender, ScrolledEventArgs e)
{
double itemheight = YourListView.RowHeight;
double fullHeight = YourListView.Count * itemheight;
double ViewHeight = YourListView.Height;
if ((fullHeight - e.ScrollY) < ViewHeight )
{
DisplayAlert("Reached", "We got to the end", "OK");
}
}
This will scroll down your list to last entry.
ListView listView = new ListView(this);
listView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
listView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
listView.setStackFromBottom(true);

Categories

Resources