Panggilan balik ke Fragmen dari DialogFragment

159

Pertanyaan: Bagaimana cara membuat panggilan balik dari DialogFragment ke Fragmen lain. Dalam kasus saya, Aktivitas yang terlibat harus benar-benar tidak menyadari DialogFragment.

Anggap saja sudah

public class MyFragment extends Fragment implements OnClickListener

Kemudian pada titik tertentu saya bisa melakukannya

DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);

Di mana MyDialogFragment terlihat seperti

protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
    DialogFragment fragment = new DialogFragment();
    fragment.listener = listener;
    return fragment;
}

Tetapi tidak ada jaminan bahwa pendengar akan ada jika DialogFragment berhenti dan dilanjutkan melalui siklus hidupnya. Satu-satunya jaminan dalam Fragmen adalah yang diteruskan melalui Bundel melalui setArguments dan getArguments.

Ada cara untuk merujuk aktivitas jika harus menjadi pendengar:

public Dialog onCreateDialog(Bundle bundle) {
    OnClickListener listener = (OnClickListener) getActivity();
    ....
    return new AlertDialog.Builder(getActivity())
        ........
        .setAdapter(adapter, listener)
        .create();
}

Tapi saya tidak ingin Kegiatan mendengarkan acara, saya butuh Fragmen. Sungguh, itu bisa berupa objek Java yang mengimplementasikan OnClickListener.

Pertimbangkan contoh konkret dari Fragmen yang menyajikan AlertDialog melalui DialogFragment. Ini memiliki tombol Ya / Tidak. Bagaimana saya bisa mengirim tombol ini menekan kembali ke Fragmen yang membuatnya?

eternalmatt
sumber
Anda menyebutkan "Tetapi tidak ada jaminan bahwa pendengar akan ada jika DialogFragment berhenti dan dilanjutkan melalui siklus hidupnya." Saya pikir keadaan Fragmen dihancurkan selama onDestroy ()? Anda pasti benar, tetapi saya hanya sedikit bingung bagaimana menggunakan status Fragmen sekarang. Bagaimana saya mereproduksi masalah yang Anda sebutkan, pendengar tidak ada?
Sean
Saya tidak melihat mengapa Anda tidak bisa menggunakan begitu saja OnClickListener listener = (OnClickListener) getParentFragment();di DialogFragment, dan Fragmen utama Anda mengimplementasikan antarmuka seperti yang Anda lakukan pada awalnya.
kiruwka
Berikut ini adalah jawaban untuk pertanyaan yang tidak terkait tetapi itu menunjukkan kepada Anda bagaimana hal ini dilakukan dengan cara yang bersih stackoverflow.com/questions/28620026/…
user2288580

Jawaban:

190

Aktivitas yang terlibat sama sekali tidak menyadari DialogFragment.

Kelas fragmen:

public class MyFragment extends Fragment {
int mStackLevel = 0;
public static final int DIALOG_FRAGMENT = 1;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (savedInstanceState != null) {
        mStackLevel = savedInstanceState.getInt("level");
    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("level", mStackLevel);
}

void showDialog(int type) {

    mStackLevel++;

    FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
    Fragment prev = getActivity().getFragmentManager().findFragmentByTag("dialog");
    if (prev != null) {
        ft.remove(prev);
    }
    ft.addToBackStack(null);

    switch (type) {

        case DIALOG_FRAGMENT:

            DialogFragment dialogFrag = MyDialogFragment.newInstance(123);
            dialogFrag.setTargetFragment(this, DIALOG_FRAGMENT);
            dialogFrag.show(getFragmentManager().beginTransaction(), "dialog");

            break;
    }
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
            case DIALOG_FRAGMENT:

                if (resultCode == Activity.RESULT_OK) {
                    // After Ok code.
                } else if (resultCode == Activity.RESULT_CANCELED){
                    // After Cancel code.
                }

                break;
        }
    }
}

}

Kelas DialogFragment:

