13

I'm wondering if there's a way to add an item to the ListView without causing the reload of the whole list.

I have a BaseAdapter-derived ListView adapter, and when the underlying model gets a new element added, it calls notifyDataSetChanged(), which triggers the ListView reload.

The elements in the list have images, which are dynamically loaded according to the element's content. Problem is, when getView() is called during the reload, the convertView parameter passed for reuse is from a different position previously, so the images have to be reloaded, too, which causes a rather ugly blinking.

So is there a way to not reload the whole list if I only add an item at the end (and that will be the only way new items are added)? Or at least somehow reuse the cells for the same position, if possible, to avoid the costly image reload?

SVD
  • 4,673
  • 2
  • 24
  • 38
  • 1
    Did you ever find out how to get rid of the blinking? Could you share your solution? There's got to be a way as I see some apps that loads extra data and the images showing do not blink. – Bitcoin Cash - ADA enthusiast Apr 15 '14 at 21:51

4 Answers4

5

No buddy its not possible in android that u can add an item without refresh the list because when u add an item it changes the height of list view.See the below link,Romain Guy said the same thing in it.

http://groups.google.com/group/android-developers/browse_thread/thread/7e54522c37772d04/05abfd17b59b07f7?lnk=gst&q=how+to+add+list+item+in+listview+without+reload+the+listview+#05abfd17b59b07f7

himanshu
  • 1,962
  • 3
  • 18
  • 36
1

do this instead of calling notifyDataSetChanged():

int firstVisiblePosition = getFirstVisiblePosition();
View view = getChildAt(0);
int distFromTop = (view == null) ? 0 : view.getTop();
setSelectionFromTop(firstVisiblePosition, distFromTop);
Nic Ang
  • 11
  • 1
  • While this does maintain the scroll position, it still triggers a reload of the views within the ListView (that is, getView is called again with recycled Views). In my implementation, which uses AsyncTasks to load, this means that the ListView "stays still" but causes loading indicators to show up again while it refreshes. – Zane Claes Sep 19 '12 at 19:16
1

Well, it is possible.

Override the methods getView and add of the ArrayAdapter

What i did for example as testcase:

public class custom_row extends ArrayAdapter<String> {

String newItem = "";
Boolean doRefresh = true;

public custom_row(Context context, int resource, ArrayList<String> objects) {
    super(context, resource, objects);
}

public custom_row(Context context, int resource, String[] objects) {
    super(context, resource, objects);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    String itemValue = getItem(position);

    if (doRefresh == false && itemValue != newItem) {
        return convertView;
    }

    LayoutInflater inflater = LayoutInflater.from(getContext());

    View customView = inflater.inflate(R.layout.customr_row, parent, false);
    ImageView myImageView = (ImageView) customView.findViewById(R.id.imageView);

    String url = "https://urltoimage/image.jpg";

    (new DownloadImageTask(myImageView)).execute(url);

    TextView myTextView = (TextView) customView.findViewById(R.id.myCustomText);
    myTextView.setText(itemValue);

    return customView;
}

public void addNewItemToList(String item) {
    this.newItem = item;
    this.doRefresh = false;
    add(item);
}

private class DownloadImageTask extends AsyncTask<String, Integer, Bitmap>{
    private ImageView mImageView;

    public DownloadImageTask(ImageView imageView) {
        this.mImageView = imageView;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        URL url = null;
        Bitmap bmp = null;

        try {
            url = new URL(params[0]);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        try {
            InputStream stream = url.openStream();
            bmp = BitmapFactory.decodeStream(stream);

        } catch (IOException e) {
            e.printStackTrace();
        }

        return bmp;
    }

    @Override
    protected void onPostExecute(Bitmap bmp) {
        this.mImageView.setImageBitmap(bmp);
    }
}

}

The first time it loads all the items and the images are loaded dynamically by the Async thread. The following part of the code in method getView prevents the list from refreshing it completely, so preventing the blinking of the images:

    String itemValue = getItem(position);

    if (doRefresh == false && itemValue != newItem) {
        return convertView;
    }

The ArrayAdapter will go through the list again when a new item is added to the list. Just check whether the item at the current position is the newly added item. If not then return the convertView which is the old view or else load the item and return the custom view.

user1881928
  • 576
  • 5
  • 8
1

If you set your adapter to have stable ids then it should prevent getView(...) from being called for the views that were already visible before calling notifyDataSetChanged(). This can be done by overriding hasStableIds() so that it returns true and then making sure that your implementation of getItemId(...) is actually returning a stable and unique id.

@Override
public long getItemId(int position) {
    //you can use position if you only append items, otherwise you will
    //need to handle the ids on your own and keep them stable and unique
    final long id = position;

    return id;
}

@Override
public final boolean hasStableIds() {
    return true;
}
foob
  • 1,188
  • 2
  • 10
  • 11