36

Sto cercando di creare un DialogFragment utilizzando una vista personalizzata in un AlertDialog. Questa visualizzazione deve essere gonfiata da xml. Nella mia classe DialogFragment ho:Problema di visualizzazione personalizzata per AlertDialog in DialogFragment

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

Ho provato altri metodi di inflazione per .setView() quali:

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

e

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

Dopo aver impostato il frammento di destinazione nel frammento che è mostrando questa finestra di dialogo.

Tutti questi tentativi di gonfiare il mio risultato visualizzazione personalizzata nel seguente eccezione:

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) 

Mentre se si tenta di utilizzare s' getLayoutInflator(Bundle) in questo modo il DialogFragment:

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

ho un StackOverflowError.

Qualcuno sa come gonfiare una vista personalizzata per un AlertDialog in un DialogFragment?

risposta

72

La prima riga di errore mi dà il suggerimento che questo è legato alla modalità di creazione della finestra di dialogo, non al dialogo stesso.

Si sta creando automaticamente la finestra di dialogo (che potrebbe significare che questo viene chiamato prima che le viste siano tutte impostate) o in risposta a un clic del pulsante? Inizialmente ho avuto problemi con i frammenti dovuti all'ordine di istanziazione.

Ho usato lo stesso codice per impostare la vista come avete, e il mio risultato funziona. Ho ritagliato l'altro setup per renderlo più pulito, ma funziona con o senza di esso.

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

, ma questo mi ha funzionato, ma mi piacerebbe capire perché. Perché getLayoutInflater di dialogFragment restituisce un layout diverso in base a quello dell'Attività? – stork

+0

Ogni LayoutInflater è legato a un contesto (che differisce tra l'attività e il frammento) ei frammenti complicano le cose. Inoltre, ci sono alcune cose che non funzionano in modo ricorsivo (ad esempio, una finestra di dialogo all'interno di una finestra di dialogo). Direi che il problema si trova a metà tra l'ordine di istanziazione e la ricorsione, ma potrebbe essere più semplice semplicemente rotolare con esso piuttosto che scavare ulteriormente ... – ProjectJourneyman

+15

Questo mi ha portato al vero problema che era che devi chiamare 'setView()' prima di ogni altra cosa (come 'setTitle()', che è quello che stavo facendo male). Grazie. – ashughes

0

nel codice in cui si chiama

create(). 

Sostituire con

show(). 
+2

'onCreateDialog()' deve restituire un 'Dialog', che è il motivo per cui io uso' .create() '. Il 'DialogFragment' è _shown_ dal frammento che crea e aggiunge questo' DialogFragment' al suo 'FragmentManager' tramite' DialogFragment.show (FragmentTransaction, String) ' – ashughes

0

non ho gonfiato da XML, ma ho fatto la generazione visione dinamica in un DialogFragment successo:

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

Quello che vuoi fare invece è creare la tua vista personalizzata nel metodo onCreateView come faresti normalmente . Se vuoi fare qualcosa come cambiare il titolo della finestra di dialogo, lo fai in onCreateView.

Ecco un esempio per illustrare quello che voglio dire:

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

Poi basta chiamare:

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

E voilà, una finestra di dialogo con il proprio vista personalizzata.

+5

Sfortunatamente questo non ti permette di impostare i pulsanti di dialogo standard del SO. –

+0

esattamente. dal momento che non hai la barra delle azioni nella finestra di dialogo, stavo contando totalmente sui pulsanti di AlertDialog – njzk2

20

Sono sorpreso da queste risposte poiché nessuno di loro risolve il problema.

Un DialogFragment consente di riutilizzare la stessa UI per una finestra di dialogo e integrata nella propria app altrove come frammento. Piuttosto una caratteristica utile. Secondo la documentazione di google, puoi ottenere ciò sostituendo onCreateDialog e onCreateView. http://developer.android.com/reference/android/app/DialogFragment.html

ci sono tre scenari qui:

  1. Override onCreateDialog solo - Funziona come una finestra, ma non può essere integrato altrove.
  2. Ignora solo onCreateView - Non funziona come finestra di dialogo, ma può essere integrato altrove.
  3. Ignora entrambi - Funziona come una finestra di dialogo e può essere integrato altrove.

Soluzione: La classe AlertDialog chiama un'altra classe che chiama requestFeature. Per risolvere questo problema, non utilizzare AlertDialog, ma utilizzare una finestra di dialogo normale o qualsiasi altro messaggio restituito da super.onCreateDialog. Questa soluzione che ho trovato funziona meglio.

Avvertenza: Altre finestre di dialogo come DatePickerDialog, ProgressDialog, TimePickerDialog ereditano tutte da AlertDialog e probabilmente causano lo stesso errore.

Bottom Line: DialogFragment è buono se è necessario creare un'interfaccia molto personalizzata che deve essere utilizzata in più punti. Non sembra funzionare per riutilizzare le finestre di dialogo di Android esistenti.

+0

Dalla sperimentazione, questo problema si verifica solo su alcune versioni di Android. Ho visto fallire il 21 e il 22, mentre funziona il 23, 24. –

10

caratteristica richiesta evitare crash e utilizzare lo stesso 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(); 
    } 
} 
+1

Questa è la soluzione migliore che ho visto. –

+2

Per tua informazione puoi usare 'getShowsDialog()' per sapere se il tuo frammento è mostrato come modale. Ma guarda la risposta di @vangorra. – Neige

2

Ho avuto lo stesso problema. Nel mio caso è stato perché Android Studio ha creato un modello onCreateView che rigonfiava una nuova vista invece di restituire la vista creata in onCreateDialog. onCreateView viene chiamato dopo onCreateDialog, quindi la soluzione era semplicemente riaprire la vista dei frammenti.

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

questo ha risolto il mio problema! –

1

Di fronte allo stesso problema, ci è voluto molto tempo per eliminare l'errore. Infine il passaggio dell'ID risorsa al metodo setView() ha risolto il problema. Aggiungi set vista come di seguito:

.setView(R.layout.dialog)

+0

Utilizzare la marcatura del codice per una maggiore leggibilità. –

0

come ho bisogno di tempo per risolvere lo stesso problema (pop up un semplice testo di dialogo) ho deciso di condividere la mia soluzione:

Il connectivity_dialog.xml LayoutFile contiene un semplice TextView con il testo del messaggio:

<?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> 

l'Attività che mostra il "dialogo" implementa una classe interna (come DialogFragment è un frammento e non una finestra di dialogo; ulteriori informazioni vedi https://stackoverflow.com/a/5607560/6355541). L'attività può attivare e disattivare DialogFragment tramite due funzioni. Se utilizzi android.support.v4, si consiglia di passare 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"); 
}