public class MyDialogFragment extends DialogFragment {

public static MyDialogFragment newInstance(int num){

    MyDialogFragment dialogFragment = new MyDialogFragment();
    Bundle bundle = new Bundle();
    bundle.putInt("num", num);
    dialogFragment.setArguments(bundle);

    return dialogFragment;

}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.ERROR)
            .setIcon(android.R.drawable.ic_dialog_alert)
            .setPositiveButton(R.string.ok_button,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, getActivity().getIntent());
                        }
                    }
            )
            .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, getActivity().getIntent());
                }
            })
            .create();
}
}
Piotr esarlesarew
sumber
100
Saya pikir kuncinya di sini adalah setTargetFragmentdan getTargetFragment. Penggunaannya onActivityResultsedikit tidak jelas. Mungkin akan lebih baik untuk mendeklarasikan metode spesifik Anda sendiri di pemanggil Fragmen, dan menggunakannya, alih-alih membidik kembali diActivityResult. Tapi itu semua semantik pada saat itu.
eternalmatt
2
variabel level stack tidak digunakan?
Nama Tampilan
6
akankah ini bertahan dari perubahan konfigurasi config?
Maxrunner
3
Digunakan ini. Catatan: tingkat tumpukan tidak diperlukan untuk bertahan hidup rotasi atau tidur. Alih-alih onActivityResult, fragmen saya mengimplementasikan DialogResultHandler # handleDialogResult (sebuah antarmuka yang saya buat). @myCode, akan sangat membantu untuk menunjukkan nilai yang dipilih dialog yang ditambahkan ke Intent, dan kemudian membaca di dalam onActivityResult Anda. Maksudnya tidak jelas bagi pemula.
Chris Betti
7
@eternalmatt, keberatan Anda sepenuhnya masuk akal, tapi saya pikir nilai onActivityResult () adalah bahwa dijamin ada pada Fragmen mana pun, sehingga Fragmen apa pun dapat digunakan sebagai induk. Jika Anda membuat antarmuka Anda sendiri dan meminta orangtua Fragment mengimplementasikannya, maka anak tersebut hanya dapat digunakan dengan orang tua yang mengimplementasikan antarmuka itu. Menggabungkan anak ke antarmuka itu mungkin kembali menghantui Anda jika Anda mulai menggunakan anak lebih luas nanti. Menggunakan antarmuka "built-in" onActivityResult () tidak memerlukan sambungan tambahan, sehingga memungkinkan Anda sedikit lebih fleksibel.
Dalbergia
78

Solusi TargetFragment tampaknya bukan pilihan terbaik untuk fragmen dialog karena mungkin dibuat IllegalStateExceptionsetelah aplikasi dihancurkan dan diciptakan kembali. Dalam hal ini FragmentManagertidak dapat menemukan fragmen target dan Anda akan mendapatkan IllegalStateExceptiondengan pesan seperti ini:

"Fragmen tidak ada lagi untuk kunci android: target_state: index 1"

Sepertinya Fragment#setTargetFragment()tidak dimaksudkan untuk komunikasi antara Fragmen anak dan orang tua, melainkan untuk komunikasi antara saudara kandung Fragmen.

Jadi cara alternatif adalah membuat fragmen dialog seperti ini dengan menggunakan ChildFragmentManagerfragmen induk, daripada menggunakan aktivitas FragmentManager:

dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");

Dan dengan menggunakan Antarmuka, dalam onCreatemetode DialogFragmentAnda bisa mendapatkan fragmen induk:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        callback = (Callback) getParentFragment();
    } catch (ClassCastException e) {
        throw new ClassCastException("Calling fragment must implement Callback interface");
    }
}

Satu-satunya yang tersisa adalah memanggil metode panggilan balik Anda setelah langkah-langkah ini.

Untuk informasi lebih lanjut tentang masalah ini, Anda dapat memeriksa tautannya: https://code.google.com/p/android/issues/detail?id=54520

Oguz Ozcan
sumber
2
Ini juga berfungsi dengan onAttach (Konteks konteks) yang diperkenalkan ditambahkan dalam api 23.
Santa Teclado
1
INI harus menjadi jawaban yang diterima. Jawaban yang diterima saat ini adalah buggy dan fragmen tidak dimaksudkan untuk digunakan seperti ini.
NecipAllef
3
@AhmadFadli Masalahnya di sini adalah untuk mendapatkan konteks yang tepat (induk) untuk komunikasi di antara fragmen. Jika Anda akan menggunakan fragmen dialog sebagai anak aktivitas, maka seharusnya tidak ada kebingungan. FragmentManager of Activity dan getActivity () untuk mengambil panggilan balik sudah cukup.
Oguz Ozcan
1
Ini harus menjadi jawaban yang diterima. Ini dijelaskan dengan jelas dan terperinci, bukan hanya kode yang dilemparkan ke orang.
Vince
1
@lukecross ParentFragment adalah fragmen yang membuat DialogFragment (yang memanggil show ()) Tapi kelihatannya childFragmentManager tidak selamat dari konfigurasi ulang / rotasi layar ...
iwat0qs
34

Saya mengikuti langkah-langkah sederhana ini untuk melakukan hal ini.

  1. Buat antarmuka seperti DialogFragmentCallbackInterfacedengan beberapa metode seperti callBackMethod(Object data). Yang akan Anda panggil untuk meneruskan data.
  2. Sekarang Anda dapat mengimplementasikan DialogFragmentCallbackInterfaceantarmuka dalam fragmen Anda sepertiMyFragment implements DialogFragmentCallbackInterface
  3. Pada saat DialogFragmentpembuatan, tetapkan fragmen pemanggilan Anda MyFragmentsebagai fragmen target yang dibuat DialogFragmentmenggunakan myDialogFragment.setTargetFragment(this, 0)cek setTargetFragment (Fragmen fragmen, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Dapatkan objek fragmen target Anda ke Anda DialogFragmentdengan menelepon getTargetFragment()dan melemparkannya ke DialogFragmentCallbackInterface. Sekarang Anda dapat menggunakan antarmuka ini untuk mengirim data ke fragmen Anda.

    DialogFragmentCallbackInterface callback = 
               (DialogFragmentCallbackInterface) getTargetFragment();
    callback.callBackMethod(Object data);

    Itu semua dilakukan! pastikan Anda telah mengimplementasikan antarmuka ini dalam fragmen Anda.

Vijay Vankhede
sumber
4
Ini harus menjadi jawaban terbaik. Jawaban yang bagus
Md. Sajedul Karim
Pastikan juga untuk menggunakan manajer Fragmen yang sama untuk fragmen sumber dan tujuan, jika tidak getTargetFragment tidak akan berfungsi. Jadi jika Anda menggunakan childFragmentManager, itu tidak akan berfungsi karena fragmen sumber tidak dilakukan oleh manajer fragmen anak. Yang terbaik untuk memikirkan 2 fragmen ini sebagai fragmen saudara daripada fragmen orang tua / anak.
Thupten
Terus terang, lebih baik menggunakan hanya pola fragmen target ketika berkomunikasi antara 2 saudara fragmen. Dengan tidak memiliki pendengar, Anda menghindari kebocoran secara tidak sengaja1 dalam fragmen2. Saat menggunakan fragmen target, jangan gunakan listener / callback. Hanya gunakan onActivityResult(request code, resultcode, intent)untuk mengembalikan hasil ke fragmen1. Dari fragment1, setTargetFragment()dan dari fragment2, gunakan getTargetFragment(). Saat menggunakan fragmen induk / anak untuk fragmen atau aktivitas untuk fragmen, Anda dapat menggunakan pendengar atau panggilan balik karena tidak ada bahaya bocornya orangtua dalam fragmen anak.
Thupten
@Kemudian ketika Anda mengatakan "kebocoran" maksud Anda kebocoran memori atau "rincian implementasi kebocoran"? Saya tidak berpikir jawaban Vijay akan membocorkan memori lebih daripada menggunakan onActivityResulty untuk nilai pengembalian. Kedua pola akan tetap mengacu pada fragmen target. Jika Anda bermaksud membocorkan detail implementasi, maka saya pikir polanya bahkan lebih baik daripada onActivityResult. Metode panggilan balik eksplisit (jika dinamai dengan benar). Jika semua yang Anda dapatkan baik-baik saja dan DIBATALKAN, fragmen pertama harus menafsirkan apa artinya itu.
tir38
34

Mungkin agak terlambat, tetapi dapat membantu orang lain dengan pertanyaan yang sama seperti yang saya lakukan.

Anda dapat menggunakan setTargetFragmentpada Dialogsebelum ditampilkan, dan dalam dialog Anda dapat menelepon getTargetFragmentuntuk mendapatkan referensi.

Rui
sumber
Berikut adalah jawaban untuk pertanyaan lain tetapi ini juga berlaku untuk pertanyaan Anda dan merupakan solusi bersih: stackoverflow.com/questions/28620026/…
user2288580
IllegalStateException kepada saya
luke cross
19

Panduan Berkomunikasi dengan Fragmen Lain mengatakan Fragmen harus berkomunikasi melalui Aktivitas terkait .

Seringkali Anda ingin satu Fragmen untuk berkomunikasi dengan yang lain, misalnya untuk mengubah konten berdasarkan peristiwa pengguna. Semua komunikasi Fragmen-ke-Fragmen dilakukan melalui Aktivitas terkait. Dua Fragmen tidak boleh berkomunikasi secara langsung.

Edward Brey
sumber
1
bagaimana dengan fragmen batin yaitu bagaimana seharusnya sebuah fragmen dalam fragmen lain berkomunikasi dengan fragmen inang
Ravi
@Ravi: Setiap fragmen harus berkomunikasi dengan aktivitas yang umum untuk semua fragmen dengan memanggil getActivity () .
Edward Brey
1
@ Chris: Jika fragmen membutuhkan komunikasi berkelanjutan, tentukan antarmuka untuk setiap fragmen yang sesuai untuk diimplementasikan. Pekerjaan aktivitas kemudian dibatasi untuk menyediakan fragmen dengan pointer antarmuka ke fragmen mitra mereka. Setelah itu, fragmen dapat berkomunikasi secara aman "langsung" melalui antarmuka.
Edward Brey
3
Saya pikir karena penggunaan fragmen telah diperluas, ide awal untuk tidak menggunakan komunikasi fragmen langsung rusak. Misalnya dalam laci navigasi setiap fragmen anak langsung dari aktivitas secara kasar bertindak sebagai suatu aktivitas. Jadi memiliki fragmen seperti komunikasi yang berkomunikasi melalui aktivitas merusak pembacaan / fleksibilitas IMO. Bahkan sepertinya tidak ada cara yang baik untuk merangkum kerangka dialog untuk memungkinkannya bekerja dengan aktivitas dan fragmen dengan cara yang dapat digunakan kembali.
Sam
16
Saya tahu ini sudah tua, tetapi kalau-kalau ada orang lain datang ke sini saya merasa kasus yang dibicarakan dalam dokumen itu tidak berlaku ketika satu fragmen "memiliki" logika yang digunakan untuk menentukan pembuatan dan pengelolaan DialogFragment. Agak aneh membuat banyak koneksi dari fragmen ke aktivitas ketika aktivitas itu bahkan tidak yakin mengapa Dialog sedang dibuat atau dalam kondisi apa ia harus diberhentikan. Selain itu, DialogFragment sangat sederhana, dan hanya ada untuk memberi tahu pengguna dan berpotensi mendapatkan respons.
Chris
12

Anda harus mendefinisikan sebuah interfacedi kelas fragmen Anda dan mengimplementasikan antarmuka itu dalam aktivitas induknya. Detailnya diuraikan di sini http://developer.android.com/guide/components/fragments.html#EventCallbacks . Kode akan terlihat mirip dengan:

Pecahan:

public static class FragmentA extends DialogFragment {

    OnArticleSelectedListener mListener;

    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }

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

Aktivitas:

public class MyActivity extends Activity implements OnArticleSelectedListener{

    ...
    @Override
    public void onArticleSelected(Uri articleUri){

    }
    ...
}
James McCracken
sumber
1
Saya pikir Anda membaca skim terlalu cepat. Kedua segmen kode ini adalah FragmentAdan dia menganggap suatu kegiatan adalah OnArticleSelectedListener, bukan Fragmen yang memulai dia.
eternalmatt
2
Saya akan mempertimbangkan apa yang Anda coba lakukan praktik buruk. Pedoman Android merekomendasikan agar semua komunikasi fragmen-ke-fragmen dilakukan melalui aktivitas (per developer.android.com/training/basics/fragments/… ). Jika Anda benar-benar ingin semuanya ditangani di dalam, MyFragmentAnda mungkin ingin beralih menggunakan regulerAlertDialog
James McCracken
1
Saya pikir kekhawatiran dengan memiliki fragmen berbicara langsung satu sama lain adalah bahwa dalam beberapa tata letak tidak semua fragmen dapat dimuat dan seperti yang ditunjukkan dalam contoh mungkin perlu untuk beralih di fragmen. Saya tidak berpikir kekhawatiran itu valid ketika berbicara tentang meluncurkan fragmen dialog dari fragmen.
Saya menerapkan ini untuk kegiatan saya. Pertanyaan: dapatkah solusi ini diperpanjang sedemikian rupa sehingga sebuah fragmen dapat memulai dialog ini?
Bill Mote
1
Ini adalah praktik yang baik dari perspektif arsitektur, dan karenanya harus menjadi jawaban yang diterima. Menggunakan onActivityResult mengarah ke arsitektur spageti
Bruno Carrier
4

Cara yang benar untuk mengatur pendengar ke sebuah fragmen adalah dengan menyetelnya ketika itu dilampirkan . Masalah yang saya miliki adalah bahwa onAttachFragment () tidak pernah dipanggil. Setelah beberapa penyelidikan saya menyadari bahwa saya telah menggunakan getFragmentManager daripada getChildFragmentManager

Inilah cara saya melakukannya:

MyDialogFragment dialogFragment = MyDialogFragment.newInstance("title", "body");
dialogFragment.show(getChildFragmentManager(), "SOME_DIALOG");

Lampirkan di onAttachFragment:

@Override
public void onAttachFragment(Fragment childFragment) {
    super.onAttachFragment(childFragment);

    if (childFragment instanceof MyDialogFragment) {
        MyDialogFragment dialog = (MyDialogFragment) childFragment;
        dialog.setListener(new MyDialogFragment.Listener() {
            @Override
            public void buttonClicked() {

            }
        });
    }
}
pengguna1354603
sumber
3

Menurut dokumentasi resmi:

Fragmen # setTargetFragment

Target opsional untuk fragmen ini. Ini dapat digunakan, misalnya, jika fragmen ini sedang dimulai oleh yang lain, dan ketika selesai ingin memberikan hasil kembali ke yang pertama. Target yang ditetapkan di sini dipertahankan di seluruh instance melalui FragmentManager # putFragment.

Fragmen # getTargetFragment

Kembalikan fragmen target yang ditetapkan oleh setTargetFragment (Fragment, int).

Jadi Anda bisa melakukan ini:

// In your fragment

public class MyFragment extends Fragment implements OnClickListener {
    private void showDialog() {
        DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
        // Add this
        dialogFrag.setTargetFragment(this, 0);
        dialogFrag.show(getFragmentManager, null);
    }
    ...
}

// then

public class MyialogFragment extends DialogFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Then get it
        Fragment fragment = getTargetFragment();
        if (fragment instanceof OnClickListener) {
            listener = (OnClickListener) fragment;
        } else {
            throw new RuntimeException("you must implement OnClickListener");
        }
    }
    ...
}
SUPERYAO
sumber
bisakah kamu menjelaskan?
Yilmaz
Dalam hal ini, kita perlu meneruskan referensi "MyFragment" ke "MyialogFragment", dan "Fragment" menyediakan metode untuk melakukannya. Saya menambahkan deskripsi dokumen resmi, itu harus mengatakan lebih jelas daripada saya.
SUPERYAO
2

Saya menghadapi masalah yang sama. Solusi yang saya temukan adalah:

  1. Deklarasikan antarmuka di DialogFragment Anda seperti yang dijelaskan James McCracken di atas.

  2. Terapkan antarmuka dalam aktivitas Anda (bukan fragmen! Itu bukan praktik yang baik).

  3. Dari metode panggilan balik dalam aktivitas Anda, panggil fungsi publik yang diperlukan dalam fragmen Anda yang melakukan pekerjaan yang ingin Anda lakukan.

Dengan demikian, itu menjadi proses dua langkah: DialogFragment -> Activity dan kemudian Activity -> Fragment

Shailesh Mani Pandey
sumber
1

Saya mendapatkan hasil untuk Dashboard FragmenLiveWall (memanggil fragmen) dari Fragment LiveWallFilterFragment (menerima fragmen) Seperti ini ...

 LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,"");

 getActivity().getSupportFragmentManager().beginTransaction(). 
 add(R.id.frame_container, filterFragment).addToBackStack("").commit();

dimana

public static LiveWallFilterFragment newInstance(Fragment targetFragment,String anyDummyData) {
        LiveWallFilterFragment fragment = new LiveWallFilterFragment();
        Bundle args = new Bundle();
        args.putString("dummyKey",anyDummyData);
        fragment.setArguments(args);

        if(targetFragment != null)
            fragment.setTargetFragment(targetFragment, KeyConst.LIVE_WALL_FILTER_RESULT);
        return fragment;
    }

