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 są 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.