50

I'm trying to create a DialogFragment using a custom view in an AlertDialog. This view must be inflated from xml. In my DialogFragment class I have:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new AlertDialog.Builder(getActivity())
        .setTitle("Title")
        .setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, null))
        .setPositiveButton(android.R.string.ok, this)
        .setNegativeButton(android.R.string.cancel, null)
        .create();
}

I have tried other inflation methods for .setView() such as:

.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, (ViewGroup) getView(), false))

and

.setView(getActivity().getLayoutInflater().inflate(R.layout.dialog, (ViewGroup) getTargetFragment().getView(), false))

After setting the target fragment in the fragment that is showing this dialog.

All of these attempts to inflate my custom view result in the following exception:

E/AndroidRuntime(32352): android.util.AndroidRuntimeException: requestFeature() must be called before adding content
E/AndroidRuntime(32352):        at com.android.internal.policy.impl.PhoneWindow.requestFeature(PhoneWindow.java:214)
E/AndroidRuntime(32352):        at com.android.internal.app.AlertController.installContent(AlertController.java:248)
E/AndroidRuntime(32352):        at android.app.AlertDialog.onCreate(AlertDialog.java:314)
E/AndroidRuntime(32352):        at android.app.Dialog.dispatchOnCreate(Dialog.java:335)
E/AndroidRuntime(32352):        at android.app.Dialog.show(Dialog.java:248)
E/AndroidRuntime(32352):        at android.support.v4.app.DialogFragment.onStart(DialogFragment.java:339)
E/AndroidRuntime(32352):        at android.support.v4.app.Fragment.performStart(Fragment.java:1288)
E/AndroidRuntime(32352):        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:873)
E/AndroidRuntime(32352):        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1041)
E/AndroidRuntime(32352):        at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:625)
E/AndroidRuntime(32352):        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1360)
E/AndroidRuntime(32352):        at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:411)
E/AndroidRuntime(32352):        at android.os.Handler.handleCallback(Handler.java:587)
E/AndroidRuntime(32352):        at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime(32352):        at android.os.Looper.loop(Looper.java:132)
E/AndroidRuntime(32352):        at android.app.ActivityThread.main(ActivityThread.java:4028)
E/AndroidRuntime(32352):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(32352):        at java.lang.reflect.Method.invoke(Method.java:491)
E/AndroidRuntime(32352):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
E/AndroidRuntime(32352):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
E/AndroidRuntime(32352):        at dalvik.system.NativeStart.main(Native Method)

While if I try to use the DialogFragment's getLayoutInflator(Bundle) like this:

.setView(getLayoutInflater(savedInstanceState).inflate(R.layout.dialog, null))

I get a StackOverflowError.

Does anyone know how to inflate a custom view for an AlertDialog in a DialogFragment?

ashughes
  • 7,155
  • 9
  • 48
  • 54

9 Answers9

92

The first error line gives me the hint that this is related to how you are creating your dialog - not the dialog itself.

Are you creating the dialog automatically (which could mean this gets called before the views are all set up) or in response to a button click? I initially had problems with fragments due to instantiation order.

I used the same code to set the view as you have, and my result works. I cut out the other setup to make this look cleaner, but it works with or without it.

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_layout, null);
    builder.setView(view);

    return builder.create();
}
ProjectJourneyman
  • 3,566
  • 1
  • 27
  • 37
  • 2
    this worked for me, but I'd like to understand why. Why is getLayoutInflater of dialogFragment returning a different layoutinflater to that of the Activity? – stork Mar 23 '12 at 10:39
  • Each LayoutInflater is tied to a context (which differs between the Activity and the fragment), and fragments complicate things. Additionally, there are a few things that don't work recursively (e.g. dialog within a dialog). I would guess that the problem lies somewhere between instantiation order and recursion, but it might be easier to just roll with it rather than dig further... – ProjectJourneyman Mar 26 '12 at 20:49
  • 16
    This led me to the real problem which was that you must call `setView()` before anything else (like `setTitle()`, which is what I was doing wrong). Thanks. – ashughes Mar 26 '12 at 23:24
  • Everything is fine but the Positive and Negative buttons are not showing now. – Shajeel Afzal Jan 26 '15 at 12:08
  • this is also working with DialogFragments testest – kamlesh parmar Aug 09 '21 at 09:51
23

I'm surprised by these answers as none of them solve the problem.

A DialogFragment allows you to reuse the same UI for both a dialog and integrated in your app elsewhere as a fragment. Quite a useful feature. As per google's documentation, you can achieve this by overriding onCreateDialog and onCreateView. http://developer.android.com/reference/android/app/DialogFragment.html

There are three scenarios here:

  1. Override onCreateDialog only - Works as a dialog but cannot be integrated elsewhere.
  2. Override onCreateView only - Does not work as a dialog but can be integrated elsewhere.
  3. Override both - Works as a dialog and can be integrated elsewhere.

Solution: The AlertDialog class is calling another class which calls requestFeature. To fix this.. Don't use the AlertDialog, instead use a plain Dialog or whatever super.onCreateDialog returns. This the solution that I have found works best.

Caveat: Other dialogs such as DatePickerDialog, ProgressDialog, TimePickerDialog all inherit from AlertDialog and will likely cause the same error.

Bottom Line: DialogFragment is good if you need to create very customized interface that needs to be used in several places. It doesn't appear to work to reuse existing android dialogs.

