DialogFragment'tan sonuç al


232

DialogFragments kullanıyorum bir dizi şey için : listeden öğe seçme, metin girme.

Bir değeri (listeden bir dize veya bir öğe) çağıran etkinliğe / parçaya geri döndürmenin en iyi yolu nedir?

Şu anda çağıran aktivite uygulamak DismissListenerve DialogFragment etkinliğe bir referans veriyorum. İletişim kutusu daha sonra OnDimissetkinlikteki yöntemi çağırır ve etkinlik sonucu DialogFragment nesnesinden alır. Çok dağınık ve DialogFragment etkinliğe olan referansı kaybettiği için yapılandırma değişikliği (yön değiştirme) üzerinde çalışmaz.

Herhangi bir yardım için teşekkürler.


10
DialogFragments hala sadece fragmanlardır. Yaklaşımınız aslında parçaların ana aktiviteden bahsetmek için kullanması için önerilen yoldur. developer.android.com/guide/topics/fundamentals/…
kodlayıcı

1
Bunun için teşekkürler. Çok yakındım (dediğin gibi). Bağlantılı belgenin bana yardımcı olduğu bit onAttach () kullanmak ve etkinliği bir dinleyiciye yayınlamaktı.
James Cross

2
@codinguser, @Styx - "DialogFragment faaliyeti bir başvuru veren" - bu ayrıntı, biraz risklidir hem Activityve DialogFragmentyeniden olabilir. ActivityGeçirilen öğeyi kullanmak onAttach(Activity activity)uygun ve önerilen yoldur.
sstn

Yanıtlar:


246

myDialogFragment.setTargetFragment(this, MY_REQUEST_CODE)İletişim kutusunu gösterdiğiniz yerden kullanın ve ardından iletişim kutunuz bittiğinde, onu arayabilir getTargetFragment().onActivityResult(getTargetRequestCode(), ...)ve uygulayabilirsiniz.onActivityResult() içeren parçayı .

onActivityResult()Özellikle faaliyetleri içermediği için kötüye kullanılması gibi görünüyor . Ama resmi google halkı tarafından ve hatta api demolarında bile önerildiğini gördüm. Bence bunun g/setTargetFragment()için eklendi.


2
setTargetFragment, requestcode'un onActivityResult içinde kullanımından bahsediyor, bu yüzden bu yaklaşımı kullanmakta sorun yok.
Giorgi

87
Hedef bir etkinlikse ne olur?
Fernando Gallego

9
Hedef etkinlik ise, "void onActivityResult2 (int requestCode, int sonuçKodu, Niyet verisi)" gibi bir yöntemle arabirim bildirir ve bir Etkinlik tarafından uygularım. DialogFragment içinde sadece getActivity ve bu arayüzü kontrol edin ve uygun şekilde arayın.
Ruslan Yanchyshyn

4
İyi bir çözüm değil. Diyalog parçası durumunu kaydettikten ve geri yükledikten sonra çalışmaz. LocalBroadcastManager bu durumda en iyi çözümdür.
Nik

4
@Nik Bu doğru değil. Bu en iyi çözüm. Durumu kaydederken ve geri yüklerken sorun yok. Eğer herhangi bir problem yaşarsanız yanlış parça yöneticisini kullandınız. Hedef parça / arayan diyalog penceresini göstermek için getChildFragmentManager () yöntemini kullanır.
İnanılmaz Ocak

140

Gördüğünüz gibi burada bunu yapmak için çok basit bir yolu yoktur.

Sizin DialogFragmentgibi bir arayüz dinleyici ekleyin:

public interface EditNameDialogListener {
    void onFinishEditDialog(String inputText);
}

Ardından, bu dinleyiciye bir referans ekleyin:

private EditNameDialogListener listener;

Bu, dinleyici yöntem (ler) ini "etkinleştirmek" ve ayrıca ana Etkinlik / Parçanın bu arabirimi uygulayıp uygulamadığını kontrol etmek için kullanılır (aşağıya bakın).

In Activity/ FragmentActivity/ Fragment"sözde" olduğunuDialogFragment sadece bu arabirimini uygular.

Her DialogFragmentşeyinizde, reddetmek DialogFragmentve sonucu döndürmek istediğiniz noktaya eklemeniz gerekir :

