Bir İletişim Kutusundan Bir Parçayı Geri Çağırma


159

Soru: DialogFragment'tan başka bir Fragment'a geri arama nasıl oluşturulur? Benim durumumda, ilgili Faaliyet DialogFragment'tan tamamen habersiz olmalıdır.

Düşünün ki

public class MyFragment extends Fragment implements OnClickListener

Sonra bir noktada ben olabilir yapmak

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

MyDialogFragment'ın göründüğü yer

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

Ancak, DialogFragment yaşam döngüsü boyunca duraklar ve devam ederse dinleyicinin etrafta olacağının garantisi yoktur. Bir Parçadaki tek garantiler setArguments ve getArguments yoluyla bir Paket üzerinden geçirilen garantilerdir.

Dinleyici olması gerekiyorsa etkinliğe başvurmanın bir yolu vardır:

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

Ama Aktivitenin olayları dinlemesini istemiyorum, bir Fragmana ihtiyacım var. Gerçekten, OnClickListener uygulayan herhangi bir Java nesnesi olabilir.

DialogFragment aracılığıyla bir AlertDialog sunan bir Parçanın somut örneğini düşünün. Evet / Hayır düğmeleri vardır. Bu düğmeye basılmasını, onu oluşturan Fragment'e nasıl geri gönderebilirim?


"Ancak DialogFragment yaşam döngüsü boyunca duraklar ve devam ederse dinleyicinin etrafta olacağının garantisi yoktur." Parça devleti onDestroy () sırasında yok edildi düşündüm? Haklı olmalısın, ama şimdi Fragment durumunu nasıl kullanacağım konusunda biraz kafam karıştı. Bahsettiğiniz sorunu nasıl yeniden üretebilirim, dinleyici etrafta değil?
Sean

Bunun OnClickListener listener = (OnClickListener) getParentFragment();yerine DialogFragment içinde neden kullanamadığınızı anlamıyorum ve ana Parçanız arayüzü ilk yaptığınız gibi uyguluyor.
kiruwka

İşte ilgisiz bir soruya bir cevap ama bunun nasıl temiz bir şekilde yapıldığını gösteriyor stackoverflow.com/questions/28620026/…
user2288580

Yanıtlar:


190

İlgili faaliyet DialogFragment'tan tamamen habersizdir.

Fragman sınıfı:

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

}

DialogFragment sınıfı:

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

100
Bence buradaki anahtar setTargetFragmentve getTargetFragment. Kullanımı onActivityResultbiraz belirsiz. Fragment arayanında kendi özel yönteminizi bildirmek ve onActivityResult öğesinin yeniden kullanılması yerine bunu kullanmak daha iyi olur. Ama bu noktada tüm anlambilim.
eternalmatt

2
yığın düzeyi değişkeni kullanılmıyor mu?
Görünen Ad

6
Bu bir konfigürasyon değişim rotasyonunda hayatta kalacak mı?
Maxrunner

3
Bunu kullandım. Notlar: rotasyon veya uykuda hayatta kalmak için yığın seviyesi gerekli değildi. OnActivityResult yerine, parçam DialogResultHandler # handleDialogResult (oluşturduğum bir arabirim) uygular. @ myCode, Niyet'e eklenen bir iletişim kutusu tarafından seçilen değeri göstermek ve sonra onActivityResult öğenizin içinde okumak için çok yardımcı olur. Niyetler yeni başlayanlar için belirsizdir.
Chris Betti

7
@eternalmatt, itirazınız tamamen mantıklı, ancak bence onActivityResult () değeri, herhangi bir Fragment üzerinde varlığının garanti edildiği, bu nedenle herhangi bir Fragment üst öğe olarak kullanılabilir. Kendi arabiriminizi oluşturursanız ve üst Parçanın onu uygulatmasını sağlarsanız, çocuk yalnızca bu arabirimi uygulayan ebeveynlerle kullanılabilir. Çocuğu daha sonra kullanmaya başlarsanız çocuğu bu arayüze bağlamak size musallat olabilir. "Yerleşik" onActivityResult () arabirimini kullanmak için ek bağlantı gerekmez, bu nedenle size biraz daha esneklik sağlar.
Dalbergia

