ListViews - how to use ArrayAdapter.addAll() function before API 11?
Asked Answered
M

8

17

I'm trying update a ListView with an entirely new ArrayList. For API 11 it works great using the addAll(...) method, but this is not available for earlier APIs. I can't figure out how to go about updating an entire list like this for older versions.

ArrayAdapter<String> textList = new ArrayAdapter<String>(listener, R.layout.list_item, stringList);
listener.setListAdapter(textList);

Then later...

textList.clear();
textList.addAll(stringList); <--- works fine for Android 3.0 (API Level 11) and beyond, but not before. 

How did you go about doing this before addAll() was introduced in API 11? Thanks.

Miltie answered 13/3, 2012 at 1:29 Comment(0)
H
11

The simplest way is avoid using ArrayAdapter.addAll() and ArrayAdapter.add() within a loop, like idiottiger suggested in his answer.

If you insist to use ArrayAdapter.addAll(), The short answer is DIY. Check out the source of android.widget.ArrayAdapter here, the actual implementation is much simpler than you thought. there are many alternatives to achieve this, for instance:

  • Option 1: Implement you own ArrayAdapter extends android.widget.BaseAdapter, you get full control of private instance variable and method and can define whatever method your want in your own implementation. there are many tutorial on the internet tells how to create custom adapter, like here and here.
  • Option 2: Implement you own ArrayAdapter extends android.widget.ArrayAdapter, then add the required public method addAll() to your own ArrayAdapter implementation, you don't have the visibility on private member in android.widget.ArrayAdapter so need use existing public API ArrayAdapter.add() to add every single element within a loop.

Option 1 is preferred and used very commonly, especially in the situation when you need render more complex custom UI stuff within your ListView.

Herthahertz answered 13/3, 2012 at 2:48 Comment(5)
wouldn't it be overkill to implement his own version of ArrayAdapter from BaseAdapter just to have an addAll method? Building it from the ArrayAdapter should be enough.Casandracasanova
Yeah, still worth to check the source to understand how Adapter is implemented in general.Herthahertz
this is somewhat unrelated, but check out this post if you're using Eclipse to develop Android applications: helloandroid.com/content/…Casandracasanova
Thanks, I suppose this is the answer. Unless it's possible to somehow use the notifyDataSetChanged() method like Josephus suggested below? How does this method work?Miltie
@mattboy, notifyDataSetChanged() is taken care by the underlying framework (defined in android.widget.BaseAdapter), every class extends should have this feature inherited, like the built-in API android.widget.ArrayAdapter.Herthahertz
M
19

Here's the full code block that uses the native addAll() for Android devices with SDK_INT >= 11, and uses the loop workaround for devices with API level less than 11.

@TargetApi(11)
public void setData(List<String> data) {
    clear();
    if (data != null) {
        //If the platform supports it, use addAll, otherwise add in loop
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            addAll(data);
        } else {
            for(String item: data) {
                add(item);
            }
        }
    }
}

The @TargetApi(11) annotation is used with ADT 17 to suppress Lint warnings when you have a <uses-sdk android:minSdkVersion="X"/> in the AndroidManifest.xml where X is less than 11. See http://tools.android.com/recent/lintapicheck for more info.

