从DialogFragmentcallback到片段

问题:如何从DialogFragment创build一个callback到另一个Fragment。 在我的情况下,涉及的Activity应该完全不知道DialogFragment。

考虑我有

public class MyFragment extends Fragment implements OnClickListener 

然后在某个时候我可以

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

MyDialogFragment的外观

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

但是,如果DialogFragment在其生命周期中暂停并恢复,则不能保证侦听器将在其周围。 碎片中的唯一保证是通过setArguments和getArguments通过Bundle传入的。

有一种方法可以引用活动,如果它应该是监听者:

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

但是我不希望活动监听事件,我需要一个片段。 真的,它可以是任何实现OnClickListener的Java对象。

考虑通过DialogFragment呈现AlertDialog的片段的具体例子。 它有是/否button。 我怎样才能发送这些button回到创build它的片段?

涉及的活动完全不知道DialogFragment。

片段类:

 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类:

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

TargetFragment解决scheme似乎不是对话框片段的最佳select,因为在应用程序被破坏并重新创build后,它可能会创buildIllegalStateException 。 在这种情况下, FragmentManager找不到目标片段,您将得到一个IllegalStateException ,并显示如下消息:

“关键的android:target_state:index 1”片段不再存在

看起来像Fragment#setTargetFragment()不是用于子和父Fragment之间的通信,而是用于兄弟片段之间的通信。

所以另一种方法是通过使用父片段的ChildFragmentManager创build对话框片段,而不是使用活动FragmentManager

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

通过使用接口,在DialogFragment onCreate方法中,您可以获得父代片段:

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

剩下的只是在这些步骤之后调用callback方法。

有关此问题的更多信息,请查看以下链接: https : //code.google.com/p/android/issues/detail?id = 54520

也许有点晚了,但可能像我一样帮助其他人提出同样的问题。

你可以在显示之前在Dialog使用setTargetFragment ,在对话框中你可以调用getTargetFragment来获得引用。

“ 与其他片段进行交stream”指导说,片段应通过相关活动进行交stream

通常你会想要一个片段与另一个片段进行通信,例如根据用户事件来改变内容。 所有片段到片段的通信都是通过关联的活动完成的。 两个碎片不应该直接沟通。

我遵循这个简单的步骤来做这个东西。

  1. 使用callBackMethod(Object data)等方法创build像DialogFragmentCallbackInterface这样的接口。 你会打电话来传递数据。
  2. 现在你可以在你的片段中实现DialogFragmentCallbackInterface接口,就像MyFragment implements DialogFragmentCallbackInterface
  3. 在创buildDialogFragment时,将调用片段MyFragment设置为创buildDialogFragment目标片段,使用myDialogFragment.setTargetFragment(this, 0)检查setTargetFragment(片段片段,int requestCode)

     MyDialogFragment dialogFrag = new MyDialogFragment(); dialogFrag.setTargetFragment(this, 1); 
  4. 通过调用getTargetFragment()并将其转换为DialogFragment ,将目标片段对象放到DialogFragmentCallbackInterface 。现在可以使用此接口将数据发送到片段。

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

    这一切都完成了! 只要确保你已经在你的片段中实现了这个接口。

您应该在您的片段类中定义一个interface ,并在其父活动中实现该接口。 细节在这里概述http://developer.android.com/guide/components/fragments.html#EventCallbacks 。 代码看起来类似于:

分段:

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

活动:

 public class MyActivity extends Activity implements OnArticleSelectedListener{ ... @Override public void onArticleSelected(Uri articleUri){ } ... } 

我正面临类似的问题。 我发现的解决scheme是:

  1. 像James McCracken在上面解释的那样在DialogFragment中声明一个接口。

  2. 在你的活动中实现接口(不是片段!这不是一个好的做法)。

  3. 从你的活动中的callback方法,在你的片段中调用一个你需要做的工作的公共函数。

因此,它变成了两个步骤:DialogFragment – > Activity,然后Activity – > Fragment

我得到的结果碎片DashboardLiveWall(调用片段)从片段LiveWallFilterFragment(接收片段)像这样…

  LiveWallFilterFragment filterFragment = LiveWallFilterFragment.newInstance(DashboardLiveWall.this ,""); getActivity().getSupportFragmentManager().beginTransaction(). add(R.id.frame_container, filterFragment).addToBackStack("").commit(); 

哪里

 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返回到调用片段

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

更新:

我使用@CallbackFragment@Callback了一个基于我的主要代码的库,为您生成这些代码。

https://github.com/zeroarst/callbackfragment

这个例子给你举个例子,从一个片段向另一个片段发送一个callback。

老答案:

我做了一个BaseCallbackFragment和注解@FragmentCallback 。 它目前扩展Fragment ,你可以将其更改为DialogFragment并将工作。 它使用以下顺序检查实现:getTargetFragment()> getParentFragment()>上下文(活动)。

然后你只需要扩展它,并在你的片段中声明你的接口,并给它注释,基本片段将完成剩下的工作。 注释还有一个mandatory参数,用于确定是否要强制片段实现callback。

 public class EchoFragment extends BaseCallbackFragment { private FragmentInteractionListener mListener; @FragmentCallback public interface FragmentInteractionListener { void onEcho(EchoFragment fragment, String echo); } } 

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

我用RxAndroid以优雅的方式解决了这个问题。 在DialogFragment的构造函数中接收一个观察者,并且在调用callback时调用observable和push值。 然后,在你的Fragment中创build一个Observer的内部类,创build一个实例并将其传递给DialogFragment的构造函数。 我在观察者中使用了WeakReference来避免内存泄漏。 这里是代码:

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

你可以在这里看到源代码: https : //github.com/andresuarezz26/carpoolingapp

将侦听器设置为片段的正确方法是在连接时设置。 我遇到的问题是onAttachFragment从来没有被调用,经过一些调查,我意识到,我一直在使用getFragmentManager而不是getChildFragmentManager

下面是我如何做到这一点:

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

附加在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() { } }); } }