2012-12-05 36 views
119

質問:DialogFragmentから別のFragmentへのコールバックを作成するにはどうすればいいですか?私の場合、関与するアクティビティはDialogFragmentを完全に認識していないはずです。DialogFragmentからのフラグメントへのコールバック

は、私は私

MyDialogFragmentが

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

のように見える。しかし、リスナーがするという保証はありません

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

を行うことができますいくつかの点で続い

public class MyFragment extends Fragment implements OnClickListener 

を持って考えてみましょう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を提示するFragmentの具体例を考えてみましょう。はい/いいえボタンがあります。どのようにしてこれらのボタンを作成したフラグメントに戻すことができますか?

+0

「あなたはDialogFragmentが一時停止してから再開する場合にリスナーが周囲にいるという保証はありませんifecycle。私は、フラグメント状態がonDestroy()の間に破壊されると思いましたか?あなたは正しくなければなりませんが、断片化状態をどのように使用するのかちょっと混乱しています。あなたが言った問題を再現するにはどうしたらいいですか? – Sean

+0

代わりにDialogFragmentの 'OnClickListener listener =(OnClickListener)getParentFragment();'を単に使うことができない理由は分かりません。 – 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'の使用は少し不明です。おそらく、onActivityResultを再利用するのではなく、独自の特定のメソッドをFragment呼び出し側で宣言して使用する方がよいでしょう。しかし、その時点でのすべてのセマンティクス。 – 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

フラグメント同士を直接話すことの懸念は、レイアウトによっては全てのフラグメントが読み込まれない例に示すように、フラグメントを切り替える必要があるかもしれません。フラグメントからダイアログフラグメントを起動することについて話しているとき、その関心事は有効ではないと私は考えています。 –

30
たぶん少し遅れ

が、私がやったように同じ質問を他の人々を助けるかもしれないコードは次のようになります。

DialogsetTargetFragmentを表示してからダイアログに戻って、getTargetFragmentに電話して参考にすることができます。

+0

これは別の質問への回答ですが、あなたの質問にも当てはまり、クリーンな解決策です:http:// stackoverflow。 com/questions/28620026/implement-dialogfragment-interface-onclicklistener/33713825#33713825 – user2288580

+0

ありがとうございます。それは私のために働いた:) –

19

Communicating with Other Fragmentsガイドでは、フラグメントは関連するアクティビティで通信する必要があります。

多くの場合、あなたは一つの断片がユーザーイベントに基づいてコンテンツを変更する 例えば、相互に通信することになるでしょう。すべて フラグメントとフラグメントの通信は、関連付けられた アクティビティを通じて行われます。2つのフラグメントは決して直接通信するべきではありません。

+0

どのように内側の断片については、どのように別のフラグメント内のフラグメントは、ホストフラグメントと通信する必要があります – Ravi

+0

@ラヴィ:各フラグメントは、[getActivity()]を呼び出すことによってすべてのフラグメントに共通の活動と通信する必要があります://developer.android.com/reference/android/app/Fragment.html#getActivity())。 –

+0

@EdwardBrey:この場合、インスタンス状態キーをどうやって整理しますか?複数の「ベース」フラグメントが同じDialogFragment実装を使用する場合 –

21

私はこの簡単な手順を実行してこの作業を行いました。

  1. callBackMethod(Object data)のような方法でDialogFragmentCallbackInterfaceのようなインターフェイスを作成します。あなたはデータを渡すために呼び出します。
  2. 今、あなたはDialogFragment作成時にMyFragment implements DialogFragmentCallbackInterface
  3. ようなあなたのフラグメントでDialogFragmentCallbackInterfaceインターフェイスを実装することができますが作成された目的の断片としてあなたの呼び出しフラグメントMyFragmentを設定DialogFragment使用myDialogFragment.setTargetFragment(this, 0)チェックsetTargetFragment (Fragment fragment, int requestCode)

    MyDialogFragment dialogFrag = new MyDialogFragment(); 
    dialogFrag.setTargetFragment(this, 1); 
    
  4. ターゲットフラグメントを取得オブジェクトをDialogFragmentに呼び出し、getTargetFragment()を呼び出してDialogFragmentCallbackInterfaceにキャストします。このインターフェースを使用して、フラグメントにデータを送信することができます。

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

    これですべて完了です!フラグメントでこのインタフェースを実装していることを確認してください。

+2

これは最良の答えです。素晴らしい答え。 –

2

私は同様の問題に直面していました。私が見つけた解決策は:

  1. ちょうどジェームズ・マクラッケンのようなあなたのDialogFragmentのインターフェイスは、上記で説明した宣言。

  2. あなたのアクティビティにインターフェイスを実装します(フラグメントではありません!これは良い方法ではありません)。

  3. あなたのアクティビティのコールバックメソッドから、あなたのやりたい仕事をしているフラグメントに必要なpublic関数を呼び出します。

したがって、それは2段階のプロセスを次のようになります。DialogFragment - >活動し、その後の活動 - それは塗布後にIllegalStateExceptionを作成する可能性があるので>フラグメント

33

TargetFragmentソリューションは、ダイアログのフラグメントのための最良のオプションはいないようです破壊され再現される。この場合、FragmentManagerは、目的の断片を見つけることができなかった、あなたは、このようなメッセージをIllegalStateExceptionを取得します:

「フラグメントはもはや重要なアンドロイドのために存在する:target_state:インデックス1」

それFragment#setTargetFragment()は、子と親の断片の間の通信ではなく、むしろ兄弟間の通信のためのものであると思われます。DialogFragmentあなたのonCreate方法では、

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

およびインターフェースを使用して:

ので、別の方法ではなく、その後の活動FragmentManagerを使用して、親フラグメントのChildFragmentManagerを使用して、このようなダイアログ断片を作成することです

@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

私は断片LiveWallFilterFragment(受信フラグメント)このようにからのフラグメントDashboardLiveWall(呼び出しフラグメント)に結果を取得していますが

バックなどのフラグメントを呼び出すsetResult
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; 
    } 
更新
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のコンストラクタでオブザーバを受け取り、監視可能にし、コールバックが呼び出されるときに値をプッシュします。次に、あなたのフラグメントで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

リスナーをフラグメントに設定する正しい方法は、のときに設定することです。

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

は、それを添付:私が持っていた問題がonAttachFragmentはここ

は、私はそれを行う方法であるgetFragmentManager代わりのgetChildFragmentManagerを使用して、私は私がしていたことに気づいたいくつかの調査の後、呼び出されることはありませんしたことonAttachFragment:

関連する問題