Android: вызов функции внутри фрагмента из пользовательской панели действий

У меня есть следующая проблема:

У меня есть собственный макет для панели действий моей деятельности:

<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>

с включенной кнопкой вот так:

<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>

Этот фрагмент добавляется в ViewPager во время onCreate() такого действия:

    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));
    }

Пока все отлично работает. У меня есть три фрагмента, отображаемых во вкладках на панели действий, и я могу без проблем провести переключение между ними.

Теперь возникает моя настоящая проблема: я хочу вызвать функцию внутри фрагмента из панели пользовательских действий.

saveSchwein () в активности (поскольку функция во фрагменте не найдена):

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

и соответствующая функция во фрагменте:

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());

}

RootView и представления назначаются во время onCreateView() следующим образом:

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);

В результате rootView и getActivity() имеют значение null, а isAdded() - false, поэтому фрагмент больше не привязан к действию. В этой строке я получаю нулевое исключение:

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

Я ищу ответ на свой вопрос уже несколько дней, но пока не нашел правильного ответа. Но я добился прогресса, узнав о жизненном цикле фрагмента, и выяснил, что это должно быть связано с тем, что фрагмент не активен во время действий в панели пользовательских действий. С опциональным меню, подобным здесь, меню опций Android в Fragment работает, но я предпочитаю использовать для этого настраиваемую панель действий!

Есть идеи?

1 ответ

Решение

Как выглядит ваша функция SchweinFragmentAdapter.getItem? Часто эти функции просто создают фрагмент, без предварительной проверки, существует ли фрагмент уже. Это первое, что нужно проверить, потому что в saveSchwein вы вызываете getItem напрямую и хотите убедиться, что вы не создаете новый фрагмент (который не имеет представления или действия), а затем вызываете saveSchwein фрагмента для этого нового ненастроенный фрагмент. Это объясняет поведение, которое вы видите.

Если это так, то вы можете сделать это вместо этого:

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

Другой альтернативой может быть изменение реализации getItem, чтобы он отслеживал все созданные им фрагменты и создавал новые только при необходимости.

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;
    }
}

РЕДАКТИРОВАТЬ:

Извините, на самом деле есть проблема с решением, которое я вам дал. Да, это работает, но это также может привести к утечке памяти, поскольку Android уже кэширует фрагменты в FragmentPagerAdapter. Я был разочарован этим классом, потому что нет общедоступного API для получения тегов, которые адаптер использует, когда добавляет фрагменты в вашу деятельность. Однако, поскольку мы можем посмотреть на код, мы можем видеть, что они делают.

Лучшее решение, хотя и некрасивое, это использовать findFragmentByTag. Тем не менее, тег не то, что я положил в своем первоначальном ответе. Попробуйте это вместо этого:

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();
}
Другие вопросы по тегам