简体   繁体   English

来自viewpager getActivity的Android childfragments返回null

[英]Android childfragments from viewpager getActivity returns null

I have an activity and with a button I am switching between the two fragments (MAIN & SETTINGS). 我有一个活动和一个按钮,我在两个片段之间切换(MAIN&SETTINGS)。 In the MAIN fragment I have a ViewPager with 4 child fragments. 在MAIN片段中,我有一个带有4个子片段的ViewPager

At first run everything works fine, but if I rotate the screen, the getActivity() for fragments within the ViewPager is returning null. 在第一次运行时一切正常,但如果我旋转屏幕, ViewPager片段的getActivity()返回null。

ActivityMain : ActivityMain

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

    // Add or show the fragments
    showHideScreenFragment(FRAGMENT_MAIN);
}

private void showHideScreenFragment(String tag) {   

    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();

    ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);

    // Get the fragment from the backstack if it is existing
    BaseFragment oldFragment = getFragmentFromBackstack(tag);

    // Get the current fragment from the layout
    BaseFragment currentFragment = getCurrentFragment();

    if (oldFragment == null) {
        if (currentFragment != null) {
             ft.hide(currentFragment);
        }

        ft.add(getMainContainerId(), getFragmentInstance(tag), tag);
    }
    else {

        if (currentFragment != null) {
            if (isSameFragment(oldFragment, currentFragment))
                return;

            ft.hide(currentFragment);
        }

        if (oldFragment.isHidden())
            ft.show(oldFragment);
    }

    ft.commit();
    fm.executePendingTransactions();
}

private BaseFragment getFragmentInstance(String tag) {
    if (tag.equals(FRAGMENT_MAIN)) return getFragmentMain();
    if (tag.equals(FRAGMENT_SETTINGS)) return getFragmentSettings();

    throw new RuntimeException("Fragment not found !");
}

private FragmentMain getFragmentMain() {
    return new FragmentMain();
}

private FragmentSettings getFragmentSettings() {
    return new FragmentSettings();
}

private BaseFragment getFragmentFromBackstack(String tag) {
    if (tag.equals(FRAGMENT_MAIN)) return getFragmentMainFromBackstack();
    if (tag.equals(FRAGMENT_SETTINGS)) return getFragmentSettingsFromBackstack();

    throw new RuntimeException("Fragment not found !");
}

private FragmentMain getFragmentMainFromBackstack() {
    return (FragmentMain) getSupportFragmentManager().findFragmentByTag(FRAGMENT_MAIN);
}

private FragmentSettings getFragmentSettingsFromBackstack() {
    return (FragmentSettings) getSupportFragmentManager().findFragmentByTag(FRAGMENT_SETTINGS);
}

private boolean isSameFragment(Fragment f1, Fragment f2) {
    return f1.getTag().equals(f2.getTag());
}

