“I always say, 'This will be my last 'Metal Gear.'” Hideo Kojima

Updating a ListView inside a Fragment placed on a ViewPager on DialogFragment dismiss

Title says all. We’ll see today how to proceed in order to update a ListView that is placed inside a Fragment (not a ListFragment) placed on a ViewPager just after dismissing a DialogFragment. Seems complicated, huh?

UPDATE: Look at this post.

When may we need this?

Well, imagine that you have an Activity which layout uses tabs (represented by Fragments) and you want to use a ViewPager so it feels better to users. With a ViewPager you can swipe tabs, and that make users happy. Now, think about your Activity’s tabs: you may have three or four and one of them may be a ListView.

Finally, imagine that this ListView is updated every time user inputs data that is stored into a database. The user inputs data using a button that presents a Dialog with buttons or whatever the app needs to show. When the user pushes the OK button, the Dialog will call onDismiss(). And the ListView should update here, but it won’t do unless you notify it that there are changes.

Nice. How do we do that?

It’s really easy, but it may be confusing sometimes. We will consider that you created a project using Android Studio wizards and that you selected “Tabs+Swipe” navigation option. Take a look at your main Activity, there should be a custom FragmentPagerAdapter. We’ll make it “remember” your fragments. You can achieve this saving all your Fragments as individual objects or use an array. For example:

public class MyPagerAdapter extends FragmentPagerAdapter {

        SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();      

        ...

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given
            // page.
            Fragment fragment = new MyFragment();
            Bundle args = new Bundle();
            args.putInt(SectionFragment.ARG_SECTION_NUMBER, position + 1);
            fragment.setArguments(args);
            registeredFragments.put(position, fragment);
            return fragment;
        }


        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            registeredFragments.remove(position);
            super.destroyItem(container, position, object);
        }

        public Fragment getRegisteredFragment(int position) {
            return registeredFragments.get(position);
        }

        ...

    }

You just have to override destroyItem() and edit getItem() to handle when we modify the array. Also add getRegisteredFragment() so we can access the fragment array.

Now, on your DialogFragment, declare a new interface and use it on the onDismiss() method. Like this:

public class MyDialogFragment extends DialogFragment implements
DialogInterface.OnDismissListener{


    private OnDBChangedListener mCallback;   

    ...
    
    @Override
    public void onDismiss(DialogInterface dialog) {
        mCallback.onDBChanged();
        super.onDismiss(dialog);
    }

    // Container Activity must implement this interface
    public interface OnDBChangedListener {
        public void onDBChanged();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (OnDBChangedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement
                OnDBChangedListener");
        }
    }
} 

Call it whatever you want and don’t forget to use onAttach() to be sure that your Activity is acting correctly. For this to happen you have to implement it on your Activity. So, add implements DialogFragmentName.OnDBChangedListener to your Activity declaration. Also, implement your interface method and use it to call a (yet to write) public method of your ListView fragment.

public class MainActivity extends Activity implements ActionBar.TabListener,
MyDialogFragment.OnDBChangedListener{
    ...
    @Override
    public void onDBChanged() {
        SectionFragment sFrag =
(SectionFragment)mSectionsPagerAdapter.getRegisteredFragment(1);
         sFrag.updateDB();
    }
}

Finally, add a public method to your ListView fragment or the Activity will not be able to finish the job : ) Something like this will work if you are using SQLite databases:


    public void updateDB() {
        if(dataSource!=null) {
            ListView lView = (ListView)rootView.findViewById(R.id.listView);
            dataSource.open();

            List<Foo> foos = dataSource.getAllFoos();
            adapter = new ArrayAdapter<Foo>(context,
                    android.R.layout.simple_list_item_1,
                    foos);
            lView.setAdapter(adapter);
            dataSource.close();
        }

    }

That’s it! Hope it helped someone : )

Jordi.

Take a look at some documentation used to write this HowTo: