Search code examples
javaandroidandroid-fragmentsandroid-viewpagerfragmentstatepageradapter

FragmentStatePageAdapter Retaining interface attached to fragment


I'm using a FragmentStatePagerAdapter (for the ViewPager), The fragments which are attached to the ViewPager have fields which I wish to pass the data back to the Activity. My problem lies in that the interface objects which I pass to the fragments are lost once the device has been rotated.

My question is how do I retain the interface objects.
Now the obligatory code snippets:

The Activity:

public class TheActivity extends AppCompatActivity {

    static final int PageCount = 2;
    static final int PageA = 0;
    static final int PageB = 1;

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

        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {

                switch (position) {
                    case PageA: {
                        PageAFragment fragment = PageAFragment.CreateFragment();
                        fragment.setDataListener(new PageAFragment.DataListener() {
                            @Override
                            public void OnPageADataChange(String value) {
                                PageAData = value;
                            }
                        });
                        return fragment;
                    }
                    case PageB: {
                        PageBFragment fragment = PageBFragment.CreateFragment(2);
                        fragment.setDataListener(new PageBFragment.DataListener() {
                            @Override
                            public void OnPageAOneDataChange(String value) {
                                PageBDataOne = value;
                            }

                            @Override
                            public void OnPageATwoDataChange(String value) {
                                PageBDataTwo = value;
                            }
                        });
                        return fragment;
                    }
                }

                return null;
            }

            @Override
            public int getCount() {
                return PageCount;
            }
        });
    }
}

The Fragments:

public class PageAFragment extends Fragment {

    private DataListener mListener;

    public static PageAFragment CreateFragment() {
        return new PageAFragment();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.page_a, container, false);
        EditText editText = (EditText) view.findViewById(R.id.edittext_data_a);
        editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionID, KeyEvent event) {

                if (actionID == EditorInfo.IME_ACTION_DONE)
                    mListener.OnPageADataChange(editText.getText().toString());
                return false;
            }
        });
        return view;
    }

    public void setDataListener(DataListener dataListener) {
        mListener = dataListener;
    }

    public interface DataListener {
        void OnPageADataChange(String value);
    }
}


public class PageBFragment extends Fragment {

    private DataListener mListener;

    public static PageBFragment CreateFragment(int data) {
        PageBFragment fragment = new PageBFragment();
        Bundle bundle = new Bundle();
        bundle.putInt("Data", data);
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.page_b, container, false);
        EditText editTextOne = (EditText) view.findViewById(R.id.edittext_data_b_one);
        editTextOne.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionID, KeyEvent event) {
                if (actionID == EditorInfo.IME_ACTION_DONE)
                    mListener.OnPageAOneDataChange(editTextOne.getText().toString());
                return false;
            }
        });
        EditText editTextTwo = (EditText) view.findViewById(R.id.edittext_data_b_two);
        editTextTwo.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionID, KeyEvent event) {
                if (actionID == EditorInfo.IME_ACTION_DONE)
                    mListener.OnPageATwoDataChange(editTextTwo.getText().toString());
                return false;
            }
        });
        return view;
    }

    public void setDataListener(DataListener dataListener) {
        mListener = dataListener;
    }

    public interface DataListener {
        void OnPageAOneDataChange(String value);

        void OnPageATwoDataChange(String value);
    }
}

Solution

  • I think you should re read this https://developer.android.com/training/basics/fragments/communicating.html.

    Let the activity implement the interfaces and use the onAttach() onDetach() methods to reset the reference of the callback to the activity.

    With your code if you try to retain the fragments' state (setRetainInstance(true), onsaveInstanceState()) which includes the interface objects you will probably have issues with memory leak or your code will become too complicated for no reason.