setResult kembali ke memanggil fragmen suka

private void setResult(boolean flag) {
        if (getTargetFragment() != null) {
            Bundle bundle = new Bundle();
            bundle.putBoolean("isWorkDone", flag);
            Intent mIntent = new Intent();
            mIntent.putExtras(bundle);
            getTargetFragment().onActivityResult(getTargetRequestCode(),
                    Activity.RESULT_OK, mIntent);
        }
    }

onActivityResult

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == KeyConst.LIVE_WALL_FILTER_RESULT) {

                Bundle bundle = data.getExtras();
                if (bundle != null) {

                    boolean isReset = bundle.getBoolean("isWorkDone");
                    if (isReset) {

                    } else {
                    }
                }
            }
        }
    }
Zar E Ahmer
sumber
1

Diperbarui:

Saya membuat perpustakaan berdasarkan kode inti saya yang menghasilkan casting untuk Anda dengan menggunakan @CallbackFragmentdan @Callback.

https://github.com/zeroarst/callbackfragment .

Dan contoh memberi Anda contoh yang mengirim panggilan balik dari sebuah fragmen ke fragmen lain.

Jawaban lama:

Saya membuat BaseCallbackFragmentdan membuat anotasi @FragmentCallback. Saat ini meluas Fragment, Anda dapat mengubahnya ke DialogFragmentdan akan bekerja. Ia memeriksa implementasi dengan urutan berikut: getTargetFragment ()> getParentFragment ()> konteks (aktivitas).