Macomber answered 7/9, 2012 at 14:48 Comment(4)
good answer, but let me share with you an experience of mine: if you use "add" method with more than x elements (I've set it to 100 finally), as after every "add" method there will be a "notifydatasetchanged" call, there will be a crash!Boulder
@Boulder with that many records, perhaps you're better off using a CursorAdapter? See #21130662.Macomber
That is not a bad idea, but I'm not using a database, the records are retreived from a JSON response from the server.Boulder
I've added setNotifyOnChange(false); / notifyDataSetChanged(); pair: if you have hundreds of items (~800 in my case), this improves performance dramatically (1500 ms vs <1 ms), as far as this prevents invoking notifyDataSetChanged() after every add().Tefillin
H
11

The simplest way is avoid using ArrayAdapter.addAll() and ArrayAdapter.add() within a loop, like idiottiger suggested in his answer.

If you insist to use ArrayAdapter.addAll(), The short answer is DIY. Check out the source of android.widget.ArrayAdapter here, the actual implementation is much simpler than you thought. there are many alternatives to achieve this, for instance:

  • Option 1: Implement you own ArrayAdapter extends android.widget.BaseAdapter, you get full control of private instance variable and method and can define whatever method your want in your own implementation. there are many tutorial on the internet tells how to create custom adapter, like here and here.
  • Option 2: Implement you own ArrayAdapter extends android.widget.ArrayAdapter, then add the required public method addAll() to your own ArrayAdapter implementation, you don't have the visibility on private member in android.widget.ArrayAdapter so need use existing public API ArrayAdapter.add() to add every single element within a loop.

Option 1 is preferred and used very commonly, especially in the situation when you need render more complex custom UI stuff within your ListView.

Herthahertz answered 13/3, 2012 at 2:48 Comment(5)
wouldn't it be overkill to implement his own version of ArrayAdapter from BaseAdapter just to have an addAll method? Building it from the ArrayAdapter should be enough.Casandracasanova
Yeah, still worth to check the source to understand how Adapter is implemented in general.Herthahertz
this is somewhat unrelated, but check out this post if you're using Eclipse to develop Android applications: helloandroid.com/content/…Casandracasanova
Thanks, I suppose this is the answer. Unless it's possible to somehow use the notifyDataSetChanged() method like Josephus suggested below? How does this method work?Miltie
@mattboy, notifyDataSetChanged() is taken care by the underlying framework (defined in android.widget.BaseAdapter), every class extends should have this feature inherited, like the built-in API android.widget.ArrayAdapter.Herthahertz
S
4

I combined barbeau and Villarey's answers into what I think is a good solution:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void setData(List<String> data) {
    clear();
    if (data != null) {
        addAll(data);
    }
}


@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void addAll(String... items) {
    //If the platform supports it, use addAll, otherwise add in loop
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        super.addAll(items);
    }else{
        for(String item: items){
            super.add(item);
        }
    }
}
Smatter answered 13/12, 2012 at 0:43 Comment(0)
B
3

I built on other peoples code online and I created this. Just use this class wherever needed instead of the ArrayAdapter class.

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.widget.ArrayAdapter;

import java.util.Collection;
import java.util.List;

public class ArrayAdapterCompat<T> extends ArrayAdapter<T> {
    public ArrayAdapterCompat(Context context, int resource, List<T> entries) {
        super(context, resource, entries);
    }

    /**
     * Add all elements in the collection to the end of the adapter.
     * @param list to add all elements
     */
    @SuppressLint("NewApi")
    public void addAll(Collection<? extends T> list) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            super.addAll(list);
        } else {
            for (T element : list) {
                super.add(element);
            }
        }
    }

    /**
     * Add all elements in the array to the end of the adapter.
     * @param array to add all elements
     */
    @SuppressLint("NewApi")
    public void addAll(T... array) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            super.addAll(array);
        } else {
            for (T element : array) {
                super.add(element);
            }
        }
    }
}
Barroom answered 15/12, 2013 at 6:4 Comment(0)
W
1

using textList.add method, loop in stringlist, and one by one added to the textList.

Wendell answered 13/3, 2012 at 1:48 Comment(0)
C
1

You can extend ArrayAdapter on a custom class, and implement your own addAll method. It would be simpler if you use an ArrayList instead of String array, so you can add data without rebuilding the whole data set.

Edit: I remembered that you can just modify the supplied array (or arraylist) that you fed into your adapter, and then call notifyDataSetChanged. This should update your list.

Casandracasanova answered 13/3, 2012 at 3:13 Comment(1)
So you mean in this case I would simply change the values of the variable stringList then call textList.notifyDataSetChanged();? I have actually tried this but it didn't work. Am I missing something? I think I read somewhere else that this method only works if you edited the adapter .add or .clear or .remove?Miltie
C
0

I came across this thread late, but the following solution was easy to implement for me:

public class CustomAdapter extends ArrayAdapter<String>...

public void setData(ArrayList<String> items) {
    clear();
    setNotifyOnChange(false);
    if (items != null) {
        for (String item : items)
            add(item);
    }
    notifyDataSetChanged();
}
Contagium answered 6/9, 2012 at 14:8 Comment(0)
P
0

just you need to simple iteration over the collection with if statement for the version

        //mForecastAdapter.clear();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            mForecastAdapter.addAll(weekForecast);
        }else {
            for (String s : strings) {
                mForecastAdapter.add(s);
            }
        }
Plethoric answered 7/1, 2016 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.