1

I have an Activity A with a fragment frag2. Inside the fragment I have a RecyclerView and Adapter to show a list of custom class objects. Adding objects to the adapter is handled programmatically. I have a button inside TwoFragment that opens a FragmentDialog. I'd like to add an object to my Adapter by confirming this dialog, but it seems that the adapter is null when called from the FragmentDialog.

The same adapter is not null, and works if I call it from the fragment OnClick.

Moreover the adapter is null only after screen rotation, it works fine before rotating.

To communicate between the two Fragments I implement a communicator class in activity A.

Activity A

public void respond(String type) {
        frag2.addSupport(type);
    }

frag2

public RecyclerView rv;
public ArrayList<support> supports;
public myAdapter adapter;

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supports = new ArrayList<>();
        adapter = new myAdapter(supports);
    }

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View layout = inflater.inflate( R.layout.fragment_two, container, false);
        layout.setId(R.id.frag2);


        if (savedInstanceState!=null)
        {
             supports = savedInstanceState.getParcelableArrayList("supports"); 
        }

        rv = (RecyclerView) layout.findViewById(R.id.rv);
        adapter = new myAdapter(supports);
        rv.setAdapter(myAdapter);
        rv.setLayoutManager(new LinearLayoutManager(getActivity()));
        rv.setItemAnimator(new DefaultItemAnimator());

@Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id){
            case R.id.button1:
                addSupport(type); // THIS WORKS ALWAYS, even after screen rotate
                break;

            case R.id.button2:
                showDialog(); 
                break;
        }

    }

public void showDialog(){

        FragmentManager manager = getFragmentManager();
        myDialog dialog = new myDialog();
        dialog.show(manager, "dialog");
    }

public void addSupport(String type){

    adapter.addItem(new support(type));  // this line gives null pointer on adapter, but only if called after screen rotate and only if called from the dialog
            }

dialog

communicator comm;

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


        View view = inflater.inflate(R.layout.dialog, null);
        comm = (myCommunicator) getActivity();
        create = (Button) view.findViewById(R.id.button_ok);
        create.setOnClickListener(this);

        return view;
    }



@Override
    public void onClick(View v) {

        if(v.getId()==R.id.button_ok)
        {
            // some controls to set type
            comm.respond(type)
            dismiss();
        }
        else {

            dismiss();
        }

myAdapter

public class myAdapter extends RecyclerView.Adapter<myAdapter.VH> {
    private LayoutInflater inflater;
    private ArrayList<support> data = new ArrayList<>();

// settings for viewholder

public myAdapter (ArrayList<support> data)
    {
        this.data=data;
    }

    public void addItem(support dataObj) {

        data.add(dataObj);
        notifyItemInserted(data.size());
    }

}

logcat

FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference

I hope there are no mistakes, I shortened the code for better understanding. Keep in mind that everything works if I never rotate the screen.

I'm a beginner with android and I'm stuck with this for several days now. Please, help.

Helike
  • 11
  • 3

1 Answers1

0

To understand the problem, it's as you say:

.. everything works if I never rotate the screen

So firstly to understand what happens on rotation, this is a quote from the Android Developer website:

Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).

Ok, now to understand the error:

FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference

Essentially, in your dialog class, you have created a strong dependency by declaring :

comm = (myCommunicator) getActivity();

because comm references objects which would have been destroyed on rotation, hence the NullPointerException.

To further understand runtime changes, such as orientation changes, I'd recommend going through Handling Runtime Changes.

Update

Thank you for your answer, what would you recommend instead of comm = (myCommunicator) getActivity(); ?

The solution comes in 3 parts:

  1. Make sure the onCreate of Activity A has the following:


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

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        frag2 = (Frag2) fm.findFragmentByTag(“frag2”);

        // create frag2 only for the first time
        if (frag2 == null) {
            // add the fragment
            frag2 = new Frag2();
            fm.beginTransaction().add(frag2 , “frag2”).commit();
        }
        ......

    }

  1. Add setRetainInstance(true) to the onCreate of frag2.

  2. Remove the implicit referencing i.e. comm = (myCommunicator) getActivity();, and implement something more loosely coupled for dialog.

dialog



    public interface Communicator {

        void respond(String type);
    }

    Communicator comm;

    ....

    public void addCommunicator(Communicator communicator) {

        comm = communicator;
    }

    public void removeCommunicator() {

        comm = null;
    }

    @Override
    public void onClick(View v) {
        if((v.getId()==R.id.button_ok) && (comm!=null))
        {
            // some controls to set type
            comm.respond(type);
        }
        // Regardless of what button is pressed, the dialog will dismiss
        dismiss();
    }

This allows you do the following in frag2 (or any other class for that matter):

frag2

<pre><code>
public class Frag2 extends Fragment implements dialog.Communicator {

    ........

    public void showDialog() {

        FragmentManager manager = getFragmentManager();
        myDialog dialog = new myDialog();
        dialog.addCommunicator(this);
        dialog.show(manager, "dialog");
    }

    @Override
    public void respond(String type){       

        adapter.addItem(new support(type));
    }

}

ade.akinyede
  • 2,214
  • 1
  • 13
  • 17
  • Thank you for your answer, what would you recommend instead of comm = (myCommunicator) getActivity(); ? – Helike Apr 10 '16 at 13:35