Mostrare la finestra di dialogo dal frammento?


119

Ho alcuni frammenti che devono mostrare una finestra di dialogo regolare. In queste finestre di dialogo l'utente può scegliere una risposta sì / no, quindi il frammento dovrebbe comportarsi di conseguenza.

Ora, la Fragmentclasse non ha un onCreateDialog()metodo da sovrascrivere, quindi immagino di dover implementare le finestre di dialogo all'esterno, nel file Activity. Va bene, ma poi è Activitynecessario riportare in qualche modo la risposta scelta al frammento. Ovviamente potrei usare un pattern di callback qui, quindi il frammento si registra nella Activitycon una classe listener, e l'attività riporterebbe la risposta attraverso quella, o qualcosa del genere.

Ma questo sembra essere un bel pasticcio per un compito semplice come visualizzare un "semplice" dialogo sì-no in un frammento. Inoltre, in questo modo il mio Fragmentsarebbe meno autonomo.

C'è un modo più pulito per farlo?

Modificare:

La risposta a questa domanda non spiega in dettaglio come utilizzare DialogFragments per visualizzare le finestre di dialogo da Fragments. Quindi AFAIK, la strada da percorrere è:

  1. Visualizza un frammento.
  2. Quando necessario, creare un'istanza di un oggetto DialogFragment.
  3. Imposta il frammento originale come destinazione di questo DialogFragment, con .setTargetFragment().
  4. Mostra il DialogFragment con .show () dal frammento originale.
  5. Quando l'utente sceglie un'opzione su questo DialogFragment, notifica al frammento originale questa selezione (ad esempio, l'utente ha fatto clic su "sì"), è possibile ottenere il riferimento del frammento originale con .getTarget ().
  6. Chiudi il DialogFragment.

1
La tua tecnica funziona, tranne quando si verifica una rotazione dello schermo. Allora ottengo una forza vicina. Qualche idea?
Weston

@Weston controllare la prima risposta di Zsombor: stackoverflow.com/questions/8235080/...
mightimaus

Risposte:


37

Dovresti invece usare un DialogFragment .


9
Sfortunatamente questo approccio è un po 'più prolisso rispetto al classico approccio a finestre di dialogo gestite delle precedenti revisioni di Android, ma ora è il metodo preferito. È possibile evitare di fare completamente riferimento Activitya utilizzando i metodi putFragmente getFragmentdi FragmentManager, consentendo DialogFragmenta di riportare direttamente al frammento chiamante (anche dopo i cambiamenti di orientamento).
Dave

E se hai un ListFragment che deve visualizzare le finestre di dialogo, non puoi estenderle entrambe
marchinram

16
La sottoclasse ListFragment userebbe DialogFragments istanziandone di nuove, non sottoclasse DialogFragment. (Un DialogFragment è un dialogo implementato come un frammento, non un frammento che può visualizzare i dialoghi.)
nmr

4
Si prega di aggiungere un frammento in modo che possiamo capire perfettamente
Arpit Patel

35

Devo cautamente dubitare della risposta precedentemente accettata che l'utilizzo di un DialogFragment è l'opzione migliore. Lo scopo (primario) previsto del DialogFragment sembra essere quello di visualizzare frammenti che sono finestre di dialogo stesse, non di visualizzare frammenti che hanno finestre di dialogo da visualizzare.

Credo che utilizzare l'attività del frammento per mediare tra il dialogo e il frammento sia l'opzione preferibile.


10
Poiché l' onCreateDialogapproccio gestito dialogs ( ) sarà presto deprecato, non sono d'accordo e direi che DialogFragmentè davvero la strada da percorrere.
Dave

4
La risposta accettata significa utilizzare un DialogFragment invece di un Dialog, non invece di un ListFragment, AFAICS.
nmr

In realtà un frammento di dialogo può essere utilizzato sia come dialogo che come frammento normale - vedi embedding developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies

@CliveJefferies Può, ma non dovrebbe ancora mostrare altre finestre di dialogo dall'interno. Penso che i ragazzi qui stiano sbagliando la domanda.
Marcel Bro

@anoniim dipende da come viene utilizzato il frammento di dialogo. Se viene utilizzato come un frammento normale, va bene. Se viene utilizzato come finestra di dialogo, sì, non dovresti visualizzare un'altra finestra di dialogo.
Clive Jefferies

24

Ecco un esempio completo di DialogFragment sì / no:

La classe:

public class SomeDialog extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle("Title")
            .setMessage("Sure you wanna do this!")
            .setNegativeButton(android.R.string.no, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do nothing (will close dialog)
                }
            })
            .setPositiveButton(android.R.string.yes,  new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do something
                }
            })
            .create();
    }
}

