Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android childfragments from viewpager getActivity returns null

I have an activity and with a button I am switching between the two fragments (MAIN & SETTINGS). In the MAIN fragment I have a ViewPager with 4 child fragments.

At first run everything works fine, but if I rotate the screen, the getActivity() for fragments within the ViewPager is returning null.

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:

    @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(); in the 4 child fragments: Search, Favorite, House and Room.

Logcat debug

1 run:

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

like image 519
Zbarcea Christian Avatar asked Oct 31 '22 14:10

Zbarcea Christian


1 Answers

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.

And the solution for getting NPE at getActivity(); in child fragments is:

Use FragmentStatePagerAdapter for your ViewPager's adapter.

and override the saved state method:

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

I don't know why, but setRetainInstance(false); does not helped me, and I think this will remain a mystery for me.

like image 86
Zbarcea Christian Avatar answered Nov 11 '22 11:11

Zbarcea Christian