FragmentMain : FragmentMain

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {         
        View view = inflater.inflate(R.layout.fragment_main, container, false);

        viewPager = (ViewPager) view.findViewById(R.id.viewPager);

        // Add the 4 child fragments to the viewpager
        populateViewPager();

        // Debugging
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                _printFragmentStates();
            }
        }, 2500);

        return view;
    }

    private void populateViewPager() {      
        ArrayList<BaseMainFragment> fragments = new ArrayList<BaseMainFragment>();

        fragments.add(new FragmentSearch());
        fragments.add(new FragmentFavorites());
        fragments.add(new FragmentHouse());
        fragments.add(new FragmentRoom());

        adapterMain = new AdapterMain(getChildFragmentManager(), fragments);

        viewPager.setOffscreenPageLimit(4);
        viewPager.setAdapter(adapterMain);
    }

    // DEBUGGING
    private void _printFragmentStates() {
        Activity actSearch = null;
        Activity actFav = null;
        Activity actHouse = null;
        Activity actRoom = null;

        actSearch = getFragmentSearch().getActivity();
        actFav = getFragmentFavorites().getActivity();
        actHouse = getFragmentHouse().getActivity();
        actRoom = getFragmentRoom().getActivity();

        Functions.logd("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Functions.logd("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Functions.logd("Main fragment act, is null: " + (getActivity() == null));
        Functions.logd("Search act, is null: " + (actSearch == null));
        Functions.logd("Favorite act, is null: " + (actFav == null));
        Functions.logd("House act, is null: " + (actHouse == null));
        Functions.logd("Room act, is null: " + (actRoom == null));
        Functions.logd("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        Functions.logd("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}

private FragmentSearch getFragmentSearch() {
    return (FragmentSearch) adapterMain.getItem(0);
}

private FragmentFavorite getFragmentFavorite() {
    return (FragmentFavorite) adapterMain.getItem(1);
}

private FragmentHouse getFragmentHouse() {
    return (FragmentHouse) adapterMain.getItem(2);
}

private FragmentRoom getFragmentHouse() {
    return (FragmentRoom) adapterMain.getItem(3);
}

As I said, at first run everything works fine, but after I rotate the screen, I am getting null for getActivity(); 正如我所说,在第一次运行时一切正常,但在旋转屏幕后,我为getActivity();得到了null getActivity(); in the 4 child fragments: Search, Favorite, House and Room. 在4个孩子的片段中:搜索,收藏,房子和房间。

Logcat debug Logcat调试

1 run: 1运行:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Main fragment act, is null: false
Search act, is null: false
Favorite act, is null: false
House act, is null: false
Room act, is null: false
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After screen orientation changed: 屏幕方向改变后:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Main fragment act, is null: false
Search act, is null: true
Favorite act, is null: true
House act, is null: true
Room act, is null: true
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

What am I doing wrong? 我究竟做错了什么?

After hours of debugging, I figured out that if you're having only 1 fragment (without child or nested fragments) attached to your activity, then you don't need to re-add your fragment. 经过几个小时的调试,我发现如果你的活动只附加了一个片段(没有子片段或嵌套片段),那么你就不需要重新添加你的片段了。

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

    // Add or show the fragments if the savedInstance is null, otherwise let the system reattach your fragment.
    if (savedIstance == null)
       showHideScreenFragment(FRAGMENT_MAIN);
}

You don't need to reattach the fragment, the android system will do this for you. 你不需要重新附加片段,android系统会为你做这个。

And the solution for getting NPE at getActivity(); 以及在getActivity();获取NPE的解决方案getActivity(); in child fragments is: 在子片段中是:

Use FragmentStatePagerAdapter for your ViewPager 's adapter. FragmentStatePagerAdapter用于ViewPager的适配器。

and override the saved state method: 并覆盖保存的状态方法:

@Override
public Parcelable saveState() {
    return null;
}

I don't know why, but setRetainInstance(false); 我不知道为什么,但是setRetainInstance(false); does not helped me, and I think this will remain a mystery for me. 没有帮助我,我认为这对我来说仍然是一个谜。

It's a good practice to follow a pattern like this when working with fragments : 在处理片段时遵循这样的模式是一种很好的做法:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view = view.findViewById(R.id.view);
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
// do your thing here
populateViewPager();
}

1) Use OnCreateView to set your fragment's view, 2) OnViewCreated to initialize view and 3) OnActivityCreated to setup things. 1)使用OnCreateView设置片段的视图,2) OnViewCreated初始化视图,3) OnActivityCreated设置事物。

Using getActivity() inside OnActivityCreated ensures that getActivity() does not return null. OnActivityCreated使用getActivity()可确保getActivity()不返回null。 This method gets called only after the activity is fully initialized. 只有在完全初始化活动后才会调用此方法。 OnAttach , OnCreate , OnCreateView may get even before the activity is created. OnAttachOnCreateOnCreateView甚至可以在创建活动之前获得。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM