I'm learning to use Fragment
and DialogFragment
. As a training I'm trying to extend the AlertDialog
example given in the DialogFragment page. Apparently everything is fine: when I show the options menu and click its button a dialog appears and it behaves as expected. However if the configuration changes (for instance when I rotate the screen) while the dialog is displayed then the dialog is recreated properly but clicking any of its buttons crashes the application. I've tried lots of things (for instance, static newInstance
, setRetainInstance
, onSaveInstanceState
methods and other documented suggestions) for fixing the problem but nothing seems to work so I'm at the starting point. I'm using support library v4.
This is my main activity code:
public class MainActivity extends FragmentActivity {
private MyDialogFragment dlgf;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
showMyDialog();
return true;
}
private void showMyDialog(){
dlgf = MyDialogFragment.newInstance();
dlgf.show(getSupportFragmentManager(), "dialog");
}
public void doPositive() {
// do stuff...
dlgf.getDialog().dismiss();
}
public void doNegative() {
// Cancel button pressed
dlgf.getDialog().dismiss();
}
}
and this is my DialogFragment
code:
public class MyDialogFragment extends DialogFragment {
private View view;
public static MyDialogFragment newInstance() {
MyDialogFragment dlgf = new MyDialogFragment();
return dlgf;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
view = getActivity().getLayoutInflater().inflate(R.layout.alertdialog, null);
return new AlertDialog.Builder(getActivity())
.setView(view)
.setTitle("A Title")
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonID) {
((MainActivity)getActivity()).doPositive();
}
})
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonID) {
((MainActivity)getActivity()).doNegative();
}
})
.create();
}
}
When the execution crashes after clicking the Cancel button the logcat shows a NullPointerException
in the doNegative method. If the crash occurs after clicking the OK button then the NullPointerException
occurs in the doPositive method. It seems clear that the DialogFragment
object is null.
Could someone help me, please? TIA
The Activity
instance that originally set dlgf
in showMyDialog()
is destroyed upon configuration change and a new one is created. That new one does not have dlgf
set so you get the NPE.
getActivity()
inside a Fragment
will return you the current activity instance the Fragment
is attached to. In your case it will correctly call the method in the fresh instance.
To solve your problem you could simply pass the Fragment
that caused the callback to your Activity
so you don't have to store a reference to it.
// in activity
public void doPositive(MyDialogFragment source) {
// do stuff...
source.getDialog().dismiss();
}
// in dialog fragment
.setPositiveButton("OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonID) {
((MainActivity)getActivity()).doPositive(MyDialogFragment.this);
}
})
Another possibility is to let your Activity
retain the reference to its MyDialogFragment
using onRetainNonConfigurationInstance()
/ getLastNonConfigurationInstance()
.