I have a requirement
I have a large data have to put in the list view and it's too stupid to load all data and populate in list view so i load 10 first item from server and populate in listView.
So everytime user scroll down at the bottom of listview ( They viewed all first 10 item ) my app will load the next 10 item automatically.
Problem is : Is there anyway that i can detect that whether user is at the bottom of the first 10 item or not ?
Sorry about my English . appreciate for any help !
You can detect the end of scrolling by the following code
if (yourListView.getLastVisiblePosition() == yourListView.getAdapter().getCount() -1 &&
yourListView.getChildAt(yourListView.getChildCount() - 1).getBottom() <= yourListView.getHeight())
{
//It is scrolled all the way down here
}
Hope it helps.
You can register a listener to the OnScroll event and then detect where you have to start reloading data:
ListView listView = (ListView)findViewById(R.id.list_view_id);
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int amountVisible, int totalItems) {
//now get the point where you have to reload data
if (firstVisibleItem+1 + amountVisible > totalItems) {
//reload your data here
}
}
});
Where's the data coming from ? It's common to load large data sets from a DB via a cursor, and then use a simple cursor adapter to populate the list view. (And I'm talking 10,000s of rows). So long as you do it in the background (cursor loader) it shouldn't be a problem, and it probably a lot easier than trying to manage the scrolling yourself.
Try this, it implements a listview that pulls more items when the user reaches the bottom, also using a progressbar in the bottom when is loading more items.
I have used it before and it works well for what you need.
Related
I have a list that displays a fairly complex layout for each list item and there is noticable lag when I scroll. Yes, I have already put it in AsyncTask and used a View Holder, as suggested here http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/ and http://developer.android.com/training/improving-layouts/smooth-scrolling.html
I think the next thing to try is to load all items in the list when the page is loading (even those that will be off screen), but I cannot seem to find a way to do this. And, yes, I understand that this is a very bad idea and goes against the point of a ListView in a way, if you are expecting many list items. But I should never have more than 20 items in my case and I cannot think of what else to try to make it faster when the user scrolls. So, I would like to be able to control what gets loaded into my ListView adapter, not just take the default of "whatever is visible on the screen at the time".
I think I understand from http://android.amberfog.com/?p=296 and How ListView's recycling mechanism works how the ListView recycling works. But I cannot see a way to load the items that are not currently appearing on the screen, so that it does not have to create a new view when the user scrolls. So my question is: is it possible to load off screen items in the list view so they are not created when the user scrolls? If so, how?
Thanks in advance.
Use this listener object to detect if visibleThreshold number of items are still to be rendered in the list. And based on that condition fire your Asynk task to load the data in advance for the rest of elements in list. This kind of technique is used in adding Footer view also.
listenerObject = new AbsListView.OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
int visibleThreshold = 3;
if (loading == false && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
loading = true;
//DO YOUR JOB HERE i.e. your async task
// once your async Task is complete make loading as false
}
}
};
lv.setOnScrollListener(listenerObject);
Here lv is the listview.
And in case your data is already loaded then you don't have to worry about view creation because it is already taken care of by Android's mechanism to optimise performance.
My ListView is displaying 25 items at a time and I can only download 25 items at a time from a web service that I am trying to use. Please could I have some suggestions as to how I would load the next 25 items to the List view with the option to go back to the previous ListView ( and hence the previous 25 itmes)? Is there a standard way of doing this? I have tried searching around and there appears to be nothing in the standard reference books. Many thanks.
modify the getCount() in ListViewAdapter
You have three options to that actually
1) If you want that your next 25 item will load as soon as you reached the end of the ListView, so for that you have to 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) {
// 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
// load your next 25 items here
Log.d("Last", "Last");
preLast = lastItem;
}
}
}
2) If you need that there should be a button at the end of the ListView which onClick returns next 25 items, then you should add a footer view for your ListView. For example refer this tutorial
3) You can use PullToRefresh libraries to load more data, which are easily available. One of them is here
So you can choose either of the three options whichever meets your requirements right now.
As i understand, you want:
Currently, i fetch 25 items and then display it on my list view: OK
Then (press next, or do st), I load next my 25 items and push it into the list view :OK
Now if i go back, i want my list view display that previous data...
There are two possible way to achieve that.
You request again and then update the list view
You store your all items in an array, and in your adapter, define the getCount() function to return 25 - the data in the list: http://developer.android.com/reference/android/widget/Adapter.html
Some important points to do:
Load data: You may need an asyntask for this. (if not,remember the network process may block the UI and leads an Android not responding.)
In your adapter, getCount() to return 25, and Call your adapter.notifyDatasetChanged() to update the data list ( may be in postExecute());
I have an ArrayAdapter linked to a ListView.
mListView.setAdapter(mArrayAdapter);
Whenever I reset the ArrayList data to the ArrayAdapter:
mArrayAdapter.clear();
mArrayAdapter.addAll(mArrayList);
mArrayAdapter.notifyDataSetChanged()
the ListView gets correctly updated
however, if just after the above three lines, I call my custom method mListView.hasScrollbar() to detect whether the listview has a scrollbar or not, I get a null lastVisibleItem:
public boolean hasScrollbar() {
View lastVisibleItem = (View) getChildAt(getChildCount() - 1);
if (lastVisibleItem.getBottom()>=getHeight()) {
return true;
}
return false;
}
does it mean that the listview is still refreshing?
My main question is:
how can I test if the listview has the scrollbar after resetting the adapter with new data?
thank you for any help!
Using getLastVisiblePosition / getFirstVisiblePosition is a valid method of detecting wether you have scrolling or not within the list view (aslong as you compare it to getCount() and do your math ofc).
The problem you have as you already guess is that you are attempting to check out of sync.
In order to sync your query when the adapter already filled your List Data and updated changes, you need to issue a post request to the list, which will stack that petition to the message queue of the adapter.
yourAdapter.notifyDataSetChanged();
yourAdapter.getListView().post(new Runnable() {
#Override public void run() {
//your code here
}
});
Make sure to call that after notifySetDataChanged() of course. Because you want the list to update before the check.
I think your question equals to "how to tell if list view contains items need to displayed on more than one screen"?
So my suggestion is to use listView.getFirstVisiblePosition() and getLastVisiblePosition() to tell it.
I'm having a bit of trouble preserving the scroll position of a list view when changing it's adapter's data.
What I'm currently doing is to create a custom ArrayAdapter (with an overridden getView method) in the onCreate of a ListFragment, and then assign it to its list:
mListAdapter = new CustomListAdapter(getActivity());
mListAdapter.setNotifyOnChange(false);
setListAdapter(mListAdapter);
Then, when I receive new data from a loader that fetches everything periodically, I do this in its onLoadFinished callback:
mListAdapter.clear();
mListAdapter.addAll(data.items);
mListAdapter.notifyDataSetChanged();
The problem is, calling clear() resets the listview's scroll position. Removing that call preserves the position, but it obviously leaves the old items in the list.
What is the proper way to do this?
As you pointed out yourself, the call to 'clear()' causes the position to be reset to the top.
Fiddling with scroll-position, etc. is a bit of a hack to get this working.
If your CustomListAdapter subclasses from ArrayAdapter, this could be the issue:
The call to clear(), calls 'notifyDataSetChanged()'. You can prevent this:
mListAdapter.setNotifyOnChange(false); // Prevents 'clear()' from clearing/resetting the listview
mListAdapter.clear();
mListAdapter.addAll(data.items);
// note that a call to notifyDataSetChanged() implicitly sets the setNotifyOnChange back to 'true'!
// That's why the call 'setNotifyOnChange(false) should be called first every time (see call before 'clear()').
mListAdapter.notifyDataSetChanged();
I haven't tried this myself, but try it :)
Check out: Maintain/Save/Restore scroll position when returning to a ListView
Use this to save the position in the ListView before you call .clear(), .addAll(), and . notifyDataSetChanged().
int index = mList.getFirstVisiblePosition();
View v = mList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
After updating the ListView adapter, the Listview's items will be changed and then set the new position:
mList.setSelectionFromTop(index, top);
Basically you can save you position and scroll back to it, save the ListView state or the entire application state.
Other helpful links:
Save Position:
How to save and restore ListView position in Android
Save State:
Android ListView y position
Regards,
Please let me know if this helps!
There is one more use-case I came across recently (Android 8.1) - caused by bug in Android code. If I use mouse-wheel to scroll list view - consecutive adapter.notifyDataSetChanged() resets scroll position to zero. Use this workaround until bug gets fixed in Android
listView.onTouchModeChanged(true); // workaround
adapter.notifyDataSetChanged();
More details is here: https://issuetracker.google.com/u/1/issues/130103876
In your Expandable/List Adapter, put this method
public void refresh(List<MyDataClass> dataList) {
mDataList.clear();
mDataList.addAll(events);
notifyDataSetChanged();
}
And from your activity, where you want to update the list, put this code
if (mDataListView.getAdapter() == null) {
MyDataAdapter myDataAdapter = new MyDataAdapter(mContext, dataList);
mDataListView.setAdapter(myDataAdapter);
} else {
((MyDataAdapter)mDataListView.getAdapter()).refresh(dataList);
}
In case of Expandable List View, you will use
mDataListView.getExpandableListAdapter() instead of
mDataListView.getAdapter()
I have a custom listview(with two image and 5 textviews) in which I have to show more than 200 data when I load it at first time with all data then it returns out of memory exception, to resolve the same problem I want that when we scrolled down the listview till the last item of the list then it app again adds more data to the same list. It running as same till we have got all the data on the list view. Please don't tell me to use EndlessAdapter because Endlessadapter always downloading the items after each 10 seconds. Which returns also the outof memory after some time.
Thanks in advance.
assign a List http://developer.android.com/reference/java/util/List.html to your addapter then you can call item.add and item.remove
please try this
endless listView
Thanks
I'd recommend that every time a user scrolls you check the position of the first element of the ListView.
ListView.getFirstVisiblePosition()
If it's close to the amount of the elements in your list you can add items to the bottom of the list and remove the ones from the top of the list.
I did something like this to load in more elements when the user scrolled down on an ExpandableListView once.
myListView.setOnScrollListener( new OnScrollListener()
{
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
int lastVisibleElement = firstVisibleItem + visibleItemCount;
if(lastVisibleElement == totalItemCount)
{
//Load elements
myListAdapter.notifyDataSetChanged();
}
}
about out of memory , there is no way that 5 imageViews and 5 textViews will cause out of memory . you are probably saving all of the 200 images together instead of using some sort of caching (like softreference and LruCache) . the whole point of the adapter is to show tons of items (not at the same time , of course) using minimal memory . you can see that even the play store (which i still like to call android market) app uses memory cache if you play enough with scrolling ...
for more information about listview, watch this
you might also want to read more about handling images here .
anyway , you can take the getView as a trigger (using the position parameter compared to the getCount() value) to when to load more items and then update the adapter using notifyDatasetChanged (and update the getCount() value) .