2

I have a Fragment with a TableLayout. The data in the table is from a SQLite db. The SQLite db is populated from a RESTful webservice in an AsyncTask in the MainActivity. The Fragment must wait for the task to complete before populating the table. The Fragment listens for the task onPostExecute() method to be called. When it is, the method onLoadAndStoreComplete in the Fragment is called. This all works.

What doesn't work is that I need the fragment activity in order to create new TableRows and TextViews in onLoadAndStoreComplete and I can't get it.

I've tried:
- making a class member fragmentActivity and assigning in onCreateView(), but by the time it gets to onLoadAndStoreComplete(), it is null.
- calling this.getActivity() again in onLoadAndStoreComplete(), but it returns null.

How do I get the fragment activity?

MyFragment.java:

public View onCreateView(LayoutInflater inflater, ViewGroup container,  Bundle savedInstanceState) {
        fragmentActivity = this.getActivity(); // return value is valid
        ...
}

@Override                               
public void onLoadAndStoreComplete() {  

        fragmentActivity = this.getActivity(); // returns null
    ...
}

from MainActivity.java (extends ActionBarActivity)

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

        Resources resources = getResources();
        String[] tabTitleArray = { resources.getString(R.string.first_fragment),  
                                   resources.getString(R.string.second_fragment),  
                                   resources.getString(R.string.help_fragment) };

        viewPager = (ViewPager) findViewById(R.id.pager);
        actionBar = getSupportActionBar();
        mAdapter = new TabsPagerAdapter(this.getSupportFragmentManager());

        viewPager.setAdapter(mAdapter);
        actionBar.setHomeButtonEnabled(false);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);        

        for (String tab_name : tabTitleArray) {
            actionBar.addTab(actionBar.newTab().setText(tab_name).setTabListener(this));
        }

        // On swiping the ViewPager, select the respective tab 
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                actionBar.setSelectedNavigationItem(position) ;// on changing the page, make respected tab selected
            }
            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
            }
            @Override
            public void onPageScrollStateChanged(int arg0) {
            }
        });

        String url = "http://myrestfulwebservice";
        Fragment secondFragment = mAdapter.getItem(SECOND_TAB);
            new LoadAndStoreDataTask((OnLoadAndStoreCompleteListener)secondFragment).execute(url);    
    }

} // end OnCreate

TabsPagerAdapter.java

public class TabsPagerAdapter extends FragmentPagerAdapter {

    public TabsPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public Fragment getItem(int index) {
        switch (index) {
        case 0:
            return new FirstFragment();
        case 1:
            return new SecondFragment();
        case 2:
            return new HelpFragment();
        }

        return null;
    }

    @Override
    public int getCount() {
        return 3; // get item count - equal to number of tabs
    }

    public static void setTabColor(TabHost tabhost) {
        for(int i=0;i<tabhost.getTabWidget().getChildCount();i++) {
            tabhost.getTabWidget().getChildAt(i).setBackgroundColor(Color.parseColor("#FF0000")); //unselected
        }
        tabhost.getTabWidget().getChildAt(tabhost.getCurrentTab()).setBackgroundColor(Color.parseColor("#0000FF")); // selected
    }

}
Al Lelopath
  • 6,448
  • 13
  • 82
  • 139

2 Answers2

2

I haven't tested this, but it seems to me that it would work.

Add a Context parameter to the constructor for both TabsPagerAdapter and MyFragment.

Something like this:

TabsPagerAdapter.java:

public class TabsPagerAdapter extends FragmentPagerAdapter {

    private Context mCtx;

    public TabsPagerAdapter(FragmentManager fragmentManager, Context context) {
        super(fragmentManager);
        mCtx = context;
    }

    @Override
    public Fragment getItem(int index) {
        switch (index) {
        case 0:
            return new FirstFragment(mCtx);
        case 1:
            return new SecondFragment(mCtx);
        case 2:
            return new HelpFragment(mCtx);
        }

        return null;
    }

    @Override
    public int getCount() {
        return 3; // get item count - equal to number of tabs
    }

    public static void setTabColor(TabHost tabhost) {
        for(int i=0;i<tabhost.getTabWidget().getChildCount();i++) {
            tabhost.getTabWidget().getChildAt(i).setBackgroundColor(Color.parseColor("#FF0000")); //unselected
        }
        tabhost.getTabWidget().getChildAt(tabhost.getCurrentTab()).setBackgroundColor(Color.parseColor("#0000FF")); // selected
    }

}

MainActivity.java:

  mAdapter = new TabsPagerAdapter(this.getSupportFragmentManager(), this);

MyFragment.java (do this in FirstFragment, SecondFragment, and HelpFragment):

public static class MyFragment extends Fragment {

    private Context fragmentActivity ;

    public MyFragment(Context context){
        fragmentActivity  = context;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        //.........            
        return rootView;
    }

    @Override
    public void onLoadAndStoreComplete() {

        //do something with fragmentActivity

    }
}
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • I think this is in the right direction. If i understand this correctly, however, the "this" in the mAdapter assigment in MainActivty is MainActivity and I need the SecondFragment activity. – Al Lelopath Apr 10 '15 at 20:17
  • @AlLelopath Actually, when you call `fragmentActivity = this.getActivity();` in the Fragment, you are getting the `MainActivity` Activity Context. So, passing `this` to the constructor references the exact same `Context` as you were before. There is actually no such thing as a Fragment Context, each Fragment uses the context of it's parent Activity. When you say " I need the SecondFragment activity", that is exactly what you are getting here. – Daniel Nugent Apr 10 '15 at 20:21
  • @AlLelopath The SecondFragment activity is MainActivity. Bear in mind though, what you really need is a `Context`, and since `Activity` extends `Context`, you can just use `this` inside the Activity to reference the Activity Context. This answer explains it pretty directly: http://stackoverflow.com/questions/8215308/using-context-in-fragment/8215398#8215398 – Daniel Nugent Apr 10 '15 at 21:07
  • Right you are. Thanks. – Al Lelopath Apr 10 '15 at 21:44
  • I guess I don't understand why setting the rootView in `OnCreateView` doesn't work, i.e. why class member rootView does get set correctly in `OnCreateView` but then in `onLoadAndStoreComplete()`, it is null. Can you explain this? – Al Lelopath Apr 13 '15 at 13:44
  • @AlLelopath It's hard to tell from your code, but my guess is that `onLoadAndStoreComplete()` is getting called before `OnCreateView()`. You could put logging in every function to see what order they are called in. – Daniel Nugent Apr 13 '15 at 14:41
1

It seems the AsyncTask starts too early. suggest loading the db in onActivityCreated(), it's be called after onCreateView

@Override
public void onActivityCreated (Bundle savedInstanceState) {
    //load your db here
}
yummy
  • 570
  • 3
  • 9
  • The AsyncTask is the indeed the last thing done in `OnCreateView()` (see code added to OP). So you are suggesting moving that code from MainActivity to the Fragment. I don't like this because then what will I do if I need the data in another Fragment. – Al Lelopath Apr 10 '15 at 19:41
  • Though AsyncTask is in the last line in onCreate, but the fragment may not be attahched to the activity even when the AsyncTask is done. If you want share data with other fragments, then i think it's better to use a 'global cache' – yummy Apr 10 '15 at 19:59