Maka Anda hanya perlu memperluas dan mendeklarasikan antarmuka Anda dalam fragmen Anda dan memberikan penjelasannya, dan fragmen dasar akan melakukan sisanya. Anotasi juga memiliki parameter mandatoryuntuk Anda menentukan apakah Anda ingin memaksa fragmen untuk mengimplementasikan panggilan balik.

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

    @FragmentCallback
    public interface FragmentInteractionListener {
        void onEcho(EchoFragment fragment, String echo);
    }
}

https://gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93

Arst
sumber
1

Kawan Kotlin, kita mulai!

Jadi masalah yang kita miliki adalah bahwa kita menciptakan suatu kegiatan,, MainActivitypada aktivitas itu kita menciptakan sebuah fragmen, FragmentAdan sekarang kita ingin membuat fragmen dialog FragmentAselain menyebutnya FragmentB. Bagaimana kita mendapatkan hasil dari FragmentBbelakang FragmentAtanpa melalui MainActivity?

catatan:

  1. FragmentAadalah fragmen anak MainActivity. Untuk mengelola fragmen yang dibuat di FragmentAkami akan menggunakan childFragmentManageryang melakukannya!
  2. FragmentAadalah fragmen induk dari FragmentB, untuk mengakses FragmentAdari dalam FragmentBkita akan menggunakan parenFragment.

Karena itu, di dalam FragmentA,

class FragmentA : Fragment(), UpdateNameListener {
    override fun onSave(name: String) {
        toast("Running save with $name")
    }

