23

I'm building an App that use Fragment and a single MainActivity that showing different fragments in the same ViewPager to replicate the behaviour of a typical MVC application.

The problem is that haven't understood how could I update the fragment programmatically to update the ViewPager with the new Fragment changes

For example I have this Fragment that show a chart, (in my intention) when I invoke setFragment(int interval, Point[] snodePoint) from the main, the chart should be updated in ViewPager

public class LineFragment extends Fragment {
    static int interval;
    static Point snodePoints[];


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        final View v = inflater.inflate(R.layout.fragment_linegraph, container,
                false);
        Bundle bundle = this.getArguments();
        ChartConfig cg = bundle.getParcelable("FragmentData");

        snodePoints = cg.getPoints();
        interval= cg.getInterval();
        Line l = new Line();

        for(int i=0; i<snodePoints.length; i++){
          LinePoint p = new LinePoint();
          Point sp=snodePoints[i];
          p.setX(sp.getX());
          p.setY(sp.getY());
          l.addPoint(p)
        }
        l.setColor(getResources().getColor(R.color.green));

        LineGraph li = (LineGraph) v.findViewById(R.id.linegraph);
        li.addLine(l);
        li.setRangeY(0, interval);
        li.setLineToFill(0);

        li.setOnPointClickedListener(new OnPointClickedListener() {

            @Override
            public void onClick(int lineIndex, int pointIndex) {
                // TODO Auto-generated method stub

            }

        });

        return v;
    }
}

In few words I need to

  • 1-create a fragment based on the data received by the main activity.
  • 2-change the data of the fragment everytime in the main activity happens something like setFragment(...) with new data (generated always by the main).

How could I do this?

The Main Activity with the FragmentAdapter

public class MainActivity extends FragmentActivity {

ViewPager mViewPager;
MyFragmentAdapter mMyFragmentAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mViewPager = (ViewPager) findViewById(R.id.view_pager);
    final ActionBar bar = this.getActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    LineFragment lineFrag = new LineFragment();
    BarFragment barFrag = new BarFragment();
    PieFragment pieFrag = new PieFragment();

    mMyFragmentAdapter = new MyFragmentAdapter(this, mViewPager);
    mMyFragmentAdapter.addTab(bar.newTab().setText("Line"), LineFragment.class,
            null, lineFrag);
    mMyFragmentAdapter.addTab(bar.newTab().setText("Bar"), BarFragment.class,
            null, barFrag);
    mMyFragmentAdapter.addTab(bar.newTab().setText("Pie"), PieFragment.class,
            null, pieFrag);
    mViewPager.setOffscreenPageLimit(mMyFragmentAdapter.getCount() - 1);
}

public static class MyFragmentAdapter extends FragmentStatePagerAdapter implements
        ActionBar.TabListener, ViewPager.OnPageChangeListener {

    private final MainActivity mContext;
    private final ActionBar mActionBar;
    private final ViewPager mViewPager;
    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
    private final ArrayList<Fragment> mFrag = new ArrayList<Fragment>();

    static final class TabInfo {
        private final Class<?> clss;
        private final Bundle args;

        TabInfo(Class<?> _class, Bundle _args) {
            clss = _class;
            args = _args;
        }
    }

    public MyFragmentAdapter(MainActivity activity, ViewPager pager) {
        super(activity.getSupportFragmentManager());
        mContext = activity;
        mActionBar = activity.getActionBar();
        mViewPager = pager;
        mViewPager.setAdapter(this);
        mViewPager.setOnPageChangeListener(this);
    }

    public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args,
            Fragment frag) {
        TabInfo info = new TabInfo(clss, args);
        tab.setTag(info);
        tab.setTabListener(this);
        mTabs.add(info);
        mFrag.add(frag);
        mActionBar.addTab(tab);
        notifyDataSetChanged();
    }

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

    @Override
    public Fragment getItem(int position) {
        TabInfo info = mTabs.get(position);
        Bundle args = new Bundle();
        ChartConfig cg = new ChartConfig(interval, snodePoint);
        args.putParcelable("FragmentData", cg);
        Fragment f = mFrag.get(position);
        f.setArguments(args);

        return f;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset,
            int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        mActionBar.setSelectedNavigationItem(position);

    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

    public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) {
        Object tag = tab.getTag();
        for (int i = 0; i < mTabs.size(); i++) {
            if (mTabs.get(i) == tag) {
                mViewPager.setCurrentItem(i);
            }
        }
    }

    public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {
    }

    @Override
    public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {
    }

}

}

The first time that I create the fragment, the main activity pass successful the parameters to the Fragment through the overrided getItem(int position), but if I try to change the data, also if I try to invoke notifyDataSetChanged() on the adapter mMyFragmentAdapter, nothing happens and the fragments in ViewPager continue to use the same data passed in creation.

EDIT

Following some partial code seen in other question about fragment update I have also tried this

public void update() {
        try{
            LineFragment fragment = 
                      (LineFragment) getSupportFragmentManager().findFragmentByTag(
                                   "android:switcher:"+R.id.view_pager+":0");
                  if(fragment != null)
                  {
                     if(fragment.getView() != null) 
                     {

                        fragment.updateDisplay();
                     }
                  }
        }
        catch(RuntimeException e){
            e.printStackTrace();
        }

    }

Assuming that 0 is the id of the first Fragment-Tab fragment(I'm not sure about this kind of operation), but fragment.updateDisplay(); is not invoked.

AndreaF
  • 11,106
  • 24
  • 92
  • 156
  • You didn't searched to much didn't you? http://stackoverflow.com/questions/7379165/update-data-in-listfragment-as-part-of-viewpager – user Nov 02 '13 at 16:53
  • 1
    @Luksprog mmm It's a bit different from what I have asked for. I need to understand how the Fragment can get the data passed by main, maybe I'm missing something but really I haven't understood how could I do this. If I try to create a custom constructor for my 'LineFragment' I get the error 'This fragment should provide a default constructor (a public constructor with no arguments)' – AndreaF Nov 02 '13 at 17:07
  • @AndreaF check dialog fragment http://developer.android.com/reference/android/app/DialogFragment.html uses a static `static MyDialogFragment newInstance(int num)` to which param is passed and then uses set and get arguments. and you pass the value like `newInstance(value)` if that is what you meany by passing data from main to fragment – Raghunandan Nov 02 '13 at 17:11
  • @Raghunandan Thanks but DialogFragment is a fragment that displays a dialog window floating on top of its activity's window, that is not my case – AndreaF Nov 02 '13 at 17:15
  • @AndreaF concept is the same apply it to fragment – Raghunandan Nov 02 '13 at 17:16
  • @Raghunandan But if the Fragment cannot have any contructor different from default constructor in what way I could solve my problem? DialogFragment use an int to identificate different istance. From what I know fragment cannot. – AndreaF Nov 02 '13 at 17:21
  • @AndreaF is DialogFragment not fragment? But i guess that is not what you need seems the answer below is what you need. However your understanding of fragment is wrong – Raghunandan Nov 02 '13 at 17:24
  • @Raghunandan ok you are right, now I have understood what you mean – AndreaF Nov 02 '13 at 17:36

4 Answers4

56

In your Adapter override getItemPosition

@Override
public int getItemPosition(Object object) {
    // POSITION_NONE makes it possible to reload the PagerAdapter
    return POSITION_NONE;
}

After that

viewPager.getAdapter().notifyDataSetChanged() 

should work.

luckyhandler
  • 8,848
  • 2
  • 40
  • 53
  • 1
    @Nino Handler.. Does that notifyDataSetChanged in the adapter refresh all the fragments or the only fragment which will visible to the user.. – Raja Jawahar Aug 09 '17 at 13:34
  • all which are currently attached - if you use a FragmentStatePagerAdapter this might not be the case for all fragments – luckyhandler Aug 10 '17 at 12:10
3

I need to understand how the Fragment can get the data passed by main, maybe I'm missing something but really I haven't understood how could I do this.

From this it seems that you want to pass the data at the moment of the creation of the Fragment. If this is what you want you shouldn't make a custom constructor(and generally for a Fragment you should have only the default no-args constructor(because the platform needs it to recreate the Fragment at certain times)).

Instead you should either pass the data in a Bundle:

@Override
public Fragment getItem(int position) {
     switch (position) {
        //...
        Bundle args = new Bundle();
        args.put("a_string_representing_theKey", data)
        LineFragment f = new LineFragment();
        f.setArguments(args);
        return f;
        //...
     } 
}

This will only work if the data can be put in a Bundle(see the put... method of the Bundle class, all primitives + objects that are Parcelable). In the fragment you could then retrieve the arguments using getArguments().

You can also make the Fragment retrieve the data directly from the Activity:

// in the lifecycle method where you want the data
((MainActivity)getActivity()).getDataForFragment(); // use the data

For example I have this Fragment that show a chart, (in my intention) when I invoke setFragment(int interval, Point[] snodePoint) from the main, the chart should be updated in ViewPager

You seem to contradict yourself. For this use the code from the question I linked to, of course just calling your current setFragment() method will not do anything as you just update the value of two variables, you'll need to let the UI know about the change(like calling another method to recreate the view hierarchy, setting the new values on the chart view and calling invalidate() etc).

Edit:

The code used in the question I linked to should work, but you aren't using the proper code. You have a FragmentStatePagerAdapter but you're using the code for a FragmentPagerAdapter which will not work:

FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) mViewPager.getAdapter();
LineFragment f = (LineFragment) a.instantiateItem(pager, thePositionYouWant);

Keep in mind that the code above will work only for the visible fragment plus 1 fragment on each side, the other fragments will not be available(and as the user doesn't see them or can't move to them right away they shouldn't be updated).

user
  • 85,380
  • 17
  • 189
  • 186
  • Thanks for the answer. In few words I need to 1-create a fragment based on the data received by the mainactivity. 2-change the data of the fragment everytime if in the main happens something like 'setFragment(...)'. If i can do this using the Bundle, could you give me more details about how to use this with Fragment? – AndreaF Nov 02 '13 at 17:27
  • @AndreaF I've edited my answer a bit. You could look at any basic tutorials to see how to use the `Bundle` as a fragment's arguments. – user Nov 02 '13 at 17:37
  • @Raghunandan The `getItem()` method is from the `FragmentPagerAdapter`/`FragmentStatePagerAdapter` where he creates the `Fragment` and you can't make it `static`. – user Nov 02 '13 at 17:44
  • @Luksprog yup understood now i guess i misunderstood the question. i thought its a fragment. getItem here is a overriden method of `FragmentPagerAdapter` – Raghunandan Nov 02 '13 at 17:46
  • @Luksprog now I have understood how to pass the parameters from main activity to Fragment, but unfortunately if I try to change the data and call `notifyDataSetChanged()` nothing happens – AndreaF Nov 03 '13 at 00:42
  • @AndreaF Are you calling `notifyDataSetChanged()` on the `MyFragmentAdapter` to update the `LineFragment`? If yes, don't do this, use the code from the question I linked to to reference the `LineFragment`, call the `setFragment()` method to set the variables and update the data(for example by resetting the view and rebuilding it). – user Nov 03 '13 at 07:11
  • @Luksprog Please, could you give me a sample? I have tried to follow the step in the link but without success. I have also tried to use `myadapter.destroyItem(mViewPager, 0, LineFragment.class);` but maybe I have passed the wrong parameters in the `destroyItem` method because crashes – AndreaF Nov 03 '13 at 14:51
  • I have updated the question with more details of my main activity – AndreaF Nov 03 '13 at 23:46
  • @AndreaF You want to update the data of the adapter's fragments not not replace the fragments themselves. In the question I linked to there is some code on **how to access a fragment** from the `ViewPager` to call update methods on it. – user Nov 04 '13 at 10:10
  • @Luksprog right I want to update the fragment, but since I have no success with this approach I have also tried to replace the fragments. However still are not able to access and update the Fragment. The method of the accepted answer in the link doesn't work (is unclear how and where the HomeListFragment is implemented) The 2nd top answer method returns NullPointerException if i try to get any data from the declared `FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) pager.getAdapter();` and if i try something like `a.startUpdate(mViewPager);` doesn't happen anything – AndreaF Nov 04 '13 at 22:23
  • @Luksprog Please see my related question: http://stackoverflow.com/questions/24833912/refresh-fragment-ui-from-fragmentactivity – Dr.jacky Jul 19 '14 at 07:48
3

very simple to override the method in the fragment

@Override

public void setUserVisibleHint(boolean isVisibleToUser) {

    super.setUserVisibleHint(isVisibleToUser);
    if(isVisibleToUser){

        actionView();
    }
    else{
        //no
    }

}
Issac Balaji
  • 1,405
  • 1
  • 13
  • 25
3

How to update-refresh Fragment in View Pager by Main Activity programmatically after long struggle I have found proper solution

tablayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                
                refreshTab(tab.getPosition());// create a method
                viewpager.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
 private void refreshTab(int position) {
    switch (position) {
        case 0:
    
  
            break;
        case 1:
            
            CompaniesInterest comP1 = (CompaniesInterest) viewpager.getAdapter().instantiateItem(viewpager, viewpager.getCurrentItem());
            comP1.refreshpage();
            break;
        case 2:
            
            TotalSelectedCompanies toTal = (TotalSelectedCompanies) viewpager.getAdapter().instantiateItem(viewpager, viewpager.getCurrentItem());
            toTal.refreshpage();
           
    }
}
// Create a interface for refresh data for fragmen
public interface Refreshpage {
    void refreshpage();
}
// implements Refreshpage interface in both fragment TotalSelectedCompanies and CompaniesInterest
//
@Override`enter code here`
    public void refreshpage() {
        // do what you want to refresh 
    }
Haider Saleem
  • 609
  • 1
  • 7
  • 12
Shiv Kumar
  • 535
  • 6
  • 11