简体   繁体   中英

what am I missing with `notifyDataSetChanged`?

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

}

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 !!

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!

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM