在Android

时间:2018-12-30 14:50:51

标签: android android-fragments

启动应用程序时,一切正常,但是旋转到横向时,它崩溃,因为在Fragment中有一个NULL字段。

我不使用setRetainInstance(true)或向FragmentManager添加片段,我会在应用启动时和应用旋转时创建新的片段。

Activity OnCreate()中,我创建Fragment并将其添加到viewPager中,如下所示。

   protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         ParentBasicInfoFragment parentBasicInfoFragment = new ParentBasicInfoFragment();
         ParentUTCFragment parentUTCFragment = new ParentUTCFragment();
         ParentEventsFragment parentEventsFragment = new ParentEventsFragment();
         this.mFragments = new ArrayList<>();
         this.mFragments.add(parentBasicInfoFragment);
         this.mFragments.add(parentUTCFragment);
         this.mFragments.add(parentEventsFragment);
         this.viewpage.setOffscreenPageLimit(3);
         setCurrentTab(0);
         this.viewpage.setAdapter(new MainActivityPagerAdapter(getSupportFragmentManager(), this.mFragments));
    }

然后我在应用程序上有一个测试按钮,当我按该按钮时,它会像

  public void test(View view) {
      ((BaseFragment) MainActivity.this.mFragments.get(MainActivity.this.viewpage.
                getCurrentItem())).activityNotifiDataChange("hello");
  }

这将起作用,并且Fragments中当前的ViewPager具有被调用的方法activityNotifiDataChange(),一切正常。

当我旋转应用程序并执行相同的操作时,按下按钮,activityNotifiDataChange()被称为正常,但由于ArrayList<Fragment> mFragment现在为NULL,所以出现了空指针异常。

这是一个小示例Android Studio项目,显示了以下行为: https://drive.google.com/file/d/1Swqu59HZNYFT5hMTqv3eNiT9NmakhNEb/view?usp=sharing

启动应用程序并按下名为“ PRESS TEST”的按钮,然后旋转设备并再次按下按钮,以查看应用程序崩溃

更新解决方案,感谢@GregMoens和@EpicPandaForce

public class MainActivityPagerAdapter extends PersistenPagerAdapter<BaseFragment> {

    private static int NUM_ITEMS = 3;

    public MainActivityPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return ParentBasicInfoFragment.newInstance(0, "Page # 1");
            case 1:
                return ParentUTCFragment.newInstance(1, "Page # 2");
            case 2:
                return ParentEventsFragment.newInstance(2, "Page # 3");
            default:
                return null;
        }
    }
}

public abstract class PersistenPagerAdapter<T extends BaseFragment> extends FragmentPagerAdapter {
    private SparseArray<T> registeredFragments = new SparseArray<T>();

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

    @Override
    public T instantiateItem(ViewGroup container, int position) {
        T fragment = (T)super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public T getRegisteredFragment(ViewGroup container, int position) {
        T existingInstance = registeredFragments.get(position);
        if (existingInstance != null) {
            return existingInstance;
        } else {
            return instantiateItem(container, position);
        }
    }
}

4 个答案:

答案 0 :(得分:3)

我在您的应用中看到的主要问题是您对FragmentPagerAdapter的工作方式有误解。我经常看到这种情况,这是由于该类缺少良好的javadocs。应该实现适配器,以便在调用getItem(position)时返回一个新的片段实例。然后,只有在该页面需要新实例时,寻呼机才会调用getItem(position)。您不应该预先创建片段,然后将其传递到适配器中。您也不应持有来自活动或父片段(例如ParentBasicInfoFragment)的片段的强引用。因为请记住,片段管理器正在管理片段,您还通过更新片段并保留对其的引用来管理片段。这会导致冲突,并且轮换后,您尝试在未实际初始化(未调用onCreate())的片段上调用activityNotifiDataChange()。使用调试器和跟踪对象ID可以确认这一点。

如果您更改代码,以便FragmentPagerAdapter在需要时创建片段,并且不存储对片段或片段列表的引用,您会看到更好的结果。

答案 1 :(得分:0)

使用setRetainInstance(true)并不是一个好方法。如果您需要相同的简单信息,例如:recyclerView的位置,recyclerView的选定项,某些模型(可拆分),则可以使用方法onSaveInstanceState / onRestoreInstanceStatethere is one limitation is 1MB来完成。 Here is an example with animations how it works.

要获得更持久的持久性,请使用SharedPreferencesRoom(Google ORM),或者您可以尝试使用ViewModel with LiveData(最好的方法是在用户会话期间保存某些数据)。

答案 2 :(得分:0)

this.mFragments = new ArrayList<>();

因为这是错误的。如果使用ViewPager,则永远不要保留对片段列表的引用。只需从getItem(int position)的{​​{1}}返回Fragment的新实例,就可以了。

但是要修复您的代码,必须完全删除FragmentPagerAdapter

答案 3 :(得分:-3)

//change in AndroidManifest.xml file,
<activity
        android:name=".activity.YourActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:screenOrientation="sensor"
         />

//YourActivity in which you defines fragment.
 //may be it helps