0

I have the following problem:

I have a custom layout for the action bar of my activity like that:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="horizontal"
   android:divider="?android:attr/dividerVertical"
   android:showDividers="middle"
   android:dividerPadding="6dp">
   <include layout="@layout/include_cancel_button" />
   <include layout="@layout/include_done_button" />
</LinearLayout>

with an included button like that:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   style="?android:actionButtonStyle"
   android:id="@+id/actionbar_done"
   android:layout_width="0dp"
   android:layout_height="match_parent"
   android:layout_weight="1"
   android:onClick="saveSchwein" >
   <TextView style="?android:actionBarTabTextStyle"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_gravity="center"
       android:paddingRight="10dp"
       android:drawableLeft="@drawable/ic_action_ok"
       android:drawablePadding="8dp"
       android:gravity="center_vertical"
       android:text="@string/ok" />
</FrameLayout>

This fragment is added to a ViewPager during the onCreate() of the activity like that:

    viewPager = (ViewPager) findViewById(R.id.schweinpager);
    tabPagerAdapter = new SchweinFragmentAdapter(getSupportFragmentManager());
    viewPager.setAdapter(tabPagerAdapter);

    actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    for (String tab_name : tabs) {
        actionBar.addTab(actionBar.newTab().setText(tab_name).setTabListener(this).setTag(tab_name));
    }

So far, everything works fine. I have three fragments displayed in tabs in the action bar and I can swipe an switch between them without problems.

Now here comes my actual problem: I want to call a function inside the fragment from the custom action bar.

saveSchwein() in activity (because the function in the fragment is not found):

public void saveSchwein(View v) {
    Fragment fragment = tabPagerAdapter.getItem(0);
    ((StammdatenFragment) fragment).saveSchwein();
}

and the corrosponding function in the fragment:

public void saveSchwein() {
    if (getActivity() == null) {
        System.out.println("getActivity ist null");
    }
    if (rootView == null) {
        System.out.println("rootView ist null");
    }
    if (isAdded()) { System.out.println("Attached to Activity"); } else { System.out.println("NOT attached to Activity"); }

        Schwein schwein = new Schwein();
        schwein.setName(add_name.getText().toString());
        schwein.setGeschlecht(geschlecht_spinner.getSelectedItem().toString());
        schwein.setRasse(rasse_spinner.getSelectedItem().toString());
        schwein.setGeburtsdatum(add_geburtsdatum.getText().toString());
        schwein.setBesonderheiten(add_besonderheiten.getText().toString());

}

The rootView and the views gets assigned during the onCreateView() like this:

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

    add_name = (EditText) rootView.findViewById(R.id.add_name);
    geschlecht_spinner = (Spinner) rootView.findViewById(R.id.geschlecht_spinner);
    rasse_spinner = (Spinner) rootView.findViewById(R.id.rasse_spinner);
    add_geburtsdatum = (TextView) rootView.findViewById(R.id.add_geburtsdatum);
    add_besonderheiten = (EditText) rootView.findViewById(R.id.add_besonderheiten);

The result is that rootView and getActivity() are both null and isAdded() is false, so the fragment is no longer attached to the activity. At this line I get the nullpointerexception:

schwein.setName(add_name.getText().toString());

I am now looking for an answer on my question since several days, but did not find the right answer yet. But I made progress by learning about the lifecircle of a fragment and figured out that is has to do something with the fact that the fragment is not active during actions in the custom action bar. With an option menu like here Android Options Menu in Fragment it's working, but I prefer to use the custom action bar for that!

Any ideas?

Community
  • 1
  • 1
necromundo
  • 25
  • 1
  • 3

1 Answers1

0

What does your SchweinFragmentAdapter.getItem function look like? Often these functions simply create a Fragment, without first checking if the fragment already exists. That's the first thing to check, because in saveSchwein you are calling getItem directly, and you want to make sure you aren't creating a new fragment (that doesn't have a view or activity) and then calling the fragment's saveSchwein on that new unconfigured fragment. That would explain the behavior you are seeing.

If that is the case, then you can do this instead:

public void saveSchwein(View v) {
    Fragment fragment = getSupportFragmentManager().findFragmentByTag("StammdatenFragment");
    ((StammdatenFragment) fragment).saveSchwein();
}

Another alternative would be to change the implementation of getItem, so that it keeps track of all the fragments it has created, and only creates new ones when needed.

public static class SchweinFragmentAdapter extends FragmentPagerAdapter {
    private Fragment [] fragments = new Fragment[3];

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = fragments[position];

        if (fragment == null) {
            if (position == 0) {
                fragment = new ...
            } else if (position == 1) {
                fragment = new ...
            } else if (position == 2) {
                fragment = new ...
            }
            fragments[position] = fragment;
        }
        return fragment;
    }
}

EDIT:

Sorry, there is actually a problem with the solution I've given you. Yes, it works, but it could also lead to a memory leak because Android is already caching the fragments in FragmentPagerAdapter. I've been frustrated with this class because there is not a public API to get the tags that the adapter uses when it adds the fragments to your activity. However, since we can look at the code, we can see what they are doing.

The better solution, although it is ugly, is to use findFragmentByTag. However, the tag is not what I put in my initial answer. Try this instead:

private static final int POSITION_STAMMDATEN = //TODO the position of your fragment in the adapter

public void saveSchwein(View v) {
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(
        "android:switcher:" + viewPager.getId() + ":"
                + tabPagerAdapter.getItemId(POSITION_STAMMDATEN)");
    ((StammdatenFragment) fragment).saveSchwein();
}
Bruce
  • 2,377
  • 1
  • 17
  • 11
  • I was too blind to see that the fragments' references were not stored anywhere! :-D Thank you very much, this modified getItem() did it for me! – necromundo Nov 30 '14 at 12:01