2012-12-05 57 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() { 

      } 
     }); 
    } 
}