Dialog aus Fragment anzeigen?

119

Ich habe einige Fragmente, die einen regelmäßigen Dialog anzeigen müssen. In diesen Dialogen kann der Benutzer eine Ja / Nein-Antwort auswählen, und dann sollte sich das Fragment entsprechend verhalten.

Jetzt hat die FragmentKlasse keine onCreateDialog()Methode zum Überschreiben, also muss ich die Dialoge wohl draußen im enthaltenen implementieren Activity. Es ist in Ordnung, aber dann Activitymuss die gewählte Antwort irgendwie an das Fragment zurückgemeldet werden. Ich könnte hier natürlich ein Rückrufmuster verwenden, so dass sich das Fragment Activitybei einer Listener-Klasse registriert und die Aktivität die Antwort darüber oder so etwas zurückmeldet.

Dies scheint jedoch ein ziemliches Durcheinander für eine einfache Aufgabe zu sein, da ein "einfacher" Ja-Nein-Dialog in einem Fragment angezeigt wird. Auch auf diese Weise Fragmentwäre ich weniger in sich geschlossen.

Gibt es einen saubereren Weg, dies zu tun?

Bearbeiten:

Die Antwort auf diese Frage erklärt nicht wirklich im Detail, wie man DialogFragments verwenden sollte, um Dialoge aus Fragmenten anzuzeigen. Also AFAIK, der Weg ist:

  1. Ein Fragment anzeigen.
  2. Instanziieren Sie bei Bedarf ein DialogFragment.
  3. Legen Sie das ursprüngliche Fragment als Ziel dieses DialogFragments fest .setTargetFragment().
  4. Zeigen Sie das DialogFragment mit .show () aus dem Originalfragment an.
  5. Wenn der Benutzer eine Option in diesem Dialogfeld auswählt, benachrichtigen Sie das ursprüngliche Fragment über diese Auswahl (z. B. hat der Benutzer auf "Ja" geklickt). Mit .getTarget () können Sie die Referenz des ursprünglichen Fragments abrufen.
  6. Schließen Sie das DialogFragment.
Zsombor Erdődy-Nagy
quelle
1
Ihre Technik funktioniert, außer wenn eine Bildschirmdrehung auftritt. Dann bekomme ich eine Kraft in die Nähe. Irgendwelche Ideen?
Weston
@ Charleston überprüfen Sie die erste Antwort von Zsombor: stackoverflow.com/questions/8235080/…
mayimaus

Antworten:

37

Sie sollten stattdessen ein DialogFragment verwenden.

mgv
quelle
9
Leider ist dieser Ansatz etwas ausführlicher als der klassische Ansatz für verwaltete Dialoge früherer Android-Versionen, aber jetzt ist er die bevorzugte Methode. Sie können das Verweisen auf das Activityvollständig vermeiden, indem Sie die Methoden putFragmentund getFragmentverwenden FragmentManager, DialogFragmentmit denen Sie direkt an das aufrufende Fragment zurückmelden können (auch nach Änderungen der Ausrichtung).
Dave
Was ist, wenn Sie ein ListFragment haben, das Dialoge anzeigen muss, nicht beide erweitern können
Marchinram
16
Die ListFragment-Unterklasse würde DialogFragments verwenden, indem neue instanziiert werden, nicht indem DialogFragment untergeordnet wird. (Ein DialogFragment ist ein Dialog, der als Fragment implementiert ist, nicht als Fragment, das Dialoge anzeigen kann.)
Nmr
4
Bitte fügen Sie einen Ausschnitt hinzu, damit wir ihn leicht verstehen können
Arpit Patel
35

Ich muss vorsichtig an der zuvor akzeptierten Antwort zweifeln, dass die Verwendung eines DialogFragments die beste Option ist. Der beabsichtigte (primäre) Zweck des DialogFragments scheint darin zu bestehen, Fragmente anzuzeigen, die selbst Dialoge sind, und nicht Fragmente anzuzeigen, in denen Dialoge angezeigt werden sollen.

Ich glaube, dass die Verwendung der Aktivität des Fragments zur Vermittlung zwischen dem Dialog und dem Fragment die bevorzugte Option ist.

Mark D.
quelle
10
Da der onCreateDialogAnsatz von Managed Dialogs ( ) bald veraltet sein wird, würde ich nicht zustimmen und sagen, dass dies DialogFragmentin der Tat der richtige Weg ist.
Dave
4
Akzeptierte Antwort bedeutet, dass ein DialogFragment anstelle eines Dialogs verwendet wird, nicht anstelle eines ListFragments, AFAICS.
nmr
Tatsächlich kann ein Dialogfragment sowohl als Dialog als auch als normales Fragment verwendet werden - siehe Einbetten von developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies
@CliveJefferies Es kann, soll aber immer noch keine anderen Dialoge aus sich heraus anzeigen. Ich denke, die Leute hier verstehen die Frage falsch.
Marcel Bro
@anoniim es hängt davon ab, wie das Dialogfragment verwendet wird. Wenn es als reguläres Fragment verwendet wird, ist es in Ordnung. Wenn es als Dialogfeld verwendet wird, sollten Sie kein weiteres Dialogfeld anzeigen.
Clive Jefferies
24

