从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 。
通常你会想要一个片段与另一个片段进行通信,例如根据用户事件来改变内容。 所有片段到片段的通信都是通过关联的活动完成的。 两个碎片不应该直接沟通。
我遵循这个简单的步骤来做这个东西。
- 使用
callBackMethod(Object data)
等方法创build像DialogFragmentCallbackInterface
这样的接口。 你会打电话来传递数据。 - 现在你可以在你的片段中实现
DialogFragmentCallbackInterface
接口,就像MyFragment implements DialogFragmentCallbackInterface
-
在创build
DialogFragment
时,将调用片段MyFragment
设置为创buildDialogFragment
目标片段,使用myDialogFragment.setTargetFragment(this, 0)
检查setTargetFragment(片段片段,int requestCode)MyDialogFragment dialogFrag = new MyDialogFragment(); dialogFrag.setTargetFragment(this, 1);
-
通过调用
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是:
-
像James McCracken在上面解释的那样在DialogFragment中声明一个接口。
-
在你的活动中实现接口(不是片段!这不是一个好的做法)。
-
从你的活动中的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() { } }); } }