    // call this function somewhere in a clickListener perhaps
    private fun startUpdateNameDialog() {
        FragmentB().show(childFragmentManager, "started name dialog")
    }
}

Inilah fragmen dialognya FragmentB.

class FragmentB : DialogFragment() {

    private lateinit var listener: UpdateNameListener

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            listener = parentFragment as UpdateNameListener
        } catch (e: ClassCastException) {
            throw ClassCastException("$context must implement UpdateNameListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            val binding = UpdateNameDialogFragmentBinding.inflate(LayoutInflater.from(context))
            binding.btnSave.setOnClickListener {
                val name = binding.name.text.toString()
                listener.onSave(name)
                dismiss()
            }
            builder.setView(binding.root)
            return builder.create()
        } ?: throw IllegalStateException("Activity can not be null")
    }
}

Berikut ini antarmuka yang menghubungkan keduanya.

interface UpdateNameListener {
    fun onSave(name: String)
}

Itu dia.

Ssenyonjo
sumber
1
Saya mengikuti dokumen ini: developer.android.com/guide/topics/ui/dialogs dan tidak berhasil. Terima kasih banyak. Saya harap hal induk ini berfungsi seperti yang diharapkan setiap saat :)
UmutTekin
1
jangan lupa untuk mengatur pendengar menjadi null di dalam onDetach :)
BekaBot
@BekaBot Terima kasih atas komentarnya. Saya telah melakukan riset dan tampaknya tidak perlu menutup pendengar. stackoverflow.com/a/37031951/10030693
Ssenyonjo
0

Saya memecahkan ini dengan cara yang elegan dengan RxAndroid. Menerima pengamat di konstruktor DialogFragment dan mendaftar untuk diamati dan mendorong nilai ketika panggilan balik dipanggil. Kemudian, di Fragmen Anda, buat kelas bagian dalam Observer, buat instance dan berikan dalam konstruktor DialogFragment. Saya menggunakan WeakReference di pengamat untuk menghindari kebocoran memori. Ini kodenya:

BaseDialogFragment.java

import java.lang.ref.WeakReference;

import io.reactivex.Observer;

public class BaseDialogFragment<O> extends DialogFragment {

    protected WeakReference<Observer<O>> observerRef;

    protected BaseDialogFragment(Observer<O> observer) {
        this.observerRef = new WeakReference<>(observer);
   }

    protected Observer<O> getObserver() {
    return observerRef.get();
    }
}

DatePickerFragment.java

public class DatePickerFragment extends BaseDialogFragment<Integer>
    implements DatePickerDialog.OnDateSetListener {


public DatePickerFragment(Observer<Integer> observer) {
    super(observer);
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    // Use the current date as the default date in the picker
    final Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    int month = c.get(Calendar.MONTH);
    int day = c.get(Calendar.DAY_OF_MONTH);

    // Create a new instance of DatePickerDialog and return it
    return new DatePickerDialog(getActivity(), this, year, month, day);
}

@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
        if (getObserver() != null) {
            Observable.just(month).subscribe(getObserver());
        }
    }
}

MyFragment.java

//Show the dialog fragment when the button is clicked
@OnClick(R.id.btn_date)
void onDateClick() {
    DialogFragment newFragment = new DatePickerFragment(new OnDateSelectedObserver());
    newFragment.show(getFragmentManager(), "datePicker");
}
 //Observer inner class
 private class OnDateSelectedObserver implements Observer<Integer> {

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public void onNext(Integer integer) {
       //Here you invoke the logic

    }

    @Override
    public void onError(Throwable e) {

    }

    @Override
    public void onComplete() {

    }
}

Anda dapat melihat kode sumber di sini: https://github.com/andresuarezz26/carpoolingapp

Gerardo Suarez
sumber
1
Lucunya tentang Android adalah ada sesuatu yang disebut siklus hidup. Fragmen dasar atau fragmen dialog harus dapat mempertahankan keadaan (dan koneksi mereka) dari peristiwa siklus hidup. Panggilan balik atau pengamat tidak dapat diserialisasi dan karenanya memiliki masalah yang sama di sini.
GDanger