78

TargetFragment çözümü, IllegalStateExceptionuygulama parçalandıktan ve yeniden oluşturulduktan sonra oluşturulabileceğinden iletişim kutusu parçaları için en iyi seçenek olarak görünmemektedir . Bu durumda FragmentManagerhedef parçayı bulamadı ve böyle IllegalStateExceptionbir mesajla bir alacaksınız :

"Anahtar android için artık mevcut değil: target_state: index 1"

Öyle görünüyor ki Fragment#setTargetFragment(), bir çocuk ve ebeveyn Parçası arasındaki iletişim için değil, kardeş-Parçacıkları arasındaki iletişim içindir.

Bu nedenle alternatif bir yol, ChildFragmentManageraktiviteleri kullanmak yerine ana parçayı kullanarak, bunun gibi iletişim parçaları oluşturmaktır FragmentManager:

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

Ve bir Arayüz kullanarak, onCreateyöntemde DialogFragmentüst parçayı alabilirsiniz:

@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");
    }
}

Geriye kalan tek şey, bu adımlardan sonra geri arama yönteminizi çağırmaktır.

Sorun hakkında daha fazla bilgi için şu bağlantıyı kontrol edebilirsiniz: https://code.google.com/p/android/issues/detail?id=54520


2
Bu aynı zamanda api 23'te eklenen onAttach (Bağlam bağlamı) ile de çalışır.
Santa Teclado

1
BU kabul edilen cevap olmalıdır. Şu an kabul edilen cevap buggy'dir ve fragmanların bu şekilde kullanılması amaçlanmamıştır.
NecipAllef

3
@AhmadFadli Burada sorun, parçalar arasında iletişim için doğru bağlamı (üst) elde etmektir. Diyalog parçasını bir faaliyetin alt öğesi olarak kullanacaksanız, o zaman karışıklık olmamalıdır. Aktivitenin FragmentManager'ı ve geri çağrıyı almak için getActivity () yeterlidir.
Oğuz Özcan

1
Bu kabul edilen cevap olmalı. Açıkça açıklanmış ve ayrıntılıdır, sadece insanlara atılan kod değildir.
Vince

1
@lukecross ParentFragment DialogFragment Ama childFragmentManager yeniden yapılandırılması / ekran rotasyonlar hayatta olmadığını görünüyor (aramalar () gösteriyor ki) oluşturan parçasıdır ...
iwat0qs

34

