Android ListView loading control - android

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.

Related

Detect which item is currently scrolled to in RecyclerView

I'm trying to implement an autoplay feature for video items in a RecyclerView (linear vertical layout). I can't figure out how to know when a certain item is currently on/off the screen so I can autoplay/pause the video. If I put the code in onBindViewHolder method all videos start playing simultaniously. Couldn't find a solution by googling it either. Help, please!
For Recyclerview you should rely on your layout manager to give you this information.
https://developer.android.com/reference/android/support/v7/widget/GridLayoutManager
or
https://developer.android.com/reference/android/support/v7/widget/LinearLayoutManager
Assign a layout to the RecyclerView when you assign the adapter. Then use it to see what is visible.
Let me help a bit more with some Psedo Kotlin code for you to help with the player aspect.
Let's pretend you have some object that is bound into each row that can trigger the playing and has a unique ID. Let's call that an ActiveRowPlayer for this example.
NOTE*
If you are using databinding, it is simple to bind your video player playing content to a property in your model that is populating the row, but that's different story for a different post.
You can make an interface like:
interface IActivePlayerUpdater{
fun onUpdateCurrentPlayer
}
You can make a helper method like:
in your activity, you can implement an interface like :IActivePlayerUpdater and override the methods for it.
override fun onUpdateCurrentPlayer(){
var activeRowPlayer = recyclerView.layoutManager.findFirstCompletelyVisibleItemPosition()
if(activeRowplayer.someID != currentRowPlayer.someID){
currentRowPlayer.stopPlaying
currentRowPlayer = activeRowPlayer
activeRowPlayer.startPlaying
}
}
Then pass it into your adapter and just monitor your onBind method and anytime a new onBind is called that means the content has moved enough to trigger a new row item.
MyAdapterConstructor(IActivePlayerUpdater myCallback)
fun recyclerView.onBindMethod(stuffThatComesHere){
//do normal stuff
myCallback.onUpdateCurrentPlayer()
}
Keep in mind, this is just pseudo to help you on your journey. not intended to be direct copy and paste.
----NOTE* REQUESTED FROM COMMENT TO SUPPLY HOW TO TOUCH VIEWMODEL FROM OUTSIDE OF ADAPTER---
#Goran, this example I had setup a long time ago to avoid
notifyDataSetChanged on selection changed to toggle a checkbox for
each item. I eventually moved to a better option, but you asked how do
you get the viewmodel, here is a sample. rvCameraRoll was my
recyclerView, I was using it to display camera media, but that is not
relevant, just focus on getting the viewModel piece.
The only part you should care about is getting the ViewHolder, I just left the rest there in case it helps you with anything else.
int count = rvCameraRoll.getChildCount();
for (int i = 0; i < count; i++) {
MediaModelGridAdapter.ViewHolder childRow = (MediaModelGridAdapter.ViewHolder)rvCameraRoll.getChildViewHolder(rvCameraRoll.getChildAt(i));
if(isVisible) {
childRow.imgCellSelected.setVisibility(View.VISIBLE);
if(getMediaModelList().get(i).getIsSelected()){
childRow.imgCellSelected.setBackgroundResource(R.drawable.ic_ap_selected_on);
}
}else{
//check that exists, because after fresh delete list may be short while updating cells
if(getMediaModelList().size() > i) {
getMediaModelList().get(i).setIsSelected(false);
}
childRow.imgCellSelected.setBackgroundResource(R.drawable.ic_ap_selected_off);
childRow.imgCellSelected.setVisibility(View.GONE);
}
}
OLD
leaving this for anyone using a ListView
There is a OnScrollListener for ListView
You can override the
onScrollStateChanged(AbsListView view, int scrollState)
and
the
onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
this will give you the ListView item that is visible. So by using the onScroll you can detect which items are visible and determine which one to play and stop playing.
if you use with recyclerview
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.OnScrollListener
The RecyclerView is only holding the items and the LayoutManager is responsible for displaying the items, so in order to get the ones that are visible to the user, assuming that you use LinearLayoutManager, you should call :
((LinearLayoutManager)recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
or
((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition();

ListView - loading 25 items at a time but would like to load the next 25

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

detect scroll down in listView android

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.

Add more items in ListView when we scrolled down it

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) .

Problem with getListView().getFirstVisiblePosition()

I'd like to have my application do some on-screen changes (outside the ListView) based on what the first visible item in my list is.
What I've done is override the onScroll handler. When this handler is invoked, I set the position of my Cursor to the first visible position so I can look at the data for that row:
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mCursor.moveToPosition(getListView().getFirstVisiblePosition());
...
I then reference the data in the Cursor to make my display changes.
Here's the thing: This working is spotty. Most of the time it works perfectly. I can't make it screw up.
However, sometimes getListView().getFirstVisiblePosition() does not return 1 until the 3rd item (index 2) is really the first visible item. It displays 0 when the 0 index is showing. It displays 0 when the 1 index is showing. It displays 1 when the 2 index is showing and so on. Additionally, it only does this when the user is scrolling down the list. If the user is scrolling backwards (up), getFirstVisiblePosition() returns the correct value.
I'm wondering if this isn't related to some efficiency problem. This handler is going to execute over and over and over as the user scrolls.
Does anyone have any ideas on what could be the problem? Does anyone have any better solutions?
Update:
Do I remember hearing that getFirstVisiblePosition() is really only a guess; and that Android essentially supposes what index might be available by rows' probable height? If this is so, then I have a problem. Some rows in my list are bigger than others. If this is the case, is there any way around it?
I would actually perform your call in onScrollStateChanged and only execute when scrolling is idle. You will drastically cut down on number of executed requests. Something like this:
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
// do yer stuff
}
}

Categories

Resources