listener.onFinishEditDialog(mEditText.getText().toString());
this.dismiss();

Çağrıya mEditText.getText().toString()ne geri dönülecekActivity .

Başka bir şey döndürmek istiyorsanız, dinleyicinin aldığı argümanları değiştirmeniz yeterlidir.

Son olarak, arayüzün gerçekten üst etkinlik / parça tarafından uygulanıp uygulanmadığını kontrol etmelisiniz:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Verify that the host activity implements the callback interface
    try {
        // Instantiate the EditNameDialogListener so we can send events to the host
        listener = (EditNameDialogListener) context;
    } catch (ClassCastException e) {
        // The activity doesn't implement the interface, throw exception
        throw new ClassCastException(context.toString()
                + " must implement EditNameDialogListener");
    }
}

Bu teknik çok esnektir ve henüz iletişim kutusunu kapatmak istemeseniz bile sonuçla geri aramaya izin verir.


13
Bu Activity've FragmentActivity' ile harika çalışıyor , ancak arayan a Fragment?
Brais Gabin

1
Seni tam olarak anladığımdan emin değilim. Ancak arayan bir ise aynı şekilde çalışır Fragment.
Assaf Gamliel

2
Arayan bir numaraysa, Fragmentbirkaç şey yapabilirsiniz: 1. Parçayı referans olarak iletin (Hafıza sızıntısına neden olabileceğiniz için iyi bir fikir olmayabilir). 2. FragmentManagerÇağrı ve findFragmentByIdya findFragmentByTagda etkinlik var parçaları alacak. Umarım yardımcı olmuştur. İyi günler!
Assaf Gamliel

5
Bu yaklaşımla ilgili sorun, parçanın nesneyi tutmada çok iyi olmamasıdır, çünkü yeniden yönlendirilmeleri amaçlanmaktadır, örneğin yönlendirmeyi değiştirmeye çalışın, işletim sistemi parçayı yeniden oluşturacak, ancak dinleyicinin örneği artık mevcut olmayacak
Necronet

5
@LOG_TAG @ Timmmm'in cevabına bakın. setTargetFragment()ve getTargetFragment()sihir.
Brais Gabin

48

Bir DialogFragment'tan sonuç almanın çok daha basit bir yolu var.

İlk olarak, Etkinlik, Fragman veya FragmentActivity'de aşağıdaki bilgileri eklemeniz gerekir:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Stuff to do, dependent on requestCode and resultCode
    if(requestCode == 1) { // 1 is an arbitrary number, can be any int
         // This is the return result of your DialogFragment
         if(resultCode == 1) { // 1 is an arbitrary number, can be any int
              // Now do what you need to do after the dialog dismisses.
         }
     }
}

requestCodeTemelde denilen DialogFragment için int etikettir, ben bunun nasıl gerçekleştirileceği bir saniyede bu eserler. SonuçKodu, DialogFragment öğesinden geri gönderdiğiniz kod olup, bekleyen bekleme Aktivitenizi, Fragmanınızı veya FragmentActivity'yi neler olduğunu söyler.

Bir sonraki kod parçası DialogFragment'a yapılan çağrıdır. Bir örnek burada:

DialogFragment dialogFrag = new MyDialogFragment();
// This is the requestCode that you are sending.
dialogFrag.setTargetFragment(this, 1);     
// This is the tag, "dialog" being sent.
dialogFrag.show(getFragmentManager(), "dialog");

Bu üç satırla DialogFragment'ınızı bildiriyorsunuz, Dialog reddedildikten sonra onActivityResult (...) olarak adlandırılacak bir istekKodu ayarlayıp iletişim kutusunu gösteriyorsunuz. Bu kadar basit.

Şimdi, DialogFragment'ınızda dismiss(), onActivityResult () öğesine bir sonuçKodu geri göndermek için doğrudan bir satır eklemeniz yeterlidir.

getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, getActivity().getIntent());
dismiss();

Bu kadar. Not, sonuçKodu, bu durumda int resultCodeayarladığım gibi tanımlanır resultCode = 1;.

İşte bu, şimdi DialogFragment'ınızın sonucunu çağıran Aktivite, Fragman veya FragmentActivity'ye geri gönderebilirsiniz.

