2012-12-05 60 views
119

问题:如何创建从DialogFragment到另一个片段的回调。就我而言,涉及的活动应完全不知道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的片段的具体示例。它有Yes/No按钮。我怎样才能将这些按钮发送回创建它的片段?

+0

您刚才提到“但也不能保证,如果DialogFragment暂停监听器将围绕并通过其L-恢复ifecycle“。我认为碎片状态在onDestroy()期间被破坏?你一定是对的,但我现在只是有点困惑如何使用Fragment状态。我如何重现你提到的问题,听众不在身边? – Sean

+0

我不明白为什么你不能简单地用'OnClickListener监听=(OnClickListener)getParentFragment();'在DialogFragment,而是和你的主要片段实现的接口为你做了最初。 – kiruwka

+0

这里是一个答案,一个不相关的问题,但它不告诉你这是如何在一个干净的方式http://stackoverflow.com/questions/28620026/implement-dialogfragment-interface-in-onclicklistener/33713825#33713825 – user2288580

回答

163

涉及的活动完全不知道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(); 
} 
} 
+83

我觉得做这里的关键是'setTargetFragment'和'getTargetFragment'。 'onActivityResult'的使用有点不清楚。在Fragment调用者中声明你自己特定的方法可能会更好,并使用它,而不是重新使用onActivityResult。但是它在这一点上的所有语义。 – eternalmatt

+1

堆栈级别变量未使用? –

+6

这将保存配置更改 - 旋转吗? – Maxrunner

9

你应该定义在你的片段类的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){ 

    } 
    ... 
} 
+1

我认为你太快删除了文档。这两个代码段都是'FragmentA',他假设一个活动是一个OnArticleSelectedListener,而不是启动他的片段。 – eternalmatt

+2

我会考虑你想要做的不好的做法。 Android指导方针建议所有片段到片段的交流通过活动进行(根据http://developer.android.com/training/basics/fragments/communicating.html)。如果你真的想这一切内部处理'MyFragment'你可能要切换到使用普通的'AlertDialog' –

+1

我想用具有片段直接互相交谈值得关注的是,在某些布局并非所有的片段可以加载正如它们在示例中所示,可能需要切换片段。在谈论从片段启动对话框片段时,我不认为这种担心是有效的。 – 2013-07-29 21:27:14

30

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

您可以在Dialog上使用setTargetFragment,然后在对话框中您可以拨打getTargetFragment以获得参考。

+0

这里是一个回答另一个问题与活动进行沟通,而不是片段,但它也适用于你的问题,是一个干净的解决方案:HTTP://计算器。 com/questions/28620026/implementation-dialogfragment-interface-in-onclicklistener/33713825#33713825 – user2288580

+0

谢谢你。它为我工作:) –

19

Communicating with Other Fragments指南指示碎片应该通过相关活动与进行通信。

通常您会希望一个片段与另一个片段进行通信,例如 示例可根据用户事件更改内容。全部 片段与片段之间的通信通过关联的 活动完成。两个碎片不应该直接通信。

+0

怎么样内片段,即另一个片段中的片段应该如何传达给主机片段 – Ravi

+0

@Ravi:每个片段应该是共同的所有片段通过调用活动通信[getActivity()](HTTP ://developer.android.com/reference/android/app/Fragment.html#getActivity())。 –

+0

@EdwardBrey:在这种情况下,你如何组织你的实例状态键?即如果多个“基本”片段使用相同的DialogFragment实现? –

21

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

  1. 与像callBackMethod(Object data)一些方法创建像DialogFragmentCallbackInterface接口。你会打电话传递数据。
  2. 现在,您可以实现DialogFragmentCallbackInterface界面在你的片段像MyFragment implements DialogFragmentCallbackInterface
  3. DialogFragment创建时间设置您调用片段MyFragment作为目标片段谁创造DialogFragment使用myDialogFragment.setTargetFragment(this, 0)检查setTargetFragment (Fragment fragment, int requestCode)

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

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

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

+2

这应该是最好的答案。好的答案。 –

2

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

  1. 声明一个接口在DialogFragment就像詹姆斯·麦克拉肯已如上所述。

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

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

因此,就变成了两个步骤的过程:DialogFragment - >活动,然后活动 - >片段

33

TargetFragment溶液似乎用于对话片段的最佳选项不会因为它可能应用后创建IllegalStateException被破坏并重新创建。在这种情况下FragmentManager找不到目标片段,你会得到一个IllegalStateException像这样的消息:

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

它好像Fragment#setTargetFragment()不是用于孩子和父母片段之间的通信,而是用于兄弟片段之间的通信。

所以另一种方式是通过使用父片段的ChildFragmentManager,而不是使用活动FragmentManager建立对话片段是这样的:

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

,并通过使用一个接口,在该DialogFragmentonCreate方法可以得到父代片段:

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

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

有关该问题的更多信息,你可以检查出的链接: https://code.google.com/p/android/issues/detail?id=54520

+0

它为我工作。 –

+0

很好的答案。 – Manoj

1

我得到的结果到碎片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 { 
        } 
       } 
      } 
     } 
    } 
1

更新时间:

我做了基于产生那些使用@CallbackFragment@Callback铸造我对你的要点代码库。

https://github.com/zeroarst/callbackfragment

并且该示例给出了从片段向另一个片段发送回调的示例。

老答案:

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

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

public class EchoFragment extends BaseCallbackFragment { 

    private FragmentInteractionListener mListener; 

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

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

0

我与RxAndroid一个优雅的方式解决了这个。在DialogFragment的构造函数中接收观察者,并在调用回调时订阅observable并推送该值。然后,在您的Fragment中创建一个Observer的内部类,创建一个实例并将其传递给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

0

收听者设置的片段的正确的方法是通过设置它时,它是。我的问题是,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() { 

      } 
     }); 
    } 
}