Frage: Wie erstellt man einen Callback von einem DialogFragment zu einem anderen Fragment? In meinem Fall sollte die betreffende Aktivität das DialogFragment nicht kennen.
Betrachten Sie, ich habe
public class MyFragment extends Fragment implements OnClickListener
Dann irgendwann würde ich könnte machen
DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
dialogFrag.show(getFragmentManager, null);
Wo sieht meinDialogFragment aus?
protected OnClickListener listener;
public static DialogFragment newInstance(OnClickListener listener) {
DialogFragment fragment = new DialogFragment();
fragment.listener = listener;
return fragment;
}
Es kann jedoch nicht garantiert werden, dass der Listener in der Nähe ist, wenn DialogFragment angehalten wird und seinen Lebenszyklus fortsetzt. Die einzigen Garantien in einem Fragment sind die über setArguments und getArguments über ein Bundle übergebenen.
Es gibt eine Möglichkeit, auf die Aktivität zu verweisen, wenn es der Listener sein sollte:
public Dialog onCreateDialog(Bundle bundle) {
OnClickListener listener = (OnClickListener) getActivity();
....
return new AlertDialog.Builder(getActivity())
........
.setAdapter(adapter, listener)
.create();
}
Aber ich möchte nicht, dass die Aktivität auf Ereignisse hört, ich brauche ein Fragment. Wirklich, es könnte jedes Java-Objekt sein, das OnClickListener implementiert.
Betrachten Sie das konkrete Beispiel eines Fragments, das einen AlertDialog über DialogFragment darstellt. Es hat Ja/Nein-Tasten. Wie kann ich diese Tastendrücke an das Fragment zurückschicken, von dem sie erstellt wurden?
Die betreffende Aktivität kennt das DialogFragment nicht.
Fragmentklasse:
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-Klasse:
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();
}
}
Die TargetFragment-Lösung scheint nicht die beste Option für Dialogfragmente zu sein, da IllegalStateException
erstellt werden kann, nachdem die Anwendung zerstört und erneut erstellt wurde. In diesem Fall konnte FragmentManager
das Zielfragment nicht finden und Sie erhalten eine IllegalStateException
mit einer Nachricht wie folgt:
Msgstr "Fragment existiert nicht mehr für Android: target_state: index 1"
Es scheint, als wäre Fragment#setTargetFragment()
nicht für die Kommunikation zwischen einem untergeordneten und einem übergeordneten Fragment gedacht, sondern eher für die Kommunikation zwischen Geschwisterfragmenten.
Alternativ können Sie also Dialogfragmente wie folgt erstellen, indem Sie die ChildFragmentManager
des übergeordneten Fragments verwenden, anstatt die Aktivitäten FragmentManager
zu verwenden:
dialogFragment.show(ParentFragment.this.getChildFragmentManager(), "dialog_fragment");
Durch die Verwendung einer Schnittstelle können Sie in der onCreate
-Methode der DialogFragment
das übergeordnete Fragment erhalten:
@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");
}
}
Jetzt müssen Sie nur noch Ihre Callback-Methode aufrufen.
Weitere Informationen zu diesem Problem finden Sie unter folgendem Link: https://code.google.com/p/Android/issues/detail?id=54520
Vielleicht etwas spät, aber vielleicht anderen Leuten mit der gleichen Frage wie ich helfen.
Sie können setTargetFragment
für Dialog
verwenden, bevor Sie sie anzeigen. Im Dialogfeld können Sie getTargetFragment
aufrufen, um die Referenz zu erhalten.
Ich bin diesen einfachen Schritten gefolgt, um dieses Zeug zu machen.
DialogFragmentCallbackInterface
mit einer Methode wie callBackMethod(Object data)
. Welches würden Sie anrufen, um Daten zu übergeben.DialogFragmentCallbackInterface
-Schnittstelle in Ihr Fragment wie MyFragment implements DialogFragmentCallbackInterface
implementieren. Zum Zeitpunkt der Erstellung von DialogFragment
setzen Sie Ihr aufrufendes Fragment MyFragment
als Zielfragment, das DialogFragment
erstellt hat. myDialogFragment.setTargetFragment(this, 0)
check setTargetFragment (Fragmentfragment, int requestCode)
MyDialogFragment dialogFrag = new MyDialogFragment();
dialogFrag.setTargetFragment(this, 1);
Holen Sie Ihr Zielfragmentobjekt in Ihre DialogFragment
, indem Sie getTargetFragment()
aufrufen und es in DialogFragmentCallbackInterface
umwandeln. Jetzt können Sie diese Schnittstelle verwenden, um Daten an Ihr Fragment zu senden.
DialogFragmentCallbackInterface callback =
(DialogFragmentCallbackInterface) getTargetFragment();
callback.callBackMethod(Object data);
Das ist alles erledigt! Stellen Sie nur sicher, dass Sie diese Schnittstelle in Ihrem Fragment implementiert haben.
Der Mit anderen Fragmenten kommunizieren Guide sagt, dass die Fragmente durch die zugehörige Aktivität kommunizieren sollen.
Oft möchten Sie, dass ein Fragment mit einem anderen kommuniziert, z. B. Beispiel, um den Inhalt basierend auf einem Benutzerereignis zu ändern. Alles Die Fragment-zu-Fragment-Kommunikation erfolgt über das zugehörige Aktivität. Zwei Fragmente sollten niemals direkt kommunizieren.
Sie sollten eine interface
in Ihrer Fragmentklasse definieren und diese Schnittstelle in ihrer übergeordneten Aktivität implementieren. Die Details finden Sie hier http://developer.Android.com/guide/components/fragments.html#EventCallbacks . Der Code würde ähnlich aussehen:
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");
}
}
}
Aktivität:
public class MyActivity extends Activity implements OnArticleSelectedListener{
...
@Override
public void onArticleSelected(Uri articleUri){
}
...
}
Ich hatte ein ähnliches Problem. Die Lösung, die ich herausfand, war:
Deklarieren Sie eine Schnittstelle in Ihrem DialogFragment, wie es James McCracken oben erklärt hat.
Implementieren Sie die Schnittstelle in Ihrer Aktivität (nicht fragmentieren! Das ist keine gute Praxis).
Rufen Sie in der Callback-Methode in Ihrer Aktivität eine erforderliche öffentliche Funktion in Ihrem Fragment auf, die die gewünschte Aufgabe ausführt.
Es wird also ein zweistufiger Prozess: DialogFragment -> Aktivität und dann Aktivität -> Fragment
Der richtige Weg, um einen Listener auf ein Fragment zu setzen, besteht darin, dass es gesetzt wird, wenn es attach ist. Das Problem, das ich hatte, war, dass onAttachFragment () nie aufgerufen wurde. Nach einiger Untersuchung stellte ich fest, dass ich getFragmentManager anstelle von getChildFragmentManager verwendet hatte.
So mache ich es:
MyDialogFragment dialogFragment = MyDialogFragment.newInstance("title", "body");
dialogFragment.show(getChildFragmentManager(), "SOME_DIALOG");
Fügen Sie es in onAttachFragment hinzu:
@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() {
}
});
}
}
Aktualisierte:
Ich habe eine Bibliothek erstellt, die auf meinem Gist-Code basiert und diese Castings für Sie generiert, indem Sie @CallbackFragment
und @Callback
verwenden.
https://github.com/zeroarst/callbackfragment .
Und das Beispiel gibt Ihnen das Beispiel, in dem ein Callback von einem Fragment an ein anderes Fragment gesendet wird.
Alte Antwort:
Ich habe eine BaseCallbackFragment
und eine Annotation @FragmentCallback
gemacht. Es erweitert derzeit Fragment
, Sie können es in DialogFragment
ändern und funktionieren. Es prüft die Implementierungen in der folgenden Reihenfolge: getTargetFragment ()> getParentFragment ()> context (activity).
Dann müssen Sie es nur erweitern und Ihre Schnittstellen in Ihrem Fragment deklarieren und ihm die Anmerkung geben, und das Basisfragment erledigt den Rest. Die Annotation hat auch einen Parameter mandatory
, mit dem Sie bestimmen können, ob Sie das Fragment zur Implementierung des Callbacks zwingen möchten.
public class EchoFragment extends BaseCallbackFragment {
private FragmentInteractionListener mListener;
@FragmentCallback
public interface FragmentInteractionListener {
void onEcho(EchoFragment fragment, String echo);
}
}
https://Gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a93
Ich bekomme ein Ergebnis für Fragment DashboardLiveWall (aufrufendes Fragment) von Fragment LiveWallFilterFragment (empfangendes Fragment).
LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,"");
getActivity().getSupportFragmentManager().beginTransaction().
add(R.id.frame_container, filterFragment).addToBackStack("").commit();
woher
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 zurück zum aufrufenden Fragment wie
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 {
}
}
}
}
}
Laut der offiziellen Dokumentation:
Optionales Ziel für dieses Fragment. Dies kann zum Beispiel verwendet werden, wenn dieses Fragment von einem anderen Fragment gestartet wird und wenn dies erledigt ist, möchte es dem ersten ein Ergebnis zurückgeben. Das hier festgelegte Ziel wird über FragmentManager # putFragment instanzübergreifend beibehalten.
Gibt das mit setTargetFragment (Fragment, int) gesetzte Zielfragment zurück.
So können Sie dies tun:
// 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");
}
}
...
}
Ich habe das mit RxAndroid auf elegante Weise gelöst. Empfangen Sie einen Beobachter im Konstruktor von DialogFragment und setzen Sie ihn auf beobachtbare Werte, und drücken Sie den Wert, wenn der Callback aufgerufen wird. Erstellen Sie dann in Ihrem Fragment eine innere Klasse des Observer, erstellen Sie eine Instanz und übergeben Sie diese im Konstruktor von DialogFragment. Ich habe WeakReference im Observer verwendet, um Speicherlecks zu vermeiden. Hier ist der Code:
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() {
}
}
Den Quellcode können Sie hier sehen: https://github.com/andresuarezz26/carpoolingapp