【问题标题】:Android: Calling a function inside a fragment from a custom action barAndroid:从自定义操作栏调用片段内的函数
【发布时间】:2014-11-29 16:30:03
【问题描述】:

我有以下问题:

我的 activity 的操作栏有一个自定义布局,如下所示:

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

这个片段在活动的 onCreate() 期间被添加到 ViewPager,如下所示:

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

到目前为止,一切正常。我在操作栏中的选项卡中显示了三个片段,我可以毫无问题地在它们之间滑动切换。

现在我的实际问题来了: 我想从自定义操作栏调用片段内的函数。

activity中的saveSchwein()(因为没有找到fragment中的函数):

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,所以fragment不再附加到activity。在这一行,我得到了 nullpointerexception:

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

几天以来,我一直在寻找有关我的问题的答案,但还没有找到正确的答案。但是我通过了解片段的生命周期取得了进展,并发现这与片段在自定义操作栏中的操作期间不活动的事实有关。 使用像 Android Options Menu in Fragment 这样的选项菜单,它可以工作,但我更喜欢使用自定义操作栏!

有什么想法吗?

【问题讨论】:

    标签: android android-fragments android-actionbar fragment android-lifecycle


    【解决方案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();
    }
    

    【讨论】:

    • 我太盲目了,没有看到片段的引用没有存储在任何地方! :-D 非常感谢,这个修改后的 getItem() 为我做了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多