Lukas
Lukas

Reputation: 766

NullPointerException when showing a ViewPager a second time

I am using a ViewPager fragment which has two fragments as children. This works great, however when I replace the ViewPager fragment by another fragment and replace this fragment by the ViewPager fragment my application crashes with the following NullPointerException:

java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference
    at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:667)
    at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:211)
    at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:1319)
    at android.view.View.dispatchRestoreInstanceState(View.java:13756)
    at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2888)
    at android.view.View.restoreHierarchyState(View.java:13734)
    at android.support.v4.app.Fragment.restoreViewState(Fragment.java:468)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1094)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613)
    at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:517)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5294)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)

My ViewPager fragment consists of the following source code instanciating the two child fragments in case they are null. Furthermore, the ViewPagerAdapter implementation is instanciated and assigned to the ViewPager.

public class ConnectionPasswordViewPagerFragment extends Fragment
{
    private ViewPager vpConnectionPassword;
    private ConnectionPasswordViewPagerAdapter paConnectionPassword;
    private ConnectionPasswordGeneratorMACAddress passwordGeneratorMACAddress;
    private ConnectionPasswordGeneratorSerialNumber passwordGeneratorSerialNumber;

    public ConnectionPasswordViewPagerFragment()
    {
    // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);

    if ((this.passwordGeneratorSerialNumber == null) && (this.passwordGeneratorMACAddress == null))
    {
        this.passwordGeneratorMACAddress = new ConnectionPasswordGeneratorMACAddress();
        this.passwordGeneratorSerialNumber = new ConnectionPasswordGeneratorSerialNumber();
    }
    }

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

    @Override
    public void onActivityCreated(Bundle savedInstanceState)
    {
    super.onActivityCreated(savedInstanceState);

    /*if (this.paConnectionPassword == null)
    {*/
        this.paConnectionPassword = new ConnectionPasswordViewPagerAdapter(this.getChildFragmentManager(), this.getActivity(), this.passwordGeneratorMACAddress, this.passwordGeneratorSerialNumber);
    //}
    this.vpConnectionPassword = (ViewPager) this.getActivity().findViewById(R.id.vpConnectionPassword);
    this.vpConnectionPassword.setAdapter(this.paConnectionPassword);
    }
}

The ViewPagerAdapter shown below implementation keeps the reference to the child fragments in an ArrayList and implements the metods getItem(), getCount() as well as getPageTitle(). The class ViewPagerFragment simply extends the support Fragment class and provides the abstract method getPageTitleStringID() implemented by the child fragments.

public class ConnectionPasswordViewPagerAdapter extends FragmentStatePagerAdapter
{
    private static final byte NUMBER_OF_FRAGMENTS = (byte) 2;

    private ArrayList<ViewPagerFragment> childFragments;
    private Activity displayActivity;

    public ConnectionPasswordViewPagerAdapter(FragmentManager fragmentMgm, Activity displayAct, ConnectionPasswordGeneratorMACAddress generatorMACAddress, ConnectionPasswordGeneratorSerialNumber generatorSerialNumber)
    {
    super(fragmentMgm);

    this.childFragments = new ArrayList<>(ConnectionPasswordViewPagerAdapter.NUMBER_OF_FRAGMENTS);
    this.setDisplayActivity(displayAct);
    if (generatorMACAddress != null)
    {
        this.childFragments.add(generatorMACAddress);
    }
    if (generatorSerialNumber != null)
    {
        this.childFragments.add(generatorSerialNumber);
    }
    }

    public Activity getDisplayActivity()
    {
    return this.displayActivity;
    }

    public void setDisplayActivity(Activity value)
    {
    this.displayActivity = value;
    }

    @Override
    public Fragment getItem(int position)
    {
    return this.childFragments.get(position);
    }

    @Override
    public int getCount()
    {
    return ConnectionPasswordViewPagerAdapter.NUMBER_OF_FRAGMENTS;
    }

    @Override
    public CharSequence getPageTitle(int position)
    {
    return this.getDisplayActivity().getString(this.childFragments.get(position).getPageTitleStringID());
    }
}

Thanks for your help!

Upvotes: 17

Views: 3685

Answers (3)

Pankaj
Pankaj

Reputation: 8058

Better way is to you can check for FragmentManager to get Fragment if its there, if not then create a new instance as follows:

FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentByTag("YourFragmentName");

if(fragment == null)
  fragment = new YourFragment();

FragmentTransaction fragmentTransaction = manager.beginTransaction();
fragmentTransaction.replace(Your_Fragmnet_Container_Layout_id, fragment, fragment.getClass().getSimpleName());
fragmentTransaction.commit;

Upvotes: 0

Lukas
Lukas

Reputation: 766

I fixed the issue by simply creating the fragment containing the view pager every time I call the support fragment manager's replace method.

public void onSwitchToConnectionPasswordGenerator(View clickedView)
{
    this.fragConnectionPassword = new ConnectionPasswordViewPagerFragment();
    this.getSupportFragmentManager().beginTransaction().replace(R.id.flFragmentContainer, this.fragConnectionPassword).commitAllowingStateLoss();
}

Upvotes: 6

Pauland
Pauland

Reputation: 2014

I think your Viewpager is in Fragment, and this fragment have setRetainInstance set to true (i have this error too when i try to retain the parent fragment)

Upvotes: 2

Related Questions