3

I'm doing some test after change device configuration (change language, orientation, etc), and i notice that after this, the method "notifyDataSetChanged()" is not working.

The action example:

I'm calling updateList() everytime i do an action like delete, save, etc. The user click a delete button, a DialogFragment is shown, "Are you sure you want to delete?", when i change the orientation, or the language, or any configuration of the device and then click "yes" on the Dialog, the data is removed, but the list doesn't update. I need to quit the activity, then go back to see the alteration.

BookAdapter:

public void updateList(ArrayList<Book> books) {
     bookList = books;
     notifyDataSetChanged();
}

What can i do to make it works after the configuration change?

Edit:

BookAdapter Constructor:

public BookAdapter(Context c, ArrayList<Book> books) {
    context = c;
    bookList = books
    bookDAO = BookDAO.getInstance(context);
}

BookFragment:

public class BookFragment extends Fragment {

    private BookDAO bookDAO;

    private BookAdapter bookAdapter;

    private ListView listBook;

    private View view;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        bookDAO = bookDAO.getInstance(getActivity());

        view = inflater.inflate(R.layout.book_tab, container, false);

        ArrayList<Book> listBook = null;

        try {
            llistBook = bookDAO.getAll();
        } catch (Exception e) {
            Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
            return view;
        }

        bookAdapter = new BookAdapter(getActivity(), listBook);
        listBook = (ListView)view.findViewById(R.id.listBook);
        listBook.setAdapter(bookAdapter);

        return view;

    }

}
Otuyh
  • 2,384
  • 5
  • 22
  • 40
  • By any chance, do you use fragments holding that adapter? – user Sep 16 '13 at 19:04
  • Yes i do! I have one fragment to each Adapter, because i'm working with Tabs. – Otuyh Sep 16 '13 at 19:07
  • Tabs with a `ViewPager`? If yes, maybe you could post the code you used to initialize the adapter of the `ViewPager`. It would be helpful to see the fields declaration and the constructor for the `BooksAdapter`. – user Sep 16 '13 at 19:21
  • Done! I updated the post with the code that you asked for. – Otuyh Sep 16 '13 at 19:32
  • Where do you call the `updateList` method? And you didn't mention if you use tabs with ViewPager. – user Sep 17 '13 at 06:13
  • I'm not using ViewPager. I'm calling updateList() everytime i do an action like delete, save, etc. The user click a button, a DialogFragment is shown, "Are you sure you want to delete?", when i change the orientation, or the language, or any configuration of the device and click "yes" on the Dialog, the data is removed, but the list doesn't update. I need to quit the activity, then go back to see the alteration. – Otuyh Sep 17 '13 at 13:35
  • That call to updateList should work. My assumption is that after a configuration change an as the activity is destroyed you somehow lose the reference to the adapter on which you call the updateList method so the result is not seen. – user Sep 17 '13 at 16:10
  • Can you show me the code that shows the dialog? and how the dialog's yes button is calling updateList. Is it a DialogFragment? – Sherif elKhatib Sep 17 '13 at 21:54
  • A suggestion, when an update is made to the items in your adapter, why don't you create a new adapter in your fragment and set it on `listBook`? If you're worried about user position, you can always save the it with `bookAdapter.getCurrentItem()` and set it on the newly created adapter with `bookAdapter.setCurrentItem()` – rarp Sep 18 '13 at 02:45
  • how you removing an item post that code and adapter if it is possible – Biraj Zalavadia Sep 23 '13 at 12:08
  • I have the project separatedly with the problem. If someone wants to give it a try, send me ur e-mail and i send an email with the project attached. – Otuyh Sep 23 '13 at 12:09

6 Answers6

6

You can try implementing BookAdapter as a Singleton to confirm that you are not calling updateList(..) from a stale reference.

Changes that you will need to make:

// I am assuming that you are using a BaseAdapter because
// BookAdapter's constructor that you provided in the code above
// does not contain a call to super(....)
public class BookAdapter extends BaseAdapter {

    private static BookAdapter mAdapter;
    private Context context;
    private static ArrayList<Book> bookList;
    private BookDAO bookDAO;

    // To keep at most one instance of BookAdapter
    public static BookAdapter getInstance(Context con, ArrayList<Book> books) {

        // If an instance exists, return it
        if (mAdapter != null) {
            bookList = books;
            return mAdapter;
        }

        // Else, craete a new instance
        mAdapter =  new MyAdapter(con, books);
        return mAdapter;
    }

    // BookAdapter's only constructor is declared as private to restrict access
    private BookAdapter(Context con, ArrayList<Book> books) {
        context = con;
        bookList = books;
        bookDAO = BookDAO.getInstance(context);
    }

    public void updateList(ArrayList<Book> books) {
        bookList = books;
        notifyDataSetChanged();
    }

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

        // Retrieve object
        Book bookItem = bookList.get(position);

        ....
        ....

}

}

This is how the Fragment's onCreateView will change:

bookAdapter = BookAdapter.getInstance(getActivity(), listBook);

Code that will be executed when the user presses yes to Are you sure you want to delete?:

// Remove entry from bookDAO
// Remove entry from listBook
// OR update listBook:
try {
    listBook = bookDAO.getAll();
} catch (Exception e) {
    Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
}

// Assertion: "listBook" does not contain the 
// item that was just deleted from "bookDAO"