Ayrıca, bu bilgiler daha önce yayınlanmış gibi görünüyor, ancak verilen yeterli bir örnek yoktu, bu yüzden daha fazla ayrıntı vereceğimi düşündüm.

EDIT 06.24.2016 Yukarıdaki yanıltıcı kod için özür dilerim. Ama kesinlikle sonucu satır olarak gören aktiviteye geri alamazsınız:

dialogFrag.setTargetFragment(this, 1);

bir hedef belirler Fragmentve belirlemez Activity. Yani bunu yapmak için bir uygulamak gerekir InterfaceCommunicator.

Setinizde DialogFragmentglobal bir değişken

public InterfaceCommunicator interfaceCommunicator;

İşleyecek genel bir işlev oluşturun

public interface InterfaceCommunicator {
    void sendRequestCode(int code);
}

Eğer kod geri göndermeye hazır olduğunuzda Sonra Activityzaman DialogFragmentçalışan yapılır, sadece sizden önce satırı ekleyin dismiss();SİZİN DialogFragment:

interfaceCommunicator.sendRequestCode(1); // the parameter is any int code you choose.

Etkinliğinizde şimdi iki şey yapmanız gerekiyor, ilki artık geçerli olmayan bir kod satırını kaldırmaktır:

dialogFrag.setTargetFragment(this, 1);  

Sonra arayüzü uygulayın ve hepsi bitti. Bunu, implementssınıfınızın en üst kısmındaki maddeye aşağıdaki satırı ekleyerek yapabilirsiniz :

public class MyClass Activity implements MyDialogFragment.InterfaceCommunicator

Ve sonra @Overrideaktivitedeki fonksiyon,

@Override
public void sendRequestCode(int code) {
    // your code here
}

Bu arayüz yöntemini tıpkı yöntem gibi kullanırsınız onActivityResult(). Arayüz yöntemi hariç DialogFragmentsve diğeri içindir Fragments.


4
Hedef, Etkinlik ise bu yaklaşım çalışmaz çünkü korumalı erişim düzeyi nedeniyle onActivityResult'u (DialogFragment'ınızdan) çağıramazsınız.
Ruslan Yanchyshyn

2
Bu doğru değil. Bu kodu projelerimde kullanıyorum. Ben de buradan çektim ve gayet iyi çalışıyor. Bu korumalı erişim düzeyi sorununu yaşıyorsanız, herhangi bir yöntem ve sınıf için erişim düzeyinizi gerekirse korumalıdan özel veya herkese açık olarak değiştirebileceğinizi lütfen unutmayın.
Brandon

6
Merhaba, dialogFrag.setTargetFragment(this, 1)bir Aktiviteden çağırabileceğinizi söylüyorsunuz , ancak bu yöntem ilk argüman olarak bir Parça alıyor, bu nedenle bu dökülemedi. Haklı mıyım?
MondKin

1
Etkinlik konularını açıklamak için birkaç kişi için bazı yanıtlar göndereceğim.
Brandon

1
@Swift @lcompare, muhtemelen DialogFragment'ınızda onAttach'ı (Bağlam bağlamı) geçersiz kılmanız gerekir. Gibi:@Override public void onAttach(Context context) { super.onAttach(context); yourInterface = (YourInterface) context; }
lidkxx

20

Peki çok geç cevap olabilir ama işte ben sonuçları geri almak için ne yaptı DialogFragment. @ brandon'un cevabına çok benzer. Burada DialogFragmentbir parçadan arıyorum , sadece iletişiminizi çağırdığınız bu kodu yerleştirin.

FragmentManager fragmentManager = getFragmentManager();
            categoryDialog.setTargetFragment(this,1);
            categoryDialog.show(fragmentManager, "dialog");

nerede categoryDialogbenim DialogFragmentaramak istiyorum ve bundan sonra dialogfragmentverilerinizi niyetinde ayarladığınız bu kodu yerine uygulamanızda . Değerini resultCode1 olarak ayarlayabilirsiniz veya Defined sistemini kullanabilirsiniz.

            Intent intent = new Intent();
            intent.putExtra("listdata", stringData);
            getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
            getDialog().dismiss();

şimdi çağıran parçaya geri dönme ve bu yöntemi uygulama zamanı. veri geçerliliğini kontrol edin veya isterseniz resultCodeve durumunda başarılı olun requestCode.

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);        
        //do what ever you want here, and get the result from intent like below
        String myData = data.getStringExtra("listdata");
Toast.makeText(getActivity(),data.getStringExtra("listdata"),Toast.LENGTH_SHORT).show();
    }

10

Bir Parçanın Faaliyetine kadar iletişim kurmasına izin vermek için farklı yaklaşım :

1) Parçada genel bir arabirim tanımlayın ve bunun için bir değişken oluşturun

public OnFragmentInteractionListener mCallback;

public interface OnFragmentInteractionListener {
    void onFragmentInteraction(int id);
}

2) Aktiviteyi parçadaki mCallback değişkenine döküm

try {
    mCallback = (OnFragmentInteractionListener) getActivity();
} catch (Exception e) {
    Log.d(TAG, e.getMessage());
}

3) Dinleyiciyi aktivitenize uygulayın

public class MainActivity extends AppCompatActivity implements DFragment.OnFragmentInteractionListener  {
     //your code here
}

4) Etkinlikteki OnFragmentInteraction değerini geçersiz kılın

@Override
public void onFragmentInteraction(int id) {
    Log.d(TAG, "received from fragment: " + id);
}

Daha fazla bilgi için: https://developer.android.com/training/basics/fragments/communicating.html


Çok iyi özetlediğiniz için teşekkürler. Diğerleri için sadece bir not, Android Devs öğreticisi public void onAttachparçanın geçersiz kılınmasını ve orada döküm gerçekleştirilmesini
önerir

8

Bulduğum kolay bir yol şuydu: Bu iletişim kutusunun uygulanması

  CallingActivity callingActivity = (CallingActivity) getActivity();
  callingActivity.onUserSelectValue("insert selected value here");
  dismiss();

Ve sonra Diyalog Parçası olarak adlandırılan aktivitede uygun işlevi şu şekilde oluşturun:

 public void onUserSelectValue(String selectedValue) {

        // TODO add your implementation.
      Toast.makeText(getBaseContext(), ""+ selectedValue, Toast.LENGTH_LONG).show();
    }

Tost işe yaradığını göstermek içindir. Benim için çalıştı.


Bunu yapmak için doğru bir yol olup olmadığından emin değilim, ama kesinlikle işe yarıyor :)
soshial

InterfaceBeton sınıflarıyla sert bağlantı yerine daha iyi kullanım .
waqaslam

6

Çok yerel yayınları kullanarak kimse önerdi olduğunu görmek için şaşırdım DialogFragmentiçin Activityiletişim! Diğer önerilerden çok daha basit ve daha temiz olduğunu düşünüyorum. Esasen, Activityyayınları dinlemek için kayıt olur ve yerel yayınları DialogFragmentörneklerinizden gönderirsiniz . Basit. Hepsini ayarlama hakkında adım adım kılavuz için buraya bakın .


2
Bu çözümü beğendim, bu Android'de iyi veya en iyi uygulama olarak kabul ediliyor mu?
Benjamin Scharbau

1
Eğiticiyi gerçekten beğendim, gönderdiğiniz için teşekkür ederiz. Her iki yöntemi gerçekleştirmek için ne çalıştığınıza bağlı olarak diğerinden daha yararlı olabilir eklemek istiyorum. İletişim kutusundan etkinliğe geri gönderilen birden çok giriş / sonuç varsa yerel yayın yolunu öneririm. Çıktınız çok temel / basitse onActivityResult yolunu kullanmanızı öneririm. Bu nedenle, en iyi uygulama sorusunu cevaplamak, neyi başarmaya çalıştığınıza bağlıdır!
Brandon

1
@AdilHussain Haklısın. İnsanların kendi Faaliyetleri içinde Parçalar kullandığını varsaydım. Bir Fragment ve DialogFragment ile iletişim kuruyorsanız setTargetFragment seçeneği mükemmeldir. Ancak DialogFragment'ı çağıran bir Etkinlik olduğunda Broadcast yöntemini kullanmanız gerekir.
Brandon

3
Foo aşkı için Yayınları kullanmayın !! Uygulamanızı bir dizi güvenlik sorununa açar. Ayrıca kötüye yayınlar üzerinde çalışmak zorunda kötü android uygulama bulmak. Kodu tamamen kullanılamaz hale getirmenin daha iyi bir yolunu düşünebilir misiniz? Şimdi bir CLEAR kod satırı yerine yayın alıcıları kök salmak zorunda mı? Açıkça söylemek gerekirse, bu bağlamda değil, yayınlar için kullanımları vardır! ASLA bu bağlamda! Sadece özensiz. Yerel veya değil. Geri aramalar tek ihtiyacınız.
StarWind0

1
Guava EventBus'un yanı sıra başka bir seçenek de GreenRobot EventBus . Guava EventBus'u kullanmadım ama GreenRobot EventBus'u kullandım ve onunla iyi bir deneyim yaşadım. Güzel ve kullanımı basit. Bir Android uygulamasının GreenRobot EventBus'u kullanması için nasıl mimari yapılacağına dair küçük bir örnek için buraya bakın .
Adil Hussain

3

Veya ViewModel'i burada gösterildiği gibi paylaşın:

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing_data_between_fragments


2

Benim durumumda bir targetFragment'e argümanlar iletmem gerekiyordu. Ama "Parça zaten aktif" istisnası var. Bu yüzden DiaFomFragment içinde parentFragment uygulanan bir arabirim ilan etti. ParentFragment bir DialogFragment başlattığında, kendisini TargetFragment olarak ayarlar. Sonra DialogFragment içinde aradım

 ((Interface)getTargetFragment()).onSomething(selectedListPosition);

2

Kotlin bölgesinde

    // My DialogFragment
class FiltroDialogFragment : DialogFragment(), View.OnClickListener {
    
    var listener: InterfaceCommunicator? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        listener = context as InterfaceCommunicator
    }

    interface InterfaceCommunicator {
        fun sendRequest(value: String)
    }   

    override fun onClick(v: View) {
        when (v.id) {
            R.id.buttonOk -> {    
        //You can change value             
                listener?.sendRequest('send data')
                dismiss()
            }
            
        }
    }
}

// Benim faaliyetim

class MyActivity: AppCompatActivity(),FiltroDialogFragment.InterfaceCommunicator {

    override fun sendRequest(value: String) {
    // :)
    Toast.makeText(this, value, Toast.LENGTH_LONG).show()
    }
}
 

Umarım hizmet eder, geliştirebilirseniz lütfen düzenleyin. İngilizcem çok iyi değil


Benim durumumda, iletişim bir çözüm değil etkinlikten oluşturulur, bu nedenle bu çözüm çalışmaz. Ama koyduğun suratı seviyorum :)
Ssenyonjo

0

bağımsız değişkenler göndermek ve sonucu ikinci parçadan almak istiyorsanız, bu görevi gerçekleştirmek için Fragment.setArguments komutunu kullanabilirsiniz.

static class FirstFragment extends Fragment {
    final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 101: // receive the result from SecondFragment
                Object result = msg.obj;
                // do something according to the result
                break;
            }
        };
    };

    void onStartSecondFragments() {
        Message msg = Message.obtain(mUIHandler, 101, 102, 103, new Object()); // replace Object with a Parcelable if you want to across Save/Restore
                                                                               // instance
        putParcelable(new SecondFragment(), msg).show(getFragmentManager().beginTransaction(), null);
    }
}

static class SecondFragment extends DialogFragment {
    Message mMsg; // arguments from the caller/FirstFragment

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);
        mMsg = getParcelable(this);
    }

    void onClickOK() {
        mMsg.obj = new Object(); // send the result to the caller/FirstFragment
        mMsg.sendToTarget();
    }
}

static <T extends Fragment> T putParcelable(T f, Parcelable arg) {
    if (f.getArguments() == null) {
        f.setArguments(new Bundle());
    }
    f.getArguments().putParcelable("extra_args", arg);
    return f;
}
static <T extends Parcelable> T getParcelable(Fragment f) {
    return f.getArguments().getParcelable("extra_args");
}

-3

Sadece seçeneklerden biri olarak (henüz kimse bahsetmediği için) - Otto gibi bir olay otobüsü kullanabilirsiniz. Böylece iletişim kutusunda şunları yapabilirsiniz:

bus.post(new AnswerAvailableEvent(42));

Arayandan (Etkinlik veya Parça) abone olmasını sağlayın:

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
   // TODO: React to the event somehow!
}
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.