vangorra
  • 1,631
  • 19
  • 24
  • From experimenting, this problem happens on only certain versions of Android. Have seen it fail on 21 and 22, while it works on 23, 24. – Jeff Muir Nov 17 '16 at 08:08
  • Using a plain Dialog instead AlertDialog was the answer for me. Thanks. – Manuel Lopera Apr 26 '18 at 06:30
  • 1
    The [documentation](https://developer.android.com/reference/android/app/DialogFragment#alert-dialog) for DialogFragment says that `onCreateDialog` "*...is most useful for creating an `AlertDialog`, allowing you to display standard alerts to the user that are managed by a fragment.*" – cambunctious Sep 30 '18 at 03:08
11

Avoid request feature crash and use same layout:

public class MyCombinedFragment extends DialogFragment
{
    private boolean isModal = false;

    public static MyCombinedFragment newInstance()
    {
        MyCombinedFragment frag = new MyCombinedFragment();
        frag.isModal = true; // WHEN FRAGMENT IS CALLED AS A DIALOG SET FLAG
        return frag;
    }

    public MyCombinedFragment()
    {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    {
        if(isModal) // AVOID REQUEST FEATURE CRASH
        {
        return super.onCreateView(inflater, container, savedInstanceState);
        }
        else
        {
        View view = inflater.inflate(R.layout.fragment_layout, container, false);
        setupUI(view);
        return view;
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        AlertDialog.Builder alertDialogBuilder = null;
        alertDialogBuilder = new AlertDialog.Builder(getActivity());
        View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_layout, null);
        alertDialogBuilder.setView(view);
        alertDialogBuilder.setTitle(“Modal Dialog“);
        alertDialogBuilder.setPositiveButton("Cancel", new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int which)
            {
                dialog.dismiss();
            }
        });
        setupUI(view);
        return alertDialogBuilder.create();
    }
}
Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
  • 3
    FYI you can use `getShowsDialog()` to know if your fragment is shown as modal. But look at @vangorra answer. – Neige Apr 10 '14 at 15:09
  • I know this is old but I had to read MANY answers to this and similar questions and this is the ONLY one that mentioned you need to return super.onCreateView for modal dialogs. Thank you. – Aaron Goselin Jul 04 '18 at 03:43
2

I had the same problem. In my case it was becasue Android Studio created a template onCreateView that re-inflated a new view instead of returning the view created in onCreateDialog. onCreateView is called after onCreateDialog, so the solution was to simply reurnt the fragments view.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return this.getView();
}
h0x0
  • 485
  • 3
  • 11
1

Faced the same issue, and it took lot of time to get rid of the error. Finally passing resource ID to setView() method solved the problem. Add set view as below:

.setView(R.layout.dialog)

  • If you use `.setView(R.layout.dialog)`, but not `View view = getActivity().getLayoutInflater().inflate(R.layout.dialog, null); builder.setView(view);` you will see a layout, but you won't be able to access controls (views). So, you won't add `addTextChangedListener`, `setOnClickListener`, etc. – CoolMind Apr 03 '19 at 12:12
0

As i needed long time for solving the same problem (Pop up a simple Text Dialog) i decided to share my solution:

The layoutfile connectivity_dialog.xml contains a simple TextView with the message text:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:gravity="center"
              android:layout_gravity="center">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:text="Connectivity was lost"
        android:textSize="34sp"
        android:gravity="center"
        />
</RelativeLayout>

The Activity showing the "dialog" implements a inner class (as DialogFragment is a Fragment and not a Dialog; further info see https://stackoverflow.com/a/5607560/6355541). The Activity can activate and deactivate the DialogFragment via two functions. If you're using android.support.v4, you may want to change to getSupportFragmentManager():

public static class ConnDialogFragment extends DialogFragment {
    public static ConnDialogFragment newInstance() {
        ConnDialogFragment cdf = new ConnDialogFragment();
        cdf.setRetainInstance(true);
        cdf.setCancelable(false);
        return cdf;
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.connectivity, container, false);
    }
}

private void dismissConn() {
    DialogFragment df = (DialogFragment) getFragmentManager().findFragmentByTag("conn");
    if (df != null) df.dismiss();
}

private void showConn() {
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    Fragment prev = getFragmentManager().findFragmentByTag("conn");
    if (prev != null) ft.remove(prev);
    ft.addToBackStack(null);
    ConnDialogFragment cdf = ConnDialogFragment.newInstance();
    cdf.show(ft, "conn");
}
Community
  • 1
  • 1
vasquez
  • 449
  • 6
  • 8
0

In your code where you call

 create().

Replace with

show().
coder_For_Life22
  • 26,645
  • 20
  • 86
  • 118
  • 3
    `onCreateDialog()` should return a `Dialog`, which is why I use `.create()`. The `DialogFragment` is _shown_ by the fragment that creates and adds this `DialogFragment` to its `FragmentManager` via `DialogFragment.show(FragmentTransaction, String)` – ashughes Sep 22 '11 at 04:54
0

I haven't inflated from XML but I have done dynamic view generation in a DialogFragment successfully:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    m_editText = new EditText(getActivity());
    return new AlertDialog.Builder(getActivity())
            .setView(m_editText)
            .setPositiveButton(android.R.string.ok,null)
            .setNegativeButton(android.R.string.cancel, null);
            .create();
}
Shinyosan
  • 357
  • 3
  • 9
0

What you want to do instead is create your custom view in the onCreateView method like you normally would. If you want to do something like change the title of the dialog, you do that in onCreateView.

Here's an example to illustrate what I mean:

        @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getDialog().setTitle("hai");
        View v = inflater.inflate(R.layout.fragment_dialog, container, false);
        return v;
    }

Then you just call:

DialogFragment df = new MyDialogFragment();
df.show(..);

And voila, a dialog with your own custom view.

stork
  • 420
  • 1
  • 3
  • 12