Problema al inflar la vista personalizada para AlertDialog en DialogFragment

Estoy intentando crear un DialogFragment usando una vista personalizada en un AlertDialog . Esta vista debe estar inflada desde xml. En mi clase DialogFragment tengo:

 @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(); } 

He intentado otros métodos de inflación para .setView() como:

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

y

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

Después de establecer el fragmento de destino en el fragmento que muestra este cuadro de diálogo.

Todos estos bashs de inflar mi vista personalizada dan como resultado la siguiente excepción:

 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) 

Mientras que si trato de usar DialogFragment getLayoutInflator(Bundle) esta manera:

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

Obtengo un StackOverflowError .

¿Alguien sabe cómo inflar una vista personalizada para un AlertDialog en un DialogFragment ?

La primera línea de error me da la pista de que esto está relacionado con la forma en que está creando su diálogo, no con el diálogo mismo.

¿Está creando el diálogo automáticamente (lo que podría significar que se llama antes de que todas las vistas estén configuradas) o en respuesta a un clic del botón? Inicialmente tuve problemas con fragmentos debido al orden de creación de instancias.

Usé el mismo código para establecer la vista que tiene, y mi resultado funciona. Corté la otra configuración para que se vea más limpia, pero funciona con o sin ella.

 @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(); } 

Me sorprenden estas respuestas ya que ninguno de ellos resuelve el problema.

Un DialogFragment le permite reutilizar la misma interfaz de usuario para un diálogo e integrarlo en su aplicación como un fragmento. Muy útil. De acuerdo con la documentación de Google, puede lograr esto sobrescribiendo onCreateDialog y onCreateView. http://developer.android.com/reference/android/app/DialogFragment.html

Hay tres escenarios aquí:

  1. Sobrescribe onCreateDialog only: funciona como un diálogo pero no se puede integrar en ningún otro lugar.
  2. Anular solo enCreateView: no funciona como un diálogo, pero puede integrarse en cualquier otro lugar.
  3. Anular ambos: funciona como un diálogo y se puede integrar en cualquier otro lugar.

Solución: la clase AlertDialog llama a otra clase que llama a requestFeature. Para solucionar esto … No use AlertDialog, en su lugar utilice un cuadro de diálogo simple o lo que sea que devuelva super.onCreateDialog. Esta es la solución que he encontrado que funciona mejor.

Advertencia: Otros cuadros de diálogo como DatePickerDialog, ProgressDialog, TimePickerDialog todos heredan de AlertDialog y probablemente causen el mismo error.

Conclusión: DialogFragment es bueno si necesita crear una interfaz muy personalizada que necesite ser utilizada en varios lugares. No parece funcionar para reutilizar los diálogos de Android existentes.

Evite el locking de la función de solicitud y use el mismo diseño:

 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(); } } 

Yo tuve el mismo problema. En mi caso, fue porque Android Studio creó una plantilla en CreateView que volvió a inflar una nueva vista en lugar de devolver la vista creada en onCreateDialog. Se llama a onCreateView después de onCreateDialog, por lo que la solución fue simplemente volver a generar la vista de fragmentos.

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return this.getView(); } 

Enfrentó el mismo problema, y ​​tomó mucho tiempo deshacerse del error. Finalmente, pasar el ID del recurso al método setView () resolvió el problema. Agregue la vista de conjunto de la siguiente manera:

.setView(R.layout.dialog)

En tu código al que llamas

  create(). 

Reemplazar con

 show(). 

No he inflado de XML, pero he hecho la generación de vista dinámica en un DialogFragment con éxito:

 @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(); } 

Lo que quiere hacer en su lugar es crear su vista personalizada en el método onCreateView como lo haría normalmente. Si desea hacer algo como cambiar el título del diálogo, hágalo en onCreateView.

Aquí hay un ejemplo para ilustrar lo que quiero decir:

  @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; } 

Entonces solo llamas:

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

Y listo, un diálogo con su propia vista personalizada.

Como necesitaba mucho tiempo para resolver el mismo problema (Pop up a simple Text Dialog), decidí compartir mi solución:

El archivo de diseño connectivity_dialog.xml contiene un simple TextView con el texto del mensaje:

 < ?xml version="1.0" encoding="utf-8"?>    

La Actividad que muestra el “diálogo” implementa una clase interna (como DialogFragment es un Fragmento y no un Diálogo, más información, consulte https://stackoverflow.com/a/5607560/6355541 ). La actividad puede activar y desactivar DialogFragment a través de dos funciones. Si está utilizando android.support.v4, puede querer cambiar a 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"); }