// Update ListView's contents
bookAdapter.updateList(listBook);
Vikram
  • 51,313
  • 11
  • 93
  • 122
  • Kind of worked, but not completly. When i change configuration, with dialog opened or not, it cant show the dialog again. The message: "Can not perform this action after onSaveInstanceState", when i tried to open the dialog after configuration change. – Otuyh Sep 19 '13 at 19:51
  • @Hor Could you explain the `Kind of worked` part? And are you, at this point, using recommendations from other answers along with mine? If at all possible, rollback the changes you have made until you get to where everything was working: except that the ListView data was not being refreshed. – Vikram Sep 19 '13 at 20:02
  • Kind of worked, because the list was updated after the delete. And yes, i rollbacked to the point where all work. BUT, now when i change the configuration, i can't open the Dialog again. That message that i talk in other comment shows up. – Otuyh Sep 19 '13 at 20:11
  • @Hor The thing is, my suggestion is independent of your activity's implementation, lifecycle, or how the data persists on configuration change/activity-recreation. So, I am not sure how I can help with this new development. Will it be possible for you to share your project's code, perhaps privately through email? We can talk here: [Link](http://www.twiddla.com/1314124). – Vikram Sep 19 '13 at 20:36
  • Hey! It's already on ur e-mail the project! Thanks for the availability! – Otuyh Sep 20 '13 at 14:15
2

The problem occurs because every time your rotate, change language, etc... the activity is recreated and your fragments are also recreated (new instance), so the notififyDataSetChanged is actually notifying the old instances of your fragments. A solution for that would be. Make your fragments static. Then you create some refresh method for your fragments, and called it when you press yes for your dialog.

In your activity you should have something like this.

private static BookFragment  bookFragment;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    if (fragment1 == null) {
        bookFragment = new BookFragment();
    }

    ...
}

Create some interface like:

public interface Refreshable {
    public void refresh();
}

Then implement this interface all your fragments. In the method that is called when the dialog is answered positively you should call

...
fragment.refresh();
...

Inside the refresh method of you fragment you can call its adapter method updateList(...)

Might not be the prettier solution, but it works....

Why this happen... Google's Android Dev Team might know.

Ygor Duarte
  • 439
  • 2
  • 4
  • Holy god, it worked! I really want to see another options, but this answer really solved my problem! Thanks a lot, Ygor! – Otuyh Sep 23 '13 at 16:14
0

I am using an ArrayList of Strings in my GridViewAdapter which extends BaseAdapter.

so in case i changed the data concerning the List, i cann notifyDataSetChanged() on the Adapter. I dont really see the point why you cant call it?

So what i would do is ovverride this method, and just call notifyDataSetChanged()

@Override
public void onConfigurationChanged(Configuration newConfig) {
   super.onConfigurationChanged(newConfig);
   adapter.notifyDataSetChanged();
}

Besides that, does your Book data chnage based on your configuration/orientation?

Daniel Bo
  • 2,518
  • 1
  • 18
  • 29
0

Try using the same list for the whole adapter lifecycle, and change only its content:

public class BookAdapter extends ArrayAdapter<Book> {

    private final booklist;
    // ..

    public BookAdapter(Context c, ArrayList<Book> books) {
        super(c, R.layout.yourlayout, books);
        context = c;
        bookList = books
        bookDAO = BookDAO.getInstance(context);
    }

    public void updateList(ArrayList<Book> books) {
        bookList.clear();
        boolList.addAll(books);
        notifyDataSetChanged();
    }

    // ..
}
flx
  • 14,146
  • 11
  • 55
  • 70
0

Why are you keeping your View as a data member?

When keeping a view across configuration changes, you hold a reference to the previous instance, before the configuration change was happening. Since it's the view holding your list - it may miss data updates.

Try to remove this field, and the use of it, and see if the list now updates.

Udinic
  • 3,014
  • 2
  • 25
  • 32
  • What you mean "data member"? Anyway, i removed the View, using only as a local property. Same problem. – Otuyh Sep 20 '13 at 12:52
-1

I think the problem comes from the fact that any configuration change such as the orientation will restart your current Activity.

Because of this, I guess some parts of your code are still referencing the previous activity that does not exist anymore and the notifyDataSetChanged is not working anymore.

There are 2 things you can quickly try:

  1. Add this line in the manifest file for your activity: android:configChanges="orientation|locale". This change means to the system that you will handle yourself the changes to do during orientation or language changes. Therefore, the app will not recreate the activity by itself anymore (so the activity should work the same).

  2. The other trick can be to add this line at the beginning of the function onCreateView: setRetainInstance(true);. This line will retain the fragment state during configuration changes as the documentation explains:

Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:

  • onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
  • onCreate(Bundle) will not be called since the fragment is not being re-created.
  • onAttach(Activity) and onActivityCreated(Bundle) will still be called.

Just be informed that as explained the Fragment lifecycle will change a little.

3) Last option could be to retain the activity state using onSaveInstanceState and onRestoreInstanceState as explained in details in this answer: https://stackoverflow.com/a/151940/2206688

Community
  • 1
  • 1
Yoann Hercouet
  • 17,894
  • 5
  • 58
  • 85
  • The number 1 i'm not even try to do, orientation and locale are not the only changes that can happen. I've Tried number 2, i still can reproduce the problem =/ – Otuyh Sep 17 '13 at 13:45
  • I added a third idea, but longer to setup, that will allow you to retain the activity's state. – Yoann Hercouet Sep 17 '13 at 14:06