androidandroid-fragmentsnullfragmentfragment-oncreateview

Fragment within fragment not setting class variable on second call


I have a TripDetailFragment that contains a TripMemberFragment and a TripPhotosFragment. Inside the TripMemberFragment, I have a private instance variable Trip trip. This variable correctly gets set on the first call of a TripDetailFragment. However, on the second opening of a TripDetailFragment, this variable is null.

public class TripDetailFragment extends Fragment {

Trip trip;
Context context;
MainActivity.BottomNavAdapter adapter;
private final List<Fragment> fragments = new ArrayList<>();

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    //inflate view

    setUpFragments();

    //return inflate view

}


    private void setUpFragments(){
    fragments.add(new TripMemberFragment(trip));
    ((TripMemberFragment)fragments.get(0)).setTrip(trip);
    fragments.add(new TripPhotosFragment());
    adapter = new MainActivity.BottomNavAdapter(getChildFragmentManager(), fragments);
    vpTrip.setAdapter(adapter);
    vpTrip.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i1) {

        }

        @Override
        public void onPageSelected(int i) {
            switch (i) {
                case 0:
                    tripNavigation.setSelectedItemId(R.id.action_members);
                    break;
                case 1:
                    tripNavigation.setSelectedItemId(R.id.action_photos);
                    break;
            }
        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }

    });
    tripNavigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch(item.getItemId()){
                case R.id.action_members:
                    vpTrip.setCurrentItem(0);
                    return true;
                case R.id.action_photos:
                    vpTrip.setCurrentItem(1);
                    return true;
                default:
                    return false;
            }
        }
    });
}

}

public class TripMemberFragment extends Fragment {
    private Trip trip;

    public TripMemberFragment(Trip trp) {
    trip = trp;
    }

    public  TripMemberFragment(){}

        @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

        // Inflate the layout for this fragment

        members = new ArrayList<>();
        memberAdapter = new TripMemberAdapter(members, callbackMember);

        rvMembers.setLayoutManager(new LinearLayoutManager(context));
        rvMembers.setAdapter(memberAdapter);

        loadMembers();

        //return inflated view

    }

   public void loadMembers(){

       try{
           ParseRelation relation = trip.getRelation("user");
           ParseQuery query = relation.getQuery();

           query.findInBackground(new FindCallback<ParseUser>() {
           @Override
           public void done(List<ParseUser> objects, ParseException e) {
                Log.d("relation", objects.toString());
                members.clear();
                members.addAll(objects);
                memberAdapter.notifyDataSetChanged();
              }
           });
      }catch (Exception e){
        e.printStackTrace();
      }

   }
}

There no logged errors or exceptions when run but the trip variable in TripDetailFragment is null when calling loadMembers() on the second run, but it seems to be correctly getting set when the constructor for the fragment is called. I think there may be an issue with putting the setUpFragments() in the onCreateView() but don't know where else it could go.


Solution

  • First of all, you should use static factory method for creating a new instance of Fragment like this:

    public static TripMemberFragment newInstance(Trip trip) {
        TripMemberFragment fragment = new TripMemberFragment();
        Bundle extras = new Bundle();
        extras.putParcelable(“EXTRA_TRIP, trip);  // or use here putSerializable method if you working with Serializable objects
        fragment.setArguments(extras);
        return fragment;
    }
    

    And you should now everytime you need an instance of Fragment use this newInstance method. So you need to remove all constructors from your fragments to prevent creating an instance of fragments with constructors.

    Trip object can be obtained from Fragment arguments like this:

    getArguments().getParcelable(“EXTRA_TRIP”) // or use here getSerializable method if you working with Serializable objects
    

    In setUpFragments method inside of a TripDetailFragment you can replace those lines:

    fragments.add(new TripMemberFragment(trip));
    ((TripMemberFragment)fragments.get(0)).setTrip(trip);
    fragments.add(new TripPhotosFragment());
    

    with:

    fragments.add(TripMemberFragment.newInstance(trip));
    fragments.add(pTripPhotosFragment.newInstance());
    

    I have a feeling that passed object is getting lost somehow. If you pass an object with Arguments, then the object should persist on fragment instance inside getArguments() method.