Bunu yapmak için bu basit adımları izledim.

  1. Gibi DialogFragmentCallbackInterfacebazı yöntemlerle arayüz oluşturun callBackMethod(Object data). Hangi veri aktarmak için çağırırsınız.
  2. Artık parçanıza DialogFragmentCallbackInterfacearayüz uygulayabilirsiniz .MyFragment implements DialogFragmentCallbackInterface
  3. Zamanında DialogFragmentyaratılması için çağıran parçasını ayarlamak MyFragmentoluşturulan hedef fragmanı olarak DialogFragmentkullanılması myDialogFragment.setTargetFragment(this, 0)onay setTargetFragment (Fragman fragmanı int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment();
    dialogFrag.setTargetFragment(this, 1); 
  4. Hedef parça nesneyi DialogFragmentarayarak çağırın getTargetFragment()ve DialogFragmentCallbackInterfaceşuraya yayınlayın.Şimdi parçanıza veri göndermek için bu arayüzü kullanabilirsiniz.

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

    Hepsi bu kadar! bu arayüzü parçanızda uyguladığınızdan emin olun.


4
Bu en iyi cevap olmalı. Mükemmel cevap.
Md. Sajedul Karim

Ayrıca, hem kaynak hem de hedef parçaları için aynı Parça yöneticisini kullandığınızdan emin olun, aksi takdirde getTargetFragment çalışmaz. Eğer childFragmentManager kullanıyorsanız, kaynak parça alt parça yöneticisi tarafından işlenmediği için çalışmaz. Bu 2 parçayı ebeveyn / çocuk parçasından ziyade kardeş parça olarak düşünmek en iyisidir.
Thupten

Açıkçası, 2 kardeş parçası arasında iletişim kurarken sadece hedef parça modelini kullanmak daha iyidir. Bir dinleyiciye sahip olmadığınızda, parçanın1 yanlışlıkla parça 2'den sızmasını önlersiniz. Hedef parçasını kullanırken dinleyici / geri arama kullanmayın. Yalnızca onActivityResult(request code, resultcode, intent)sonucu parça 1'e döndürmek için kullanın . Fragman1'den setTargetFragment()ve fragman2'den kullanın getTargetFragment(). Parçayı parçaya ayırmak için ebeveyn / çocuk parçasını veya parçalamaya aktiviteyi kullanırken, çocuk parçasında ebeveyn sızıntı tehlikesi olmadığından dinleyici veya geri arama kullanabilirsiniz.
Thupten

@ "Sızıntı" derken, bellek sızıntısı mı yoksa "sızıntı uygulama detayları" mı demek istediniz? Vijay'ın cevabının, dönüş değeri için onActivityResulty'i kullanmaktan daha fazla bellek sızdırdığını sanmıyorum. Her iki desen de hedef parçaya referansta bulunacaktır. Sızan uygulama ayrıntılarını kastediyorsanız, deseninin onActivityResult'dan daha iyi olduğunu düşünüyorum. Geri arama yöntemi açıktır (doğru adlandırılırsa). Geri döndüğünüz her şey yolundaysa ve İPTAL EDİLDİĞİ takdirde, ilk parça bunların ne anlama geldiğini yorumlamalıdır.
tir38

34

Belki biraz geç, ama benim gibi aynı soruya sahip diğer insanlara yardımcı olabilir.

Sen kullanabilirsiniz setTargetFragmentüzerinde Dialoggöstermeden önce ve iletişim Arayabileceğin getTargetFragmentreferans almak için.


İşte başka bir sorunun cevabı ama aynı zamanda sorunuz için de geçerlidir ve temiz bir çözümdür: stackoverflow.com/questions/28620026/…
user2288580

Bana IllegalStateException
Luke Cross

19

Diğer Fragmanlar ile Haberleşiyor rehber fragmanlar gerektiğini söylüyor ilişkili Aktivite aracılığıyla iletişim .

Genellikle bir Fragment'ın diğeriyle iletişim kurmasını istersiniz, örneğin içeriği bir kullanıcı etkinliğine göre değiştirmek için. Tüm Fragman-Fragman iletişimi ilişkili Etkinlik aracılığıyla yapılır. İki Parça asla doğrudan iletişim kurmamalıdır.


1
iç fragmanlara ne dersin, başka bir fragman içindeki bir fragman konak fragmanına nasıl haberleşmelidir
Ravi

@Ravi: Her parça, getActivity () öğesini çağırarak tüm parçalarda ortak olan etkinlikle iletişim kurmalıdır .
Edward Brey

1
@Chris: Parçaların sürekli iletişime ihtiyacı varsa, uygulanacak her uygun parça için bir arayüz tanımlayın. Daha sonra aktivitenin işi, karşı taraf parçalarına arayüz işaretçileri olan fragmanlar sağlamakla sınırlıdır. Bundan sonra, parçalar arayüzler üzerinden güvenli bir şekilde "doğrudan" iletişim kurabilir.
Edward Brey

3
Bence fragmanların kullanımı genişledikçe, doğrudan fragman iletişimi kullanmama konusundaki orijinal fikir bozulur. Örneğin, bir seyrüsefer çekmecesinde, etkinliğin hemen her alt parçası kabaca bir etkinlik olarak işlev görür. Dolayısıyla, bir diyalog parçası gibi bir parçanın aktivite yoluyla iletişim kurması, okunabilirlik / esneklik IMO'suna zarar verir. Aslında, diyalog parçasını, hem faaliyetlerle hem de parçalarla yeniden kullanılabilir bir şekilde çalışmasına izin vermek için kapsüllemenin iyi bir yolu yok gibi görünüyor.
Sam

16
Bunun eski olduğunu biliyorum, ancak başka biri buraya geldiği takdirde, bir parça DialogFragment'ın oluşturulmasını ve yönetimini belirlemek için kullanılan mantığın "sahibi" olduğunda söz konusu belgede bahsi geçen vakanın geçerli olmadığını hissediyorum. Etkinlik neden bir Diyalog oluşturulduğundan veya hangi koşullar altında reddedilmesi gerektiğinden bile emin olmadığında, parçadan etkinliğe bir grup bağlantı oluşturmak garip. Bunun yanında DialogFragment çok basittir ve yalnızca kullanıcıyı bilgilendirmek ve potansiyel olarak bir yanıt almak için vardır.
Chris

12

interfaceParça sınıfınızda bir tanımlamalı ve bu arabirimi üst etkinliğinde uygulamalısınız. Ayrıntılar burada özetlenmiştir http://developer.android.com/guide/components/fragments.html#EventCallbacks . Kod şuna benzer:

Fragment:

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

Aktivite:

public class MyActivity extends Activity implements OnArticleSelectedListener{

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

    }
    ...
}

