1

I've made a custom BaseAdapter class to display registration keys in a ListView. Whenever I add another key to the list and call notifyDataSetChanged(), the ListView updates visually just fine. My data structure is simply an ArrayList of simple structures containing a few Strings and an int. However, when I try to clear the key list and call notifyDataSetChanged(), nothing happens visually. When I close and reopen the app, the list is empty, as it should be. Before calling notifyDataSetChanged() I now call notifyDataSetInvalidated(). This makes visuals update properly as soon as I clear the keys, but Google's documentation warns against using that method unless you're completely done using that adapter. When i register more keys after wiping them, they still show up in the ListView, which makes it seem like notifyDataSetInvalidated() is a working solution. Is there any danger associated with this method if I keep using the same BaseAdapter and ArrayList for the ListView? Is there a better solution? Here's my custom BaseAdapter implementation:

class KeyAdapter extends BaseAdapter implements Database.KeyRegistrationListener {

    private ArrayList<Database.KeyDetails> keyInfo;

    public KeyAdapter(ArrayList<Database.KeyDetails> keyInfo) {

        this.keyInfo = keyInfo;

    }

    @Override
    public int getCount() {

        return keyInfo.size();

    }

    @Override
    public Database.KeyDetails getItem(int position) {

        return keyInfo.get(position);

    }

    @Override
    public long getItemId(int position) {

        return position;

    }

    @Override
    public View getView(int pos, View convertView, ViewGroup parent) {

        TextView userID;
        TextView appName;
        TextView date;
        TextView time;

        if (convertView == null) {

            convertView = getLayoutInflater().inflate(R.layout.registration_item, parent, false);

            userID =  (TextView) convertView.findViewById(R.id.user_id);
            appName = (TextView) convertView.findViewById(R.id.app_name);
            date =    (TextView) convertView.findViewById(R.id.date_used);
            time =    (TextView) convertView.findViewById(R.id.time_used);

            convertView.setTag(R.id.user_id, userID);
            convertView.setTag(R.id.app_name, appName);
            convertView.setTag(R.id.date_used, date);
            convertView.setTag(R.id.time_used, time);

        } else {

            userID =  (TextView) convertView.getTag(R.id.user_id);
            appName = (TextView) convertView.getTag(R.id.app_name);
            date =    (TextView) convertView.getTag(R.id.date_used);
            time =    (TextView) convertView.getTag(R.id.time_used);

        }

        Database.KeyDetails key = getItem(pos);

        userID.setText(key.userID);
        appName.setText(key.appName);
        date.setText(key.date);
        time.setText(key.time);

        return convertView;

    }

    @Override
    public void notifyKeysUpdated(Database.KeyDetails newKeyDesc) {

        if (newKeyDesc == null) {

            keyInfo.clear();
            notifyDataSetInvalidated();
            notifyDataSetChanged();

        } else{

            keyInfo.add(newKeyDesc);
            notifyDataSetChanged();

        }

    }

}

The notifyKeysUpdated() method is called by my wrapper class for a SQLite database whenever I add a key to the database. If my Database object feeds the listener null, it means all the keys in the database have been cleared and the ListView should be visually empty.

EDIT: Since I started using notifyDataSetInvalidated(), the ListView no longer displays anything, including when I register new keys.

Sam Claus
  • 1,807
  • 23
  • 39

2 Answers2

1

After doing some research on the source code of BaseAdapter and ListView in android and also this question (Android - What does adapter.notifyDataSetInvalidated do?)

The first thing you might be sure is, Your data is not totally binding to the adapter. Normally the BaseAdapter is just a collection of interface and it does not contains any field of the arraylist. That's mean the size , fetching data is upon on you. To indicate the data set is not valid any more, you should override the method notifyDataSetInvalidated().

I tried to copy your code into my project. Both method notifyDataSetInvalidated() and notifyDataSetChanged() will update the whole list, which approved the arraylist is not totally binding to the adapter. It is just an adapter for getting the list of data. I am wondering the google documentation does not explain clearly about what to do with this method.

Community
  • 1
  • 1
Long Ranger
  • 5,888
  • 8
  • 43
  • 72
  • In all the other BaseAdapter implementations I saw, the custom sub class just stored it's own data structure and implemented the few methods I did. The list is definitely updating, it's just that for some reason the view only updates when I add to the list, but not when I remove items. I think I may change to a CursorAdapter instead, considering the keys registered in my app are initially stored in a SQLite database. Thanks for the effort though! – Sam Claus Aug 19 '16 at 05:48
0

In the end, the issue simply turned out to be an error with the reference to my data. Because Java is a very safe language and doesn't allow explicit pointers, I guess at some point along the line I made a copy of the data object, rather than passing the one I was going to update to the ListView. After simplifying my code and creating and passing data to the table in the simplest way possible, it is fully functional.

Sam Claus
  • 1,807
  • 23
  • 39