Tom Milner
Tom Milner

Reputation: 25

Android ViewPager, ListFragments and updating when an element in a list is changed

Im writing my first android app as a coursework for university. So far I have most things working, but I have come across an issue.

The app is structured so I have an Activity that sets up a ViewPager with 3 Fragments in it. Each Fragment is an extension of ListFragment, the list are filled with data pulled from a database. The adapters display custom views for each ListItem, which include a checkbox.

What I want to happen is when the checkbox is ticket all three fragments are updated. That is the database is re-queried for each fragment and the list views get updated.

The HomeActivity. This sets up the ViewPager. It adds the fragments and set up the Fragment Adapter. public class HomeActivity extends SherlockFragmentActivity {

    private FragmentAdapter mPagerAdapter;
    private ViewPager mViewPager;

    /**
    * Set up the view. Add the Fragments to the viewPager, set up tabs. 
    */
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        getSupportActionBar();

        ...

        //Set up Fragments. 
        List<MemoFragment> fragments = new Vector<MemoFragment>();
        fragments.add(new IncompleteMemoFragment());
        fragments.add(new CompleteMemoFragment());
        fragments.add(new UrgentMemoFragment());

        mPagerAdapter = new FragmentAdapter(getSupportFragmentManager(), fragments);

        mViewPager = (ViewPager) findViewById(R.id.viewPager);
        mViewPager.setAdapter(mPagerAdapter);
        mViewPager.setOnPageChangeListener(new TabListener(mTabHost,mViewPager));
        mViewPager.setOffscreenPageLimit(3);
    }
    ...
}

The Fragment Adapter

public class FragmentAdapter extends FragmentPagerAdapter {

    private List<MemoFragment> mFragments;

    public FragmentAdapter(FragmentManager fm, List<MemoFragment> fragments) {
        super(fm);
        this.mFragments = fragments;
    }

    @Override
    public MemoFragment getItem(int position) {
        return mFragments.get(position);
    }

    @Override
    public int getCount() {
        return this.mFragments.size();
    }

}

The MemoFragment class, IncompleteMemoFragment, CompleteMemoFragment, UrgentMemoFragment all extend this, and are 99% the same, they just pull a different set of data from the database.

public class MemoFragment extends SherlockListFragment {

    protected DatabaseSource datasource;

    /**
     * Creates the View. 
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_memo_list, container, false);

        datasource = new DatabaseSource(container.getContext());
        datasource.open();

        List<Memo> values = getMemos();

        datasource.close();

        MemoListViewAdaptor adapter = new MemoListViewAdaptor(container.getContext(), values);

        setListAdapter(adapter);

        return rootView;
    }

    /**
    * Gets the memos to show.
    * @return the memos to show.
    */
    public List<Memo> getMemos(){
        return datasource.getAllMemos();
    }

}

This is the MemoListViewAdapter. Here you will see the checkbox im trying to look for changes too.

public class MemoListViewAdaptor extends ArrayAdapter<Memo> {
    private final Context mContext;
    private final List<Memo> mMemos;

    /**
     * Save the context and the Memos
     * @param context the context to save
     * @param memos the memos to save.
     */
    public MemoListViewAdaptor(Context context, List<Memo> memos) {
        super(context, R.layout.memo_list_row, memos);
        this.mContext = context;
        this.mMemos = memos;
    }

    /**
     * Get the view. Sets up the list to use the custom view. 
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View rowView = inflater.inflate(R.layout.memo_list_row, parent, false);

        ...
        CheckBox done = (CheckBox) rowView.findViewById(R.id.done);

        Memo memo = mMemos.get(position);


        done.setOnClickListener(new ClickListener(memo));
        if(memo.getCompleteness() == Memo.COMPLETE){
            done.setChecked(true);
        }

        ...

        return rowView;
    }

}

And, Finally here is the Click listner.

public class ClickListener implements OnClickListener {
    private Memo memo;

    ClickListener(Memo memo){
        this.memo = memo;
    }
    @Override
    public void onClick(View v) {
        if(v.getClass() == CheckBox.class){
            Log.v("com.milner.memolist", "These people are crazy. This task is done");
            if(((CheckBox)v).isChecked()){
                memo.setCompleteness(1);
            }else{
                memo.setCompleteness(0);
            }
            DatabaseSource datasource = new DatabaseSource(v.getContext());
            datasource.open();
            datasource.updateMemo(memo);
            datasource.close();

        }else{
        ...
        }
    }  
}

So, there you have it. Thats what I have so far! As you can see I've probably done this a very complex way... I dont have much experience with android. I guess what I really want to be doing is to reload all the fragments when the check box is ticked.

Is there an easy way to do this? Is there even a complex way to do this? I just cant get my head around this last bit!

Hope you can help! :)

Thanks

Tom M

Upvotes: 0

Views: 2000

Answers (1)

user
user

Reputation: 87064

Is there an easy way to do this?

Add:

@Override
public int getItemPosition(Object object) {     
    return POSITION_NONE;
}

to your FragmentAdapter and when you need to update the fragments call notifyDataSetChanged() on the FragmentAdapter. This will trigger a rebuild of the fragments.

Is there even a complex way to do this?

Updating the visible fragment immediately so the user sees the change and update the next fragments after or when the user tries to see them(on starting to swipe). To check this solution google a bit about getting/updating a Fragment in/from a ViewPager.

If you just have three fragments in the ViewPager set the off screen page limit to 2 as you'll only have a maximum of two fragments on either side of any fragment from the ViewPager. Also keeping references to the fragment like you do with that Vector(use an ArrayList) for (I'm assuming) accessing them later will fail after a screen rotation as those will not be the fragments the ViewPager will use.

Upvotes: 4

Related Questions