1
Sanırım belgeleri çok hızlı gözden kaçırdın. Bu kod parçalarının her ikisi de vardır FragmentAve bir faaliyetin OnArticleSelectedListenerkendisini başlatan Parçacığı değil , bir olduğunu varsayar .
eternalmatt

2
Kötü uygulama yapmaya çalıştığın şeyi düşünürdüm. Android yönergeleri, tüm parçadan parçaya iletişimin etkinlik aracılığıyla gerçekleştirilmesini önerir ( developer.android.com/training/basics/fragments/… ). Eğer gerçekten her şeyin içinde ele alınmasını MyFragmentistiyorsanız, düzenli bir kullanıma geçmek isteyebilirsinizAlertDialog
James McCracken

1
Bence parçaların doğrudan birbirleriyle konuşmasıyla ilgili kaygı, bazı mizanpajlarda tüm parçaların yüklenemeyebileceğidir ve örnekte gösterildiği gibi parçayı değiştirmek gerekebilir. Bir parçanın bir diyalog parçasını başlatma hakkında konuşurken endişenin geçerli olduğunu düşünmüyorum.

Bunu faaliyetlerim için uyguladım. Soru: Bu çözüm, bir parça bu iletişim kutusunu başlatabilecek şekilde genişletilebilir mi?
Bill Mote

1
Bu mimari açıdan iyi bir uygulamadır ve bu nedenle kabul edilen cevap olmalıdır. OnActivityResult kullanmak spagetti mimarisine yol açar
Bruno Carrier

4

Bir dinleyiciyi bir parçaya ayarlamanın doğru yolu, takıldığında onu ayarlamaktır . Ben sorun onAttachFragment () asla çağrıldı oldu. Bazı araştırmalardan sonra getChildFragmentManager yerine getFragmentManager kullandığımı fark ettim

İşte nasıl yaparım:

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

OnAttachFragment içine ekleyin:

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

            }
        });
    }
}

3

Resmi belgelere göre:

Fragment # setTargetFragment

Bu parça için isteğe bağlı hedef. Bu, örneğin, bu parça bir başkası tarafından başlatılıyorsa ve bittiğinde birinciye bir sonuç vermek istiyorsa kullanılabilir. Burada ayarlanan hedef FragmentManager # putFragment aracılığıyla örnekler arasında korunur.

Fragment # getTargetFragment

SetTargetFragment (Fragment, int) tarafından ayarlanan hedef parçayı döndürür.

Böylece bunu yapabilirsiniz:

// 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");
        }
    }
    ...
}

açıklayabilir misin
Yılmaz

Bu durumda, "MyFragment" referansını "MyialogFragment" e geçirmemiz gerekir ve "Fragment" bunu yapma yöntemini sağlar. Resmi belgenin açıklamasını ekledim, benden daha net söylemeliyim.
SUPERYAO

2

Benzer bir sorunla karşı karşıyaydım. Bulduğum çözüm:

  1. DialogFragment'ınızda tıpkı James McCracken'in yukarıda açıkladığı gibi bir arayüz beyan edin.

  2. Arayüzü aktivitenize uygulayın (parça değil! Bu iyi bir uygulama değildir).

  3. Etkinliğinizdeki geri arama yönteminden, parçanızda yapmak istediğiniz işi yapan gerekli bir genel işlevi çağırın.

Böylece, iki adımlı bir işlem haline gelir: DialogFragment -> Etkinlik ve sonra Etkinlik -> Parça


1

Ben Fragment LiveWallFilterFragment (parça alma) Fragment DashboardLiveWall (parça fragman) sonucu alıyorum Bu gibi ...

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

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

nerede

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 parçasına benzer şekilde geri dön

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 {
                    }
                }
            }
        }
    }

1

Güncellenmiş:

Ben kullanarak senin için casting üretir benim özü koduna göre bir kütüphane yapılmış @CallbackFragmentve @Callback.

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

Ve örnek size bir parçadan başka bir parçaya geri arama gönderen örneği verir.

Eski cevap:

Bir BaseCallbackFragmentaçıklama ekledim @FragmentCallback. Şu anda uzanıyor Fragment, değiştirebilir DialogFragmentve çalışacaksınız. Uygulamaları şu sırayla kontrol eder: getTargetFragment ()> getParentFragment ()> içerik (etkinlik).

Sonra sadece uzatmanız ve parçanızdaki arayüzlerinizi beyan etmeniz ve ek açıklama vermeniz yeterlidir ve temel parça gerisini halleder. Ek açıklamada mandatory, parçanın geri aramayı uygulamaya zorlamak isteyip istemediğinizi belirlemek için bir parametre de vardır .

public class EchoFragment extends BaseCallbackFragment {

    private FragmentInteractionListener mListener;

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

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


1

Kotlin beyler başlıyoruz!

Elimizdeki sorun biz bir etkinlik oluşturdu olmasıdır Yani MainActivity, bu etkinlikte bir parçasını oluşturdu, FragmentAve şimdi üstünde bir iletişim parçasını oluşturmak istediğiniz FragmentAçağrı onu FragmentB. Sonuçları FragmentBgeriye FragmentAdönmeden nasıl geri alabiliriz MainActivity?

Not:

  1. FragmentAbir çocuk parçasıdır MainActivity. Oluşturulan parçaları yönetmek için FragmentAbunu yaparız childFragmentManager!
  2. FragmentAbir ebeveyn parçası, içeriden FragmentBerişmek FragmentAiçin FragmentBkullanacağız parenFragment.

Bunu söyledikten sonra, içeride 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")
    }
}

İşte diyalog parçası 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")
    }
}

İkisini birbirine bağlayan arayüz.

interface UpdateNameListener {
    fun onSave(name: String)
}

Bu kadar.


1
Bu dokümanı takip ettim: developer.android.com/guide/topics/ui/dialogs ve işe yaramadı. Çok teşekkür ederim. Umarım bu üst öğe her zaman beklendiği gibi çalışır :)
UmutTekin

1
dinleyiciyi onDetach içinde null olarak ayarlamayı unutmayın :)
BekaBot

@BekaBot Yorum için teşekkürler. Biraz araştırma yaptım ve dinleyicileri kapatmanın gerekli olmadığı anlaşılıyor. stackoverflow.com/a/37031951/10030693
Ssenyonjo

0

Bunu RxAndroid ile zarif bir şekilde çözdüm. DialogFragment öğesinin yapıcısında bir gözlemci alın ve gözlemlenebilir hale getirin ve geri arama çağrıldığında değeri itin. Daha sonra, Parçanızda Observer'ın bir iç sınıfını oluşturun, bir örnek oluşturun ve bunu DialogFragment'ın yapıcısına iletin. Bellek sızıntılarını önlemek için WeakReference'ı gözlemcide kullandım. İşte kod:

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() {

    }
}

Kaynak kodu burada görebilirsiniz: https://github.com/andresuarezz26/carpoolingapp


1
Android hakkında komik olan şey, yaşam döngüsü adı verilen bir şey olmasıdır. Temel parçanın veya iletişim parçasının, yaşam döngüsü olayları üzerindeki durumunu (ve bağlantılarını) koruyabilmesi gerekir. Geri aramalar veya gözlemciler serileştirilemez ve bu nedenle burada aynı sorun vardır.
GDanger
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.