Tampilkan dialog dari fragmen?

119

Saya memiliki beberapa fragmen yang perlu menampilkan dialog biasa. Pada dialog ini, pengguna dapat memilih jawaban ya / tidak, dan fragmen harus berperilaku sesuai.

Sekarang, Fragmentkelas tidak memiliki onCreateDialog()metode untuk ditimpa, jadi saya rasa saya harus mengimplementasikan dialog di luar, di dalamnya Activity. Tidak apa-apa, tapi kemudian Activitykebutuhan untuk melaporkan kembali jawaban yang dipilih entah bagaimana ke fragmen. Saya tentu saja bisa menggunakan pola panggilan balik di sini, jadi fragmen mendaftarkan dirinya di kelas Activitywith pendengar, dan Aktivitas akan melaporkan kembali jawabannya melalui itu, atau sesuatu seperti itu.

Tapi ini tampaknya menjadi kekacauan besar untuk tugas sederhana seperti menampilkan dialog ya-tidak yang "sederhana" dalam sebuah fragmen. Juga, dengan cara ini saya Fragmentakan menjadi kurang mandiri.

Adakah cara yang lebih bersih untuk melakukan ini?

Edit:

Jawaban atas pertanyaan ini tidak benar-benar menjelaskan secara detail bagaimana seseorang harus menggunakan DialogFragments untuk menampilkan dialog dari Fragmen. Jadi AFAIK, cara yang harus dilakukan adalah:

  1. Tampilkan Fragmen.
  2. Jika perlu, buat instance DialogFragment.
  3. Tetapkan Fragmen asli sebagai target DialogFragment ini, dengan .setTargetFragment().
  4. Tampilkan DialogFragment dengan .show () dari Fragmen aslinya.
  5. Ketika pengguna memilih beberapa opsi pada DialogFragment ini, beri tahu Fragmen asli tentang pilihan ini (mis. Pengguna mengklik 'ya'), Anda bisa mendapatkan referensi Fragmen asli dengan .getTarget ().
  6. Tutup DialogFragment.
Zsombor Erdődy-Nagy
sumber
1
Teknik Anda berfungsi, kecuali saat rotasi layar terjadi. Saya mendapatkan kekuatan dekat itu. Ada ide?
Weston
@Weston lihat jawaban pertama oleh Zsombor: stackoverflow.com/questions/8235080/…
mightimaus

Jawaban:

37

Anda harus menggunakan DialogFragment sebagai gantinya.

mgv
sumber
9
Sayangnya pendekatan ini sedikit lebih bertele-tele daripada pendekatan dialog terkelola klasik dari revisi Android sebelumnya, tetapi sekarang metode ini lebih disukai. Anda bisa menghindari mereferensikan Activityseluruhnya dengan menggunakan putFragmentdan getFragmentmetode FragmentManager, memungkinkan DialogFragmentuntuk melaporkan kembali langsung ke fragmen pemanggil (bahkan setelah orientasi berubah).
Dave
Bagaimana jika Anda memiliki ListFragment yang perlu menampilkan Dialog, tidak dapat memperpanjang keduanya
marchinram
16
Subkelas ListFragment akan menggunakan DialogFragment dengan membuat instance baru, bukan dengan membuat subclass DialogFragment. (DialogFragment adalah dialog yang diimplementasikan sebagai Fragmen, bukan Fragmen yang dapat menampilkan Dialog.)
nmr
4
Harap tambahkan beberapa cuplikan agar kami dapat memahami dengan mudah
Arpit Patel
35

Saya harus hati-hati meragukan jawaban yang diterima sebelumnya bahwa menggunakan DialogFragment adalah pilihan terbaik. Tujuan (utama) DialogFragment tampaknya untuk menampilkan fragmen yang merupakan dialog itu sendiri, bukan untuk menampilkan fragmen yang memiliki dialog untuk ditampilkan.

Saya yakin bahwa menggunakan aktivitas fragmen untuk menengahi dialog dan fragmen adalah opsi yang lebih disukai.

Mark D
sumber
10
Karena pendekatan managed dialogs ( onCreateDialog) akan segera dihentikan, saya tidak setuju dan mengatakan itulah DialogFragmentcara yang harus dilakukan.
Dave
4
Jawaban yang diterima berarti menggunakan DialogFragment dan bukan Dialog, bukan ListFragment, AFAICS.
nmr
Sebenarnya fragmen dialog dapat digunakan baik sebagai dialog maupun fragmen normal - lihat embedding developer.android.com/reference/android/app/DialogFragment.html
Clive Jefferies
@CliveJefferies Bisa, tapi tetap tidak seharusnya menampilkan dialog lain dari dalam dirinya sendiri. Saya pikir orang-orang di sini salah menjawab pertanyaan.
Marcel Bro
@anoniim itu tergantung pada bagaimana fragmen dialog digunakan. Jika digunakan sebagai fragmen biasa maka tidak masalah. Jika itu digunakan sebagai dialog, maka ya, Anda tidak boleh menampilkan dialog lain.
Clive Jefferies
24

Berikut adalah contoh lengkap dari DialogFragment ya / tidak:

Kelas:

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

Untuk memulai dialog:

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

Anda juga dapat mengizinkan kelas mengimplementasikan onClickListener dan menggunakannya sebagai ganti listener yang disematkan.

Panggilan Balik ke Aktivitas

Jika Anda ingin mengimplementasikan callback, begini caranya dalam aktivitas Anda:

YourActivity extends Activity implements OnFragmentClickListener

dan

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

Kelas panggilan balik:

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

Kemudian untuk melakukan callback dari sebuah fragmen, Anda perlu memastikan listener terpasang seperti ini:

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

Dan panggilan balik dilakukan seperti ini:

mListener.onFragmentClick(SOME_ACTION, null); // null or some important object as second parameter.
Warpzit
sumber
4
Ini tidak menjelaskan bagaimana memulainya dari Fragmen
akohout
@raveN Anda cukup melakukan callback ke aktivitas Anda yang kemudian akan memulai fragmen.
Warpzit
@Warpzit Terima kasih atas jawaban lengkapnya. Saya takut menyentuh FragmentManager, yang sudah saya lakukan hal-hal yang tidak saleh dengan ... Bagaimana saya bisa meneruskan kejadian onClick kembali ke Fragmen (non-Dialog) yang membutuhkan masukan pengguna? BTW, bukankah seharusnya transaksi ditambahkan ke backstack?
kaay
@kaay dari aktivitas Anda bisa memanggil metode publik apa pun dalam fragmen yang diberikan yang memerlukan masukan baru. Dari sana seharusnya cukup mudah memperbarui dengan konten baru.
Warpzit
1
@RichardLeMesurier Memang, fragmen naik turun.
Warpzit
13

Bagi saya, itu adalah sebagai berikut-

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

Aktivitas:

public class MyActivity extends ActionBarActivity implements ShowDialog
{
    ..

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

Tata letak 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>

Seperti yang dialogfragment_heightfixhiddenbtndiperlihatkan oleh namanya , saya tidak dapat menemukan cara untuk memperbaiki bahwa tinggi tombol bagian bawah dipotong menjadi dua meskipun sudah dikatakan wrap_content, jadi saya menambahkan tombol tersembunyi untuk "dipotong" menjadi dua. Maaf atas retasannya.

EpicPandaForce
sumber
1
+1 kumpulan referensi dengan setTargetFragment()dibuat ulang dengan benar oleh sistem saat memulai ulang kumpulan Aktivitas / Fragmen setelah rotasi. Jadi referensi akan mengarah ke target baru secara otomatis.
Richard Le Mesurier
Tapi aku akan memukul diriku sendiri sekarang karena tidak menggunakan ButterKnife dulu. Gunakan ButterKnife, ini membuat tampilan Anda terlihat lebih cantik.
EpicPandaForce
Dan Anda dapat menggunakan Otto untuk mengirim acara dari Fragmen ke Aktivitas, sehingga Anda tidak memerlukan sihir penyambungan antarmuka. Contoh Otto di sini: stackoverflow.com/a/28480952/2413303
EpicPandaForce
@EpicPandaForce Tolong, dapatkah Anda menambahkan Antarmuka / Kelas "ShowDialog"? Itu satu-satunya hal yang hilang dalam contoh Anda.
ntrch
@ntrch itupublic 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();

}

di mana .test_dialog adalah kustom xml

Alex Zaraos
sumber
2

Saya sendiri seorang pemula dan sejujurnya saya tidak dapat menemukan jawaban yang memuaskan yang dapat saya pahami atau terapkan.

Jadi, inilah tautan eksternal yang sangat membantu saya mencapai apa yang saya inginkan. Ini sangat mudah dan mudah diikuti juga.

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

INI YANG SAYA COBA UNTUK MENCAPAI KODE:

Saya memiliki MainActivity yang menghosting Fragmen. Saya ingin dialog muncul di atas tata letak untuk meminta masukan pengguna dan kemudian memproses masukan yang sesuai. Lihat tangkapan layar

Inilah tampilan onCreateView dari fragmen saya

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

        }
    });

Saya harap ini akan membantu Anda

Beri tahu saya jika ada kebingungan. :)

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

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

    fragment.show(fm, "tag");
}
Elmar Fazlagic
sumber
3
tolong tambahkan beberapa penjelasan untuk jawaban Anda
RealCheeseLord