Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knowing when a View in a ListView has gone off the screen?

Tags:

I have Googled this but can't find an answer, so here goes...

I have a ListView that displays some text and an image. The underlying adapter recycles the views for performance reasons (as per the recommended approach), and I load the images using an AsynchTask as per the recommendation found on the Android Developers site's bitmap page.

Everything works perfectly and smoothly, but I have one issue. When the View in the adapter is recycled, it still has a reference to the old image (ImageView). If the user scrolls slowly, then the AsynchTask has enough time to load the new image and display it, so there is no visible reloading of the new image to the user.

However, if the user scrolls very quickly, the delay in loading the image means they see the old image (that was loaded when the View was being used by another item) before its replaced by the new image.

So, my question is, how can I detect when a View is no longer visible on the screen, so I can then remove the image? This would mean the user sees an empty list view item that will eventually be loaded with the appropriate image, which would look better.

Many thanks in advance. Here is the list view adapter code.

@Override public View getView(int position, View convertView, ViewGroup parent) {     View view = convertView;     ListViewHolder viewHolder;      // if this is not a recycled view then create a new view for the data...     if (view == null)     {         view = this.inflater.inflate(R.layout.target_list_view_layout, null, true);          viewHolder = new ListViewHolder();          viewHolder.manufacturer = (TextView) view.findViewById(R.id.manufacturer);         viewHolder.targetName = (TextView) view.findViewById(R.id.targetName);         viewHolder.targetThumbnail = (ImageView) view.findViewById(R.id.targetThumbnail);          view.setTag(viewHolder);     } else     {         viewHolder = (ListViewHolder) convertView.getTag();     }      TargetDescriptor targetDescriptor = this.selectedTargets.get(position);      viewHolder.manufacturer.setText(targetDescriptor.manufacturer);     viewHolder.targetName.setText(targetDescriptor.targetName);      // At this point I pass the image view reference to my background task to load the image     LoadImageViewAsynchTask loadImageTask = new LoadImageViewAsynchTask(viewHolder.targetThumbnail, targetDescriptor);     loadImageTask.execute(new Integer[]     { 64, 64 });      return view; } 

EDIT: Those that use the eBay Android App can see the effect I am looking for if that helps.

like image 609
Justin Phillips Avatar asked Oct 27 '13 17:10

Justin Phillips


People also ask

How check ListView is empty?

just check if (cartlist. size()<0) then yours list is Empty.!

What does list view look like?

Android ListView is a ViewGroup that is used to display the list of items in multiple rows and contains an adapter that automatically inserts the items into the list. The main purpose of the adapter is to fetch data from an array or database and insert each item that placed into the list for the desired result.

What is the function of list view?

A list view is an adapter view that does not know the details, such as type and contents, of the views it contains. Instead list view requests views on demand from a ListAdapter as needed, such as to display new views as the user scrolls up or down.

What is ListView explain anyone with suitable example?

Android ListView is a view which groups several items and display them in vertical scrollable list. The list items are automatically inserted to the list using an Adapter that pulls content from a source such as an array or database.


2 Answers

There is actually a simpler way to do that, you can use a listener :

mListView.setRecyclerListener(new AbsListView.RecyclerListener() { @Override     public void onMovedToScrapHeap(View view) {   } }); 
like image 73
oznus Avatar answered Nov 02 '22 09:11

oznus


Can't believe how stupid I have been, this is a really easy one liner to solve!

Basically in my adapter if the view is being re-cycled, I simply need to null the ImageView bitmap before the view is re-used. That means the image in the list view item is blank before the next image is loaded. I no longer have the issue of the old image being there whilst the new image is being loaded in the background.

I have also made a change in the adapter that cancels any current AsynchTask bound to the view that might still be loading a previous image that is no longer needed. This was very prevalent when I scroll very fast through the list view, with often two or more image loads backing up, so the image would change several times before settling on the correct image.

Here is the new code, with the changes commented so you can see what is happening:

@Override public View getView(int position, View convertView, ViewGroup parent) {     ListViewHolder viewHolder;      // if this is not a recycled view then create a new view for the data...     if (convertView == null)     {         convertView = this.inflater.inflate(R.layout.target_list_view_layout, null, true);          viewHolder = new ListViewHolder();          viewHolder.manufacturer = (TextView) convertView.findViewById(R.id.manufacturer);         viewHolder.targetName = (TextView) convertView.findViewById(R.id.targetName);         viewHolder.targetThumbnail = (ImageView) convertView.findViewById(R.id.targetThumbnail);          convertView.setTag(viewHolder);     } else     {         viewHolder = (ListViewHolder) convertView.getTag();          // Cancel the previous attempt to load an image as this is going to be superceded by the next image         viewHolder.loadImageViewAsynchTask.cancel(true);          // Clear down the old image so when this view is displayed, the user does not see the old image before the         // new image has a chance to load in the background         viewHolder.targetThumbnail.setImageBitmap(null);     }      TargetDescriptor targetDescriptor = this.selectedTargets.get(position);      viewHolder.manufacturer.setText(targetDescriptor.manufacturer);     viewHolder.targetName.setText(targetDescriptor.targetName);      LoadImageViewAsynchTask loadImageViewAsynchTask = new LoadImageViewAsynchTask(viewHolder.targetThumbnail);     loadImageViewAsynchTask.setTargetDescriptor(targetDescriptor);     loadImageViewAsynchTask.execute(new Integer[]     { 64, 64 });      // Keep a reference to the task so we can cancel it if the view is recycled next time round to prevent     // un-neccessary image loads that are out of date     viewHolder.loadImageViewAsynchTask = loadImageViewAsynchTask;      return convertView; } 
like image 23
Justin Phillips Avatar answered Nov 02 '22 07:11

Justin Phillips