Per avviare la finestra di dialogo:

        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        // Create and show the dialog.
        SomeDialog newFragment = new SomeDialog ();
        newFragment.show(ft, "dialog");

Puoi anche lasciare che la classe implementi onClickListener e lo usi al posto dei listener incorporati.

Richiamata ad attività

Se vuoi implementare il callback, ecco come viene fatto nella tua attività:

YourActivity extends Activity implements OnFragmentClickListener

e

@Override
public void onFragmentClick(int action, Object object) {
    switch(action) {
        case SOME_ACTION:
        //Do your action here
        break;
    }
}

La classe di callback:

public interface OnFragmentClickListener {
    public void onFragmentClick(int action, Object object);
}

Quindi per eseguire una richiamata da un frammento è necessario assicurarsi che l'ascoltatore sia allegato in questo modo:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnFragmentClickListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement listeners!");
    }
}

E un callback viene eseguito in questo modo:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.

4
Questo non spiega come avviarlo da un frammento
akohout il

@raveN dovresti semplicemente richiamare la tua attività che poi avvierebbe il frammento.
Warpzit

@ Warpzit Grazie per la risposta esauriente. Ho paura di toccare il FragmentManager, con cui sto già facendo cose empie ... Come posso trasmettere gli eventi onClick al frammento (non di dialogo) che richiedeva l'input dell'utente? BTW, la transazione non dovrebbe essere aggiunta al backstack?
Kaay

@kaay dall'attività puoi chiamare qualsiasi metodo pubblico nel frammento dato che necessita del nuovo input. Da lì dovrebbe essere abbastanza facile aggiornare con il nuovo contenuto.
Warpzit

1
@RichardLeMesurier In effetti, i frammenti sono alti e bassi.
Warpzit

13

Per me, è stato il seguente-

MyFragment:

public class MyFragment extends Fragment implements MyDialog.Callback
{
    ShowDialog activity_showDialog;

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        try
        {
            activity_showDialog = (ShowDialog)activity;
        }
        catch(ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "ShowDialog interface needs to be     implemented by Activity.", e);
            throw e;
        }
    }

    @Override
    public void onClick(View view) 
    {
        ...
        MyDialog dialog = new MyDialog();
        dialog.setTargetFragment(this, 1); //request code
        activity_showDialog.showDialog(dialog);
        ...
    }

    @Override
    public void accept()
    {
        //accept
    }

    @Override
    public void decline()
    {
        //decline
    }

    @Override
    public void cancel()
    {
        //cancel
    }

}

MyDialog:

public class MyDialog extends DialogFragment implements View.OnClickListener
{
    private EditText mEditText;
    private Button acceptButton;
    private Button rejectButton;
    private Button cancelButton;

    public static interface Callback
    {
        public void accept();
        public void decline();
        public void cancel();
    }

    public MyDialog()
    {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        View view = inflater.inflate(R.layout.dialogfragment, container);
        acceptButton = (Button) view.findViewById(R.id.dialogfragment_acceptbtn);
        rejectButton = (Button) view.findViewById(R.id.dialogfragment_rejectbtn);
        cancelButton = (Button) view.findViewById(R.id.dialogfragment_cancelbtn);
        acceptButton.setOnClickListener(this);
        rejectButton.setOnClickListener(this);
        cancelButton.setOnClickListener(this);
        getDialog().setTitle(R.string.dialog_title);
        return view;
    }

    @Override
    public void onClick(View v)
    {
        Callback callback = null;
        try
        {
            callback = (Callback) getTargetFragment();
        }
        catch (ClassCastException e)
        {
            Log.e(this.getClass().getSimpleName(), "Callback of this class must be implemented by target fragment!", e);
            throw e;
        }

        if (callback != null)
        {
            if (v == acceptButton)
            {   
                callback.accept();
                this.dismiss();
            }
            else if (v == rejectButton)
            {
                callback.decline();
                this.dismiss();
            }
            else if (v == cancelButton)
            {
                callback.cancel();
                this.dismiss();
            }
        }
    }
}

Attività:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

    @Override
    public void showDialog(DialogFragment dialogFragment)
    {
        FragmentManager fragmentManager = getSupportFragmentManager();
        dialogFragment.show(fragmentManager, "dialog");
    }
}

DialogFragment layout:

<?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:layout_gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/dialogfragment_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="10dp"
        android:text="@string/example"/>

    <Button
        android:id="@+id/dialogfragment_acceptbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/dialogfragment_textview"
        android:text="@string/accept"
        />

    <Button
        android:id="@+id/dialogfragment_rejectbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@+id/dialogfragment_acceptbtn"
        android:layout_below="@+id/dialogfragment_acceptbtn"
        android:text="@string/decline" />

     <Button
        android:id="@+id/dialogfragment_cancelbtn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_rejectbtn"
        android:layout_below="@+id/dialogfragment_rejectbtn"
        android:text="@string/cancel" />

     <Button
        android:id="@+id/dialogfragment_heightfixhiddenbtn"
        android:layout_width="200dp"
        android:layout_height="20dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="20dp"
        android:layout_alignLeft="@+id/dialogfragment_cancelbtn"
        android:layout_below="@+id/dialogfragment_cancelbtn"
        android:background="@android:color/transparent"
        android:enabled="false"
        android:text=" " />
</RelativeLayout>

Come dialogfragment_heightfixhiddenbtnmostra il nome , non riuscivo a trovare un modo per correggere il fatto che l'altezza del pulsante inferiore fosse stata tagliata a metà nonostante fosse detto wrap_content, quindi ho aggiunto un pulsante nascosto da "tagliare" a metà. Ci scusiamo per l'hack.


1
+1 il set di riferimento con setTargetFragment()viene ricreato correttamente dal sistema quando riavvia il set di attività / frammento dopo la rotazione. Quindi il riferimento punterà automaticamente al nuovo target.
Richard Le Mesurier

Tuttavia, mi darei un pugno sul naso ora per non aver usato ButterKnife nel corso della giornata. Usa ButterKnife, rende la tua vista molto più bella.
EpicPandaForce

E puoi usare Otto per inviare eventi da Fragment a Activity, in modo che non sia necessario che l'interfaccia alleghi la magia. Otto esempio qui: stackoverflow.com/a/28480952/2413303
EpicPandaForce

@EpicPandaForce Per favore, puoi aggiungere l'interfaccia / classe "ShowDialog"? È l'unica cosa che manca nel tuo esempio.
ntrch

@ntrch erapublic interface ShowDialog { void showDialog(DialogFragment dialogFragment); }
EpicPandaForce

3
 public void showAlert(){


     AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
     LayoutInflater inflater = getActivity().getLayoutInflater();
     View alertDialogView = inflater.inflate(R.layout.test_dialog, null);
     alertDialog.setView(alertDialogView);

     TextView textDialog = (TextView) alertDialogView.findViewById(R.id.text_testDialogMsg);
     textDialog.setText(questionMissing);

     alertDialog.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             dialog.cancel();
         }
     });
     alertDialog.show();

}

dove .test_dialog è di xml custom


2

Anch'io sono un principiante e onestamente non sono riuscito a trovare una risposta soddisfacente che potessi capire o implementare.

Quindi ecco un link esterno che mi ha davvero aiutato a ottenere ciò che volevo. È molto semplice e facile da seguire.

http://www.helloandroid.com/tutorials/how-display-custom-dialog-your-android-application

QUELLO CHE HO PROVATO A OTTENERE CON IL CODICE:

Ho una MainActivity che ospita un Fragment. Volevo che una finestra di dialogo apparisse sopra il layout per chiedere l'input dell'utente e quindi elaborare l'input di conseguenza. Guarda uno screenshot

Ecco come appare l'onCreateView del mio frammento

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View rootView = inflater.inflate(R.layout.fragment_home_activity, container, false);

    Button addTransactionBtn = rootView.findViewById(R.id.addTransactionBtn);

    addTransactionBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Dialog dialog = new Dialog(getActivity());
            dialog.setContentView(R.layout.dialog_trans);
            dialog.setTitle("Add an Expense");
            dialog.setCancelable(true);

            dialog.show();

        }
    });

Spero che ti possa aiutare

Fammi sapere se c'è qualche confusione. :)


0
    public static void OpenDialog (Activity activity, DialogFragment fragment){

    final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();

    fragment.show(fm, "tag");
}

3
per favore aggiungi qualche spiegazione alla tua risposta
RealCheeseLord
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.