Pokaż dialog z fragmentu?


119

Mam kilka fragmentów, które muszą pokazywać zwykłe okno dialogowe. W tych oknach dialogowych użytkownik może wybrać odpowiedź tak / nie, a następnie fragment powinien odpowiednio się zachowywać.

Teraz Fragmentklasa nie ma onCreateDialog()metody do przesłonięcia, więc myślę, że muszę zaimplementować okna dialogowe na zewnątrz, w pliku zawierającym Activity. W porządku, ale potem Activitytrzeba jakoś zgłosić wybraną odpowiedź do fragmentu. Mógłbym oczywiście użyć tutaj wzorca wywołania zwrotnego, więc fragment rejestruje się w Activityklasie słuchacza, a Activity zgłosi odpowiedź przez to lub coś w tym stylu.

Ale wydaje się, że to dość duży bałagan jak na proste zadanie, takie jak wyświetlanie „prostego” dialogu tak-nie we fragmencie. W ten sposób Fragmentbyłbym mniej samowystarczalny.

Czy jest na to jakiś czystszy sposób?

Edytować:

Odpowiedź na to pytanie nie wyjaśnia szczegółowo, w jaki sposób należy używać DialogFragments do wyświetlania okien dialogowych z fragmentów. AFAIK, droga do zrobienia to:

  1. Wyświetl fragment.
  2. W razie potrzeby Utwórz wystąpienie DialogFragment.
  3. Ustaw oryginalny fragment jako obiekt docelowy tego DialogFragment za pomocą .setTargetFragment().
  4. Pokaż DialogFragment z .show () z oryginalnego fragmentu.
  5. Gdy użytkownik wybierze jakąś opcję w tym DialogFragment, powiadom oryginalny Fragment o tym wyborze (np. Użytkownik kliknął „tak”), możesz uzyskać referencję do oryginalnego fragmentu za pomocą .getTarget ().
  6. Odrzuć DialogFragment.

1
Twoja technika działa, z wyjątkiem sytuacji, gdy następuje obrót ekranu. Wtedy zbliżam się do siły. Jakieś pomysły?
Weston,

@Weston sprawdź pierwszą odpowiedź od Zsombor: stackoverflow.com/questions/8235080/ ...
mayimaus

Odpowiedzi:


37

Zamiast tego należy użyć DialogFragment .


9
Niestety to podejście jest nieco bardziej szczegółowe niż klasyczne podejście do zarządzanych okien dialogowych z poprzednich wersji Androida, ale teraz jest metodą preferowaną. Możesz uniknąć Activitycałkowitego odwoływania się do tego , używając metod putFragmenti , umożliwiając raportowanie bezpośrednio do wywołującego fragmentu (nawet po zmianie orientacji). getFragmentFragmentManagerDialogFragment
Dave

Co zrobić, jeśli masz ListFragment, który musi wyświetlać okna dialogowe, nie możesz rozszerzyć ich obu
marchinram

16
Podklasa ListFragment użyłaby DialogFragments przez utworzenie nowych wystąpień, a nie przez utworzenie podklasy DialogFragment. (DialogFragment to okno dialogowe zaimplementowane jako fragment, a nie fragment, który może wyświetlać dialogi.)
nmr

4
Dodaj fragment, abyśmy mogli łatwo zrozumieć
Arpit Patel

35

Muszę ostrożnie wątpić w wcześniej zaakceptowaną odpowiedź, że użycie DialogFragment jest najlepszą opcją. Wydaje się, że zamierzonym (głównym) celem DialogFragment jest wyświetlanie fragmentów, które same oknami dialogowymi, a nie wyświetlanie fragmentów, które mają okna dialogowe do wyświetlenia.

Uważam, że wykorzystanie aktywności fragmentu do pośredniczenia między dialogiem a fragmentem jest opcją preferowaną.


10
Ponieważ onCreateDialogpodejście managed dialogs ( ) wkrótce zostanie wycofane, nie zgodziłbym się z tym i powiedziałbym, że DialogFragmentjest to właściwy sposób.
Dave

4
Zaakceptowana odpowiedź oznacza użycie DialogFragment zamiast Dialog, a nie zamiast ListFragment, AFAICS.
NMR

Właściwie fragment okna dialogowego może być używany zarówno jako okno dialogowe, jak i normalny fragment - zobacz osadzanie developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies

@CliveJefferies Może, ale nadal nie ma pokazywać innych okien dialogowych z samego siebie. Myślę, że ludzie tutaj źle rozumieją to pytanie.
Marcel Bro,

@anoniim zależy to od tego, jak używany jest fragment okna dialogowego. Jeśli jest używany jako zwykły fragment, to jest w porządku. Jeśli jest używany jako okno dialogowe, to tak, nie powinieneś pokazywać kolejnego okna dialogowego.
Clive Jefferies,

24

Oto pełny przykład tak / nie DialogFragment:

Klasa:

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

Aby rozpocząć okno dialogowe:

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

Możesz również pozwolić klasie zaimplementować onClickListener i użyć tego zamiast wbudowanych detektorów.

Callback to Activity

Jeśli chcesz zaimplementować callback, oto jak to się robi w Twojej aktywności:

YourActivity extends Activity implements OnFragmentClickListener

i

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

Klasa wywołania zwrotnego:

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

Następnie, aby wykonać wywołanie zwrotne z fragmentu, musisz upewnić się, że słuchacz jest dołączony w następujący sposób:

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

A oddzwonienie jest wykonywane w ten sposób:

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

4
To nie wyjaśnia, jak zacząć od Fragmentu
akohout

@raveN po prostu wykonasz callback do swojej aktywności, która następnie uruchomi fragment.
Warpzit

@Warpzit Dziękuję za wyczerpującą odpowiedź. Boję się dotknąć FragmentManagera, z którym już robię bezbożne rzeczy… Jak mogę przekazać zdarzenia onClick z powrotem do fragmentu (nie dialogowego), który wymagał wprowadzenia przez użytkownika? BTW, czy transakcja nie powinna zostać dodana do backstacka?
kaay,

@kaay z aktywności możesz wywołać dowolną metodę publiczną w danym fragmencie, która wymaga nowego wejścia. Stamtąd aktualizacja powinna być łatwa dzięki nowej zawartości.
Warpzit,

1
@RichardLeMesurier Rzeczywiście, fragmenty to wzloty i upadki.
Warpzit,

13

Dla mnie było to:

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

Czynność:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

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

Układ DialogFragment:

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

Jak nazwa dialogfragment_heightfixhiddenbtnwskazuje, po prostu nie mogłem znaleźć sposobu na naprawienie tego, że wysokość dolnego przycisku została zmniejszona o połowę, pomimo tego, że powiedziałem wrap_content, więc zamiast tego dodałem ukryty przycisk do „przecięcia” na pół. Przepraszam za włamanie.


1
+1 zestaw odniesienia z setTargetFragment()jest poprawnie odtwarzany przez system po ponownym uruchomieniu zestawu Aktywności / Fragmentu po obróceniu. Tak więc odniesienie automatycznie wskaże nowy cel.
Richard Le Mesurier

Jednak teraz uderzyłbym się w nos, że nie używałam kiedyś ButterKnife. Użyj noża ButterKnife, dzięki temu widok będzie ładniejszy.
EpicPandaForce

I możesz użyć Otto do wysłania zdarzenia z fragmentu do działania, więc nie potrzebujesz, aby interfejs dołączał magię. Przykład Otto tutaj: stackoverflow.com/a/28480952/2413303
EpicPandaForce

@EpicPandaForce Proszę, czy możesz dodać interfejs / klasę "ShowDialog"? Tylko tego brakuje w twoim przykładzie.
ntrch

@ntrch to byłopublic 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();

}

gdzie .test_dialog ma niestandardowy XML


2

Sam jestem początkującym i szczerze nie mogłem znaleźć satysfakcjonującej odpowiedzi, którą mógłbym zrozumieć lub wdrożyć.

Oto link zewnętrzny, który naprawdę pomógł mi osiągnąć to, co chciałem. Jest to bardzo proste i łatwe do naśladowania.

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

TO, CO PRÓBOWAŁEM OSIĄGNĄĆ Z KODEM:

Mam MainActivity, który obsługuje fragment. Chciałem, aby na górze układu pojawiło się okno dialogowe z prośbą o wprowadzenie danych przez użytkownika, a następnie odpowiednie przetworzenie danych wejściowych. Zobacz zrzut ekranu

Oto, jak wygląda onCreateView mojego fragmentu

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

        }
    });

Mam nadzieję, że ci to pomoże

Daj mi znać, jeśli jest jakieś zamieszanie. :)


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

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

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

3
proszę dodać wyjaśnienie do swojej odpowiedzi
RealCheeseLord
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.