3

I have an activity that draws some icons on the view.

It sets a list and listAdapter in onCreate().

I have extracted to public method the code that assign the list to my adapter.

So that external code, can call the UI thread and assign new list to the adapter and make it notify the change via notifyDataSetChange()

However the new icons are not drawn, but only after leaving and getting back to the Activity.

How can I fix this?

I have tried adapter.clear()

and doubled checked the UI thread runs this code.

what else?

public class CategoriesActivity extends ActivityBase {
    private Category[] categories;

    SettingValueAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.settings_values);
        ((TitleBar)findViewById(R.id.theTitleBar)).init(this,DisplayStrings.DS_CATEGORIES);

        adapter = new SettingValueAdapter(this);

        DriveToNativeManager nativeManager = DriveToNativeManager.getInstance();
        nativeManager.getCategories(new CategoriesListener() {

            @Override
            public void onComplete(Category[] aCategories) {
                categories = aCategories;
                refreshListIcons();
            }
        });
        final ListView list = (ListView)findViewById(R.id.settingsValueList);
        list.setAdapter(adapter);
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            ..
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        ..
    }

    public void refreshListIcons()  {
        NativeManager nativeManager = AppService.getNativeManager();
        SettingsValue[] values = new SettingsValue[categories.length];
        for (int i = 0; i < categories.length; i++) {
            values[i] = new SettingsValue(categories[i].value, nativeManager.getLanguageString(categories[i].displayString), false);
            values[i].icon = ResManager.GetSkinDrawable(categories[i].iconName + ".bin");
        }
        adapter.setValues(values);
    }   
}




public class SettingValueAdapter extends BaseAdapter {

    private SettingsValue[] values;
    private LayoutInflater inflater;
    public SettingValueAdapter(Context context) {
        inflater = LayoutInflater.from(context);
    }

...

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.settings_item, null);
        }
        SettingsValue item = values[position];
        CheckedTextView name = (CheckedTextView) convertView.findViewById(R.id.itemText);
        ImageView iconView = (ImageView) convertView.findViewById(R.id.itemIcon);
        if (iconView != null && (item != null) && (item.icon != null)) {
            iconView.setImageDrawable(item.icon);
            iconView.setVisibility(View.VISIBLE);
        } else {
            iconView.setVisibility(View.GONE);
        }
        name.setText(item.display);
        name.setChecked(item.isSelected);
        View container = convertView.findViewById(R.id.itemContainer);
        if (position == 0) {
            ..
        return convertView;
    }
    public void setValues(SettingsValue[] values) {
        this.values = values;
        notifyDataSetChanged();
    }

}
Elad Benda
  • 35,076
  • 87
  • 265
  • 471
  • Even though you say you're sure it's in UI thread, please try to put `refreshListIcons()` call inside `CategoriesActivity.this.runOnUiThread()` – Piotr Chojnacki Jun 27 '13 at 10:18
  • didn't help. I have tried. Can it even cause a crash ? – Elad Benda Jun 27 '13 at 18:24
  • It shouldn't. Edit your question and post how did you try it and what error did you get in logcat. – Piotr Chojnacki Jun 28 '13 at 05:19
  • My application crashes. Maybe in the native thread, how can I track the crash effectovly in java? in c? – Elad Benda Jun 28 '13 at 09:42
  • instead of using this.values = values; i'd have values declared as a list, and initiate the new list, and call this.values = new arraylist() and this.values.addall(values) and only then call notifydatasetchanged. it always works for me. – meh Jun 28 '13 at 12:28
  • @meh I'll try. but what the difference? why assigning `values = values;` doesn't work? – Elad Benda Jun 30 '13 at 07:11
  • @meh it's problematic to change values from `Array` to `ArrayList`. It's a common code and many others call it – Elad Benda Jun 30 '13 at 07:14
  • geCount returns values.lenght? what does getItemId returnd? – Blackbelt Jun 30 '13 at 09:16
  • @EladBenda I prefer using lists, never encountered a problem with a list as my data set. – meh Jun 30 '13 at 19:37
  • @meh how is it done with immutable set, as Array? There is got to be a way, no? – Elad Benda Jun 30 '13 at 22:17
  • 1
    Is your problem just for displaying the new icons, or you don't see any change at all when calling notifyDataSetChanged ? – Gomino Jul 02 '13 at 12:05

3 Answers3

2

You should extend ArrayAdapter instead of BaseAdapter:

http://developer.android.com/reference/android/widget/ArrayAdapter.html

This class is suitable for Arrays and Collections and it handles correctly all concerns regarding internal state.

In this case use clear followed by addAll at Adapter object itself before call notifyDataSetChanged. Another aproach is to change the Array or Collection bound to your Adapter.

I've found a very well done tutorial here:

http://www.vogella.com/articles/AndroidListView/article.html

Hope it helps !! Best regards !!

ivan.aguirre
  • 500
  • 4
  • 11
1

A dirty hack to do this, in case you are having too much problems getting the new notification and in case you dont have a significant amount of data in the adapter, you can always create a new adapter and assign it to the list list.setAdapter(adapter); forcing the list to render it over again from scratch...

Regards!

Martin Cazares
  • 13,637
  • 10
  • 47
  • 54
1

I don't think you can update the memory location of the list powering your adapter during runtime. This isn't allowed:

this.values = values;

If you know that the number of items won't change, use this piece of code:

public void setValues(SettingsValue[] values) {
    for (int i = 0; i < values.length; i++) {
        this.values[i] = values[i];
    }
    notifyDataSetChanged();
}

Otherwise you will need to create a new adapter every time you call setValues and update your listview by pointing to the new adapter. I'll try to link to the article where I read this when I find it. Hope this works.

rarp
  • 1,122
  • 10
  • 20