简体   繁体   中英

Android - Reload Fragment into a ViewPager

I'm building an App which extracts data from a SQLite local database.

I have an Activity, in which I use a ViewPager ; in this ViewPager, there are 12 swipable Fragments.

I'd like to implement a " Reload " button which allows user to reload the actual Fragments, removing any changes caused by user modifications and resuming the original state of Fragment.

I tried to use notifyDataSetChanged() method on myAdapter (an extension of FragmentStatePagerAdapter ) even if it should reload the whole pager: this method works (each Fragment is reloaded), but user modifications are not removed (for example, if I get an EditText and if I replace its text, pushing "Reload" button doesn't resume its original state).

I also tried to replace getItemPosition() method as suggested, but it doesn't solve the problem.

1) Activity:

public class MyActivity extends AppCompatActivity {
//code
    ViewPager pager;
    MyAdapter adapter;
    TabLayout tabLayout;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...
        //Pager settings
        adapter = new Adapter(getSupportFragmentManager(),this);
        pager = (ViewPager) findViewById(R.id.pager);
        pager.setAdapter(adapter);
        //TabLayout settings
        tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
        tabLayout.setupWithViewPager(pager);
        //...
    }
    //...
    //Method called after clicking on 'Reload' button
    public void reloadFragment() {
        adapter.notifyDataSetChanged();
    }
}

2) Adapter:

public class Adapter extends FragmentStatePagerAdapter {
    //...
    public Fragment getItem(int position) {
        Fragment f = null;
        switch (position) {
            case 0:
                f = new MyFragment01();
                break;
            //case from 0 to 11
        }
        return f;
    }
    //...
    @Override
    public int getItemPosition( Object object ) {
        return POSITION_NONE;
    }

3) MyFragment01 (1 of 12):

import android.support.v4.app.Fragment;
//...
public class MyFragment01 extends Fragment {
    PopulateEditText editText01;
    public MyFragment01() {
        //Empty constructor used to load from MyActivity
    }
    @Override
    public void onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        //...
        View myFragmentView = inflater.inflate(R.layout.layout_fragment_01, container, false);
        //...
        acquire(myFragmentView); //Acquire layout elements (EditTexts, Spinners, other)
        populate(); //Insert text on previous layout elements, based on SQLite data
        //...
        //'Reload' button
        ImageButton test = (ImageButton) myFragmentView.findViewById(R.id.testBtn);
        test.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((MyActivity)getActivity()).reloadFragment();
             }
            });
        }
    }
}

EDIT #1 (about user yshahak's question)

3b) Details of method acquire(myFragmentView); from MyFragment01 , which is called to inflate elements:

public void acquire(View myFragmentView) {
    editText01 = new PopulateEditText(R.id.editText01,myFragmentView);
    //...
}
public void populate() {
    //cursor is a cursor which extracts a record from SQLite Database
    //column is an attribute of this record
    editText01.initializeEditText(cursor,"column",true);
    //...
}

4) PopulateEditText class:

public class PopulateEditText {
    EditText editText;
    Activity activity;
    //Constructor
    public PopulateEditText(int elementFromLayout, View myFragmentView) {
        this.editText=(EditText) myFragmentView.findViewById(elementFromLayout);
        reset();
    }
    public void reset() {
        if(editText!=null)
            editText.setText("");
    }

    public void initializeEditText(Cursor c, String column, boolean enableEditText) {
        this.column=column;
        String s = c.getString(c.getColumnIndex(column));
        editText.setText(s);
        editText.setSelection(editText.getText().length());
        editText.setEnabled(enableEditText);
    }
}

EDIT #2
When i press 'Reload' button, these methods are called from MyFragment01 (checked by some breakpoints on debug):
1. onCreateView
2. acquire(myFragmentView)
3. populate()
And if I check my EditText's text, it's correctly reloaded at the start value, so it seems that "new Fragment" is reloaded, but not attached to ViewPager element.

Example:
- Start value: Text
- Modified value: Text 1234
- Hitting 'Reload', actual value: Text 1234
- getText().toString() from EditText: Text (expected result, not shown on actual view)

Lets try to look it slowly:

When you hit the "test" Button, what happens is the adpater bring all it Fragments and ask itself what to do with them. If you had use FragmentPageAdapter then because you define that it will throw them (with the "POSITION_NONE") it would had reload all Fragments by calling it getItem();

But because you use FragmentStatePagerAdapter extension it try to use it old Fragments so the old modification remain.

So basically I think that id you replace to FragmentPageAdapter it should work.

Keep in mind that this approach not really efficient to reload all Fragments just because you want to update one of them.

I found a solution for my problem. Inside Adapter , I added FragmentManager fm as variable in constructor:

FragmentManager fm;
public Adapter(FragmentManager fm, Activity activity) {
    super(fm);
    this.fm=fm;
    this.context=activity.getApplicationContext();
    this.activity=activity;
}

Inside MyActivity , I've just edited reloadFragment() method in this way, now that FragmentManager is available:

public void reloadFragment(int position) {
    Adapter adapter = (Adapter)pager.getAdapter();
    //This list contains a 'list' of Fragments available on ViewPager adapter. 
    List<Fragment> list = adapter.fm.getFragments();
    switch(position) {
        case 0:
            ((MyFragment01)list.get(position)).populate();
            break;
        //iterate for case from 0 to 11
    }
 }

Calling my populate() custom method on each Fragment, every EditText (and general elements) is correctly restored to its original value.

The argument position of reloadFragment(int position) is provided when I click on Reload button. I moved the method on a general class in order to call it from each fragment I want. Because of this, I needed to add view and activity in arguments:

public static void reloadButton(View myFragmentView, final Activity activity, final int position) {
    ImageButton test = (ImageButton) myFragmentView.findViewById(R.id.reloadBtn);
    test.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ((MyActivity)activity).reloadFragment(position);
        }
    });
}

Maybe it's not the most efficient solution ever, if I'll find a better general solution, I'll publish it here

EDIT #1 : I have to modify the way to implement the control, As yshahak told me, List is not a good way to solve my problems. That's why, when I open my activity, Fragment are not attached in order (from 0 to 11), but "on demand". So if I open my activity and if I swipe directly to 11th Fragment, that will be element number 3 instead of 11 (because my Activity cache only first 2 Fragment onCreate). I will search for another solution and I'll update here

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