Hier ist ein vollständiges Beispiel für ein Ja / Nein-DialogFragment:

Die Klasse:

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

So starten Sie den Dialog:

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

Sie können die Klasse auch onClickListener implementieren lassen und diesen anstelle eingebetteter Listener verwenden.

Rückruf zur Aktivität

Wenn Sie einen Rückruf implementieren möchten, gehen Sie in Ihrer Aktivität folgendermaßen vor:

YourActivity extends Activity implements OnFragmentClickListener

und

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

Die Rückrufklasse:

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

Um dann einen Rückruf von einem Fragment durchzuführen, müssen Sie sicherstellen, dass der Listener wie folgt angehängt ist:

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

Und ein Rückruf wird wie folgt ausgeführt:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.
Warpzit
quelle
4
Dies erklärt nicht, wie man es von einem Fragment aus
startet
@raveN Sie würden einfach einen Rückruf zu Ihrer Aktivität tätigen, der dann das Fragment starten würde.
Warpzit
@ Warpzit Vielen Dank für die umfassende Antwort. Ich fürchte, den FragmentManager zu berühren, mit dem ich bereits gottlose Dinge mache ... Wie kann ich onClick-Ereignisse an das Fragment (ohne Dialog) zurückgeben, für das Benutzereingaben erforderlich sind? Übrigens, sollte die Transaktion nicht zum Backstack hinzugefügt werden?
Kaay
@kaay Aus der Aktivität können Sie eine beliebige öffentliche Methode in dem angegebenen Fragment aufrufen, die die neue Eingabe benötigt. Von dort sollte es ziemlich einfach sein, mit dem neuen Inhalt zu aktualisieren.
Warpzit
1
@RichardLeMesurier In der Tat sind Fragmente Höhen und Tiefen.
Warpzit
13

Für mich war es das Folgende:

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

Aktivität:

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>

Wie der Name schon dialogfragment_heightfixhiddenbtnsagt wrap_content, konnte ich einfach nicht herausfinden, wie die Höhe des unteren Knopfes halbiert werden konnte, obwohl ich dies sagte . Deshalb habe ich einen versteckten Knopf hinzugefügt, um ihn stattdessen in zwei Hälften zu schneiden. Entschuldigung für den Hack.

EpicPandaForce
quelle
1
+1 Der Referenzsatz mit setTargetFragment()wird vom System korrekt neu erstellt, wenn der Aktivitäts- / Fragmentsatz nach der Drehung neu gestartet wird. Die Referenz zeigt also automatisch auf das neue Ziel.
Richard Le Mesurier
Ich würde mir jetzt in die Nase schlagen, weil ich ButterKnife früher nicht benutzt habe. Wenn Sie ButterKnife verwenden, wird Ihre Ansicht viel schöner.
EpicPandaForce
Und Sie können Otto verwenden, um Ereignisse von Fragment zu Aktivität zu senden, sodass Sie die Schnittstelle nicht benötigen, um Magie anzuhängen. Otto Beispiel hier: stackoverflow.com/a/28480952/2413303
EpicPandaForce
@EpicPandaForce Können Sie bitte die Schnittstelle / Klasse "ShowDialog" hinzufügen? Es ist das einzige, was in Ihrem Beispiel fehlt.
ntrch
@ntrch es warpublic 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();

}

Dabei ist .test_dialog von XML benutzerdefiniert

Alex Zaraos
quelle
2

Ich bin selbst Anfänger und konnte ehrlich gesagt keine zufriedenstellende Antwort finden, die ich verstehen oder umsetzen konnte.

Hier ist also ein externer Link, der mir wirklich geholfen hat, das zu erreichen, was ich wollte. Es ist sehr einfach und leicht zu folgen.

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

DAS, WAS ICH VERSUCHTE, MIT DEM CODE ZU ERREICHEN:

Ich habe eine MainActivity, die ein Fragment hostet. Ich wollte, dass über dem Layout ein Dialogfeld angezeigt wird, in dem Sie nach Benutzereingaben gefragt und die Eingabe entsprechend verarbeitet werden. Siehe einen Screenshot

So sieht die onCreateView meines Fragments aus

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

        }
    });

ich hoffe es hilft dir

Lassen Sie mich wissen, wenn es Verwirrung gibt. :) :)

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

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

    fragment.show(fm, "tag");
}
Elmar Fazlagic
quelle
3
Bitte fügen Sie einige Erklärungen zu Ihrer Antwort hinzu
RealCheeseLord