1

In an opened DatePickerDialog, when screen orientation is changed, it reset the selected user data.

(the DatePickerDialog does NOT CLOSE and it does not maintain the selected data)

Code:

public class ActivityNeki extends FragmentActivity {
    DialogFragment newDF = null;
    private int datY, datM, datD;

    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        if(savedInstanceState == null){ setTheData(); writeTheData(); }
    }

    @Override protected void onSaveInstanceState(Bundle outState) {
        outState.putInt("izY", datY); outState.putInt("izM", datM); outState.putInt("izD", datD);
        super.onSaveInstanceState(outState);
    }
    @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState);
        datY = savedInstanceState.getInt("izY"); datM = savedInstanceState.getInt("izM"); datD = savedInstanceState.getInt("izD");
        writeTheData();
    }

    public void onClickOpenDPD(View view) {    // the method that is caled from XML onClick
        class MyDialogFragment extends DialogFragment {
            @Override public void onDestroyView() {
                if (getDialog() != null && getRetainInstance()) getDialog().setDismissMessage(null);
                super.onDestroyView();
            }
            @Override public void onCreate(Bundle state) { super.onCreate(state);
                setRetainInstance(true);
            }
            @Override public Dialog onCreateDialog(Bundle state) {
                DatePickerDialog dpd = new DatePickerDialog( getActivity(), new DatePickerDialog.OnDateSetListener() {
                    @Override public void onDateSet(DatePicker view, int leto, int mesec, int dan) {
                        datY = leto; datM = mesec; datD = dan;
                        writeTheData();
                } }, datY, datM, datD);
                return dpd;
            }
        }
        newDF = new MyDialogFragment();
        newDF.show( getSupportFragmentManager(), null );
    }
    public void setTheData(){
        Calendar c = Calendar.getInstance();
        datY = c.get(Calendar.YEAR); datM = c.get(Calendar.MONTH); datD = c.get(Calendar.DAY_OF_MONTH);
    }
    public void writeTheData(){  /* writes the data in a txtView */ }
}

suggestion me, how to solve this issue?

RobinHood
  • 10,897
  • 4
  • 48
  • 97
BRap
  • 529
  • 2
  • 10
  • 29
  • You are supposed to close it and reopen it – Amedeo Baragiola Feb 04 '15 at 16:45
  • and how I get the date, which the user set, before turning the screen(closing and reopening the dialog) ? – BRap Feb 04 '15 at 21:18
  • Similar question has been before. The solution provided here looks very elegant http://stackoverflow.com/a/15444485/2841101 – Dhir Pratap Feb 05 '15 at 05:44
  • This should actually work out of the box if you are calling setRetainInstance( true ) (which you obviously do). But you should be really careful with non static inner classes, especially if you retain the instance! http://stackoverflow.com/a/10968689/1493269 – Taig Feb 05 '15 at 10:31
  • And furthermore, what Drew suggests is actually true. setRetaininstance is not even necessary. The DialogFragment is able to restore the selected time on its own! – Taig Feb 05 '15 at 11:14

3 Answers3

2

Your code looks pretty bad.

First, you declare anonymous fragment class within the method body - however, it would be better to separate this class from Activity class or make it static inner class.

Then, you setRetainInstance(true), which tells Android not to destroy the fragment upon Activity rotation. This is intended for other use cases, yours is not the case.

I suggest you read the information on:

Handling runtime changes

Activity Lifecycle

How to communicate Activity with Fragments

Basically, much of the user input will be saved upon device rotation for you out-of-the-box if you play by Android rules (see links above).

Drew
  • 3,307
  • 22
  • 33
1

A properly implemented DialogFragment will be able to restore the state without any additional work on your side. The main problem in your code is the non-static inner class. This is flawed for 2 reasons:

  1. A non-static inner class naturally keeps a reference to its outer class (see link in #2). This is in fact a reference to your activity, your context, the views; basically everything that the user is facing. When the device is rotated or the user switches to another app, then your app is most likely shut down. However, a retained fragment will be kept alive, which can be very useful sometimes. But since it's an inner class in your case the garbage collection is not allowed to trash the context, activity, etc. because there is still an active reference around. Thus you are leaking memory. And if you access this context (e.g. to update a view) after application recreation you'll get a runtime exception telling you that you're dealing with an outdated context/view. You might wanna check out WeakReferences if you really want to go down that road.

    https://stackoverflow.com/a/10968689/1493269

  2. Another reason why non-static inner classes are dangerous is that they never provide a default constructor. But the Android framework requires every Fragment to have an empty constructor in order to instantiate it behind the scenes. If your app is teared down and restarted (e.g. on rotation change), then Android will re-instantiate your Fragment via the compulsory default constructor and later restore the state via the serialized bundle. If you do not have a default constructor: runtime exception. You kind of work around of this by retaining your fragment, but I explained in 1) why this is bad as well.

    http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor

Moral of the story: Be careful with non-static inner classes.

How your code should be working:

ActivityNeki.java

public class ActivityNeki extends FragmentActivity implements DatePickerDialog.OnDateSetListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);

        Fragment fragment = getSupportFragmentManager().findFragmentByTag( "my_dialog_tag" );

        if( fragment != null )
        {
            ( (MyDialogFragment) fragment ).listener = this;
        }
    }

    // Called from xml
    public void onClickOpenDPD(View view)
    {    
       MyDialogFragment.newInstance( x, x, x, this ).show( getSupportFragmentManager(), "my_dialog_tag" );
    }

    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth)
    {
        // Do your crazy callback stuff
    }
}

MyDialogFragment.java

class MyDialogFragment extends DialogFragment
{
    public static MyDialogFragment newInstance( int datY, int datM, int datD, DatePickerDialog.OnDateSetListener listener )
    {
        Bundle bundle = new Bundle( 3 );
        bundle.putInt( "y", datY );
        bundle.putInt( "m", datM );
        bundle.putInt( "d", datD );

        MyDialogFragment fragment = new MyDialogFragment();
        fragment.setArguments( bundle );
        fragment.listener = listener;
        return fragment;
    }

    public DatePickerDialog.OnDateSetListener listener = null;

    // Not entirely sure if this is still necessary    
    @Override
    public void onDestroyView()
    {
        if(getDialog() != null && getRetainInstance())
        {
            getDialog().setDismissMessage(null);
            super.onDestroyView();
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle state)
    {
        return new DatePickerDialog(
            getActivity(),
            listener,
            getArguments().getInt( "y" ),
            getArguments().getInt( "m" ),
            getArguments().getInt( "d" )
        );
    }
}

Please excuse minor errors, as I wrote this off the top of my head without testing.

Community
  • 1
  • 1
Taig
  • 6,718
  • 4
  • 44
  • 65
0

When the orientation is changed the app is actually destroyed and created again. So you have to save the state somewhere, e.g. in the instantState Bundle

-- new Edit

I tried with a different approach, using the OnClickListener callback in Button and the state is preserved changing orientation.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button1 = (Button) findViewById(R.id.button1);

        button1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // the method that is called from XML onClick
                class MyDialogFragment extends DialogFragment {
                    @Override
                    public void onDestroyView() {
                        if (getDialog() != null && getRetainInstance())
                            getDialog().setDismissMessage(null);
                        super.onDestroyView();
                    }

                    @Override
                    public void onCreate(Bundle state) {
                        super.onCreate(state);
                        setRetainInstance(true);
                    }

                    @Override
                    public Dialog onCreateDialog(Bundle state) {
                        DatePickerDialog dpd = new DatePickerDialog(getActivity(), new DatePickerDialog.OnDateSetListener() {
                            @Override
                            public void onDateSet(DatePicker view, int leto, int mesec, int dan) {
                                datY = leto;
                                datM = mesec;
                                datD = dan;
                                writeTheData();
                            }
                        }, datY, datM, datD);
                        return dpd;
                    }
                }
                newDF = new MyDialogFragment();
                newDF.show(getFragmentManager(), null);
            }
        });

    }
sthor69
  • 638
  • 1
  • 10
  • 25
  • You have to override onSaveInstanceState method. Look at http://developer.android.com/training/basics/activity-lifecycle/recreating.html – sthor69 Feb 04 '15 at 21:10
  • yes but I don't know how to get the data witch the user set before turning the screen? I know then that I must put this data in onSaveInstanceState, I done this for the saved time in the Activity, as you can see – BRap Feb 04 '15 at 21:20
  • But you did that on the activity, not on the dialog – sthor69 Feb 04 '15 at 21:48
  • yes because I don't know how to get the data, that I described... :( – BRap Feb 04 '15 at 21:50