DialogFragmentから結果を受け取る


232

DialogFragmentsを使用して、リストから項目を選択したり、テキストを入力したりしています。

値(つまり、リストからの文字列または項目)を呼び出しアクティビティ/フラグメントに戻す最良の方法は何ですか?

現在、私は呼び出しアクティビティを実装DismissListenerし、DialogFragmentにアクティビティへの参照を与えています。次に、Dialog OnDimissがアクティビティのメソッドを呼び出し、アクティビティがDialogFragmentオブジェクトから結果を取得します。非常に乱雑で、DialogFragmentがアクティビティへの参照を失うため、構成の変更(方向の変更)では機能しません。

助けてくれてありがとう。


10
DialogFragmentsはまだフラグメントです。あなたのアプローチは、実際にフラグメントがメインアクティビティに戻るために使用する推奨方法です。developer.android.com/guide/topics/fundamentals/...
codinguser

1
それをありがとう。私は(あなたが言ったように)非常に近かった。そのリンクされたドキュメントが私を助けてくれたのは、onAttach()を使用してアクティビティをリスナーにキャストすることでした。
James Cross

2
@ codinguser、@ Styx-「DialogFragmentにアクティビティへの参照を与える」- Activityとの両方DialogFragmentが再作成される可能性があるため、この詳細は少し危険です。Activity渡されたを使用するのがonAttach(Activity activity)適切で推奨される方法です。
sstn 2013年

回答:


246

myDialogFragment.setTargetFragment(this, MY_REQUEST_CODE)ダイアログを表示する場所から使用し、ダイアログが終了したら、そこからを呼び出して、含まれているフラグメントにgetTargetFragment().onActivityResult(getTargetRequestCode(), ...)実装できますonActivityResult()

onActivityResult()特にアクティビティをまったく含まないため、の乱用のようです。しかし、私はそれが公式のグーグル人々によって推薦されたのを見た、そして多分apiデモでさえ。それg/setTargetFragment()が追加された理由だと思います。


2
setTargetFragmentは、リクエストコードがonActivityResultで使用するためのものであることを述べているので、このアプローチを使用しても大丈夫だと思います。
ジョルジ2013

87
ターゲットがアクティビティの場合はどうなりますか?
フェルナンドガジェゴ2014年

9
ターゲットがアクティビティの場合、「void onActivityResult2(int requestCode、int resultCode、Intent data)」のようなメソッドを使用してインターフェイスを宣言し、アクティビティによって実装します。DialogFragmentでは、getActivityを使用してこのインターフェイスを確認し、適切に呼び出します。
Ruslan Yanchyshyn 2014

4
それは良い解決策ではありません。ダイアログフラグメントの状態を保存および復元した後は機能しません。この場合、LocalBroadcastManagerが最適なソリューションです。
Nik

4
@Nikそれは真実ではありません。これが最良のソリューションです。状態の保存、復元は問題ありません。問題が発生した場合は、間違ったフラグメントマネージャを使用しました。ターゲットフラグメント/呼び出し元は、ダイアログを表示するためにgetChildFragmentManager()を使用する必要はありません。
信じられないほどの月

140

ここを見るとわかるように、非常に簡単な方法があります。

あなたのDialogFragmentようなインターフェースリスナーを追加してください:

public interface EditNameDialogListener {
    void onFinishEditDialog(String inputText);
}

次に、そのリスナーへの参照を追加します。

private EditNameDialogListener listener;

これは、リスナーメソッドを「アクティブ化」し、親アクティビティ/フラグメントがこのインターフェイスを実装しているかどうかを確認するためにも使用されます(以下を参照)。

ではActivity/ FragmentActivity/ Fragment「と呼ばれる」ということDialogFragmentだけで、このインタフェースを実装しています。

あなたにはDialogFragment、すべてあなたが却下したいポイントで追加する必要がありDialogFragment、その結果を返す、この次のとおりです。

listener.onFinishEditDialog(mEditText.getText().toString());
this.dismiss();

mEditText.getText().toString()呼び出しに渡されるものはどこですかActivity

他のものを返したい場合は、リスナーが取る引数を変更するだけであることに注意してください。

最後に、インターフェースが実際に親アクティビティ/フラグメントによって実装されたかどうかを確認する必要があります。

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Verify that the host activity implements the callback interface
    try {
        // Instantiate the EditNameDialogListener so we can send events to the host
        listener = (EditNameDialogListener) context;
    } catch (ClassCastException e) {
        // The activity doesn't implement the interface, throw exception
        throw new ClassCastException(context.toString()
                + " must implement EditNameDialogListener");
    }
}

この手法は非常に柔軟性があり、ダイアログを閉じたくない場合でも結果をコールバックできます。


13
これは、との素晴らしい作品ActivityのとFragmentActivityのが、発信者Aの場合はFragment
Brais Gabin 2013

1
私はあなたを完全に理解しているとは思いません。ただし、呼び出し元がの場合は同じように機能しFragmentます。
Assaf Gamliel 2013

2
呼び出し元がaの場合、Fragmentいくつかのことができます。1.フラグメントを参照として渡します(メモリリークが発生する可能性があるため、お勧めできません)。2. FragmentManagerand呼び出しを使用するfindFragmentByIdfindFragmentByTag、アクティビティに存在するフラグメントを取得します。お役に立てば幸いです。すてきな一日を!
Assaf Gamliel 2013

5
このアプローチの問題は、フラグメントは、それらが再作成されることを意図されているので、向きを変更するインスタンスの試みのために、オブジェクトを保持では非常に良好ではないということです、フラグメントが、リスナーのインスタンスを再作成しますOSはもう利用できません
Necronet

5
@LOG_TAG @Timmmmの答えを見てください。setTargetFragment()そしてgetTargetFragment()魔法です。
Brais Gabin 2013

48

DialogFragmentから結果を受け取るには、もっと簡単な方法があります。

まず、Activity、Fragment、またはFragmentActivityで、次の情報を追加する必要があります。

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Stuff to do, dependent on requestCode and resultCode
    if(requestCode == 1) { // 1 is an arbitrary number, can be any int
         // This is the return result of your DialogFragment
         if(resultCode == 1) { // 1 is an arbitrary number, can be any int
              // Now do what you need to do after the dialog dismisses.
         }
     }
}

これrequestCodeは基本的に、あなたが呼び出したDialogFragmentのintラベルです。これがどのように機能するかをすぐに示します。resultCodeは、DialogFragmentから返送されるコードで、現在待機中のActivity、Fragment、またはFragmentActivityに何が起こったかを伝えます。

次に行うコードは、DialogFragmentの呼び出しです。例はここにあります:

DialogFragment dialogFrag = new MyDialogFragment();
// This is the requestCode that you are sending.
dialogFrag.setTargetFragment(this, 1);     
// This is the tag, "dialog" being sent.
dialogFrag.show(getFragmentManager(), "dialog");

これらの3行で、DialogFragmentを宣言し、ダイアログが閉じられるとrequestCode(onActivityResult(...)を呼び出す)を設定し、ダイアログを表示します。これはとても簡単です。

ここで、DialogFragmentで、resultCodeをdismiss()onActivityResult()に送り返すために、直前に1行追加する必要があります。

getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, getActivity().getIntent());
dismiss();

それでおしまい。この場合、resultCodeはint resultCode私が設定しresultCode = 1;たものとして定義されていることに注意してください。

以上で、DialogFragmentの結果を呼び出し元のActivity、Fragment、またはFragmentActivityに送信できるようになりました。

また、この情報は以前に投稿されたようですが、十分な例が示されていないので、もう少し詳しく説明したいと思いました。

編集06.24.2016 上記の誤解を招くコードをお詫び申し上げます。しかし、ほとんどの場合、次の行を見てアクティビティに結果を受け取ることはできません。

dialogFrag.setTargetFragment(this, 1);

ではFragmentなくターゲットを設定しますActivity。したがって、これを行うには、の実装を使用する必要がありますInterfaceCommunicator

あなたのDialogFragmentセットでグローバル変数

public InterfaceCommunicator interfaceCommunicator;

それを処理するパブリック関数を作成する

public interface InterfaceCommunicator {
    void sendRequestCode(int code);
}

あなたがにコードバックを送信する準備ができたら次にActivity際にDialogFragment動作して行われている、あなたは単にあなたの前に行を追加しますdismiss();あなたのDialogFragment

interfaceCommunicator.sendRequestCode(1); // the parameter is any int code you choose.

アクティビティでは、次の2つのことを行う必要があります。1つ目は、適用されなくなった1行のコードを削除することです。

dialogFrag.setTargetFragment(this, 1);  

その後、インターフェースを実装すれば、すべて完了です。これを行うにimplementsは、クラスの最上部の句に次の行を追加します。

public class MyClass Activity implements MyDialogFragment.InterfaceCommunicator

そして@Override、活動の機能、

@Override
public void sendRequestCode(int code) {
    // your code here
}

メソッドと同じように、このインターフェイスメソッドを使用しますonActivityResult()。インターフェースメソッドがのためのものでDialogFragmentsあり、もう一方がのものであることを除いてFragments


4
保護されたアクセスレベルのため、(DialogFragmentから)onActivityResultを呼び出すことができないため、ターゲットがActivityである場合、このアプローチは機能しません。
Ruslan Yanchyshyn 2014

2
それは単に真実ではありません。私は自分のプロジェクトでこの正確なコードを使用しています。それは私がそれを引っ張ったところで、それはうまくいきます。この保護されたアクセスレベルの問題がある場合は、必要に応じて、メソッドとクラスのアクセスレベルを保護からプライベートまたはパブリックに変更できます。
ブランドン

6
こんにちは、あなたはdialogFrag.setTargetFragment(this, 1)アクティビティから呼び出すことができると言いますが、このメソッドは最初の引数としてフラグメントを受け取るため、これをキャストできませんでした。私は正しいですか?
MondKin、2015年

1
アクティビティについて説明するために、いくつかの回答をいくつか投稿します。
Brandon

1
@Swift @lcompareおそらく、DialogFragmentでonAttach(Context context)をオーバーライドする必要があります。このように:@Override public void onAttach(Context context) { super.onAttach(context); yourInterface = (YourInterface) context; }
lidkxx

20

まあそれは遅すぎるかもしれませんが、これは私が結果を返すために私がやったことDialogFragmentです。@brandonの回答と非常に似ています。ここでDialogFragmentは、フラグメントから呼び出しています。ダイアログを呼び出す場所にこのコードを配置してください。

FragmentManager fragmentManager = getFragmentManager();
            categoryDialog.setTargetFragment(this,1);
            categoryDialog.show(fragmentManager, "dialog");

categoryDialogDialogFragmentがどこに呼び出したいのか、そしてあなたの実装の中dialogfragmentでこのコードを意図的にデータを設定している場所に私のコードはどこですか?の値resultCodeは1で、設定するか、システム定義を使用できます。

            Intent intent = new Intent();
            intent.putExtra("listdata", stringData);
            getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
            getDialog().dismiss();

ここで、呼び出しフラグメントに戻り、このメソッドを実装します。データの有効性を確認するか、あなたがしたい場合は成功を引き起こすresultCoderequestCodeの条件の場合に。

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);        
        //do what ever you want here, and get the result from intent like below
        String myData = data.getStringExtra("listdata");
Toast.makeText(getActivity(),data.getStringExtra("listdata"),Toast.LENGTH_SHORT).show();
    }

10

フラグメントがそのアクティビティまで通信できるようにする別のアプローチ:

1)フラグメントでパブリックインターフェイスを定義し、その変数を作成します

public OnFragmentInteractionListener mCallback;

public interface OnFragmentInteractionListener {
    void onFragmentInteraction(int id);
}

2)フラグメントのmCallback変数にアクティビティをキャストします

try {
    mCallback = (OnFragmentInteractionListener) getActivity();
} catch (Exception e) {
    Log.d(TAG, e.getMessage());
}

3)アクティビティにリスナーを実装する

public class MainActivity extends AppCompatActivity implements DFragment.OnFragmentInteractionListener  {
     //your code here
}

4)アクティビティのOnFragmentInteractionをオーバーライドします

@Override
public void onFragmentInteraction(int id) {
    Log.d(TAG, "received from fragment: " + id);
}

詳細:https : //developer.android.com/training/basics/fragments/communicating.html


まとめてくれてありがとう。Android Devsチュートリアルでは、他の人への1つのメモとしてpublic void onAttach、フラグメントのをオーバーライドし、そこでアクティビティをキャストすることを提案しています
Big_Chair

8

私が見つけた1つの簡単な方法は次のとおりです。これを実装するには、dialogFragment、

  CallingActivity callingActivity = (CallingActivity) getActivity();
  callingActivity.onUserSelectValue("insert selected value here");
  dismiss();

次に、Dialog Fragmentを呼び出したアクティビティで、適切な関数を作成します。

 public void onUserSelectValue(String selectedValue) {

        // TODO add your implementation.
      Toast.makeText(getBaseContext(), ""+ selectedValue, Toast.LENGTH_LONG).show();
    }

トーストはそれが機能することを示すことです。私のために働いた。


それを行う正しい方法であるかどうかは
わかり

Interface具象クラスとハードカップリングするよりも、使用するほうが適切です。
waqaslam

6

誰もDialogFragmentActivityコミュニケーションのためにローカル放送を使用することを提案していないことを見て、私はとても驚いています!他の提案よりもずっとシンプルでクリーンであることがわかりました。基本的に、Activityブロードキャストをリッスンするように登録し、DialogFragmentインスタンスからローカルブロードキャストを送信します。シンプル。すべてをセットアップする方法のステップバイステップガイドについては、こちらを参照してください


2
私はそのソリューションが好きですが、これはAndroidでの良いまたはベストプラクティスと見なされますか?
Benjamin Scharbau、2015年

1
私は本当にチュートリアルが好きだった、それを投稿してくれてありがとう。私はあなたがどちらの方法を達成しようとしているのかに応じて、もう一方よりも役立つかもしれないことを付け加えたいと思います。複数の入力/結果がダイアログからアクティビティに送り返される場合は、ローカルブロードキャストルートをお勧めします。出力が非常に基本的/単純な場合は、onActivityResultルートを使用することをお勧めします。したがって、ベストプラクティスの質問に答えるには、何を達成しようとしているのかによって異なります。
ブランドン

1
@AdilHussainそうですね。私は人々が彼らの活動の中でフラグメントを使用していると仮定しました。FragmentおよびDialogFragmentと通信する場合、setTargetFragmentオプションは最適です。ただし、DialogFragmentを呼び出すアクティビティの場合は、Broadcastメソッドを使用する必要があります。
ブランドン

3
フーの愛のために、ブロードキャストを使用しないでください!! それはあなたのアプリケーションを多くのセキュリティ問題に開きます。また、私は虐待放送に取り組む必要がある最悪のAndroidアプリケーションを見つけました。コードを完全に使用できなくするより良い方法を考えられますか?CLEARコード行ではなく、ブロードキャストレシーバーをルートアウトする必要がありますか?明確にするために、ブロードコーストの用途がありますが、このコンテキストではありません!このような状況では絶対に使用しないでください。そのずさんな。ローカルかどうか。必要なのはコールバックだけです。
StarWind0

1
Guava EventBusと同様に、別のオプションはGreenRobot EventBusです。私はGuava EventBusを使用していませんが、GreenRobot EventBusを使用していて、それを使って良い経験をしました。使いやすく、シンプルです。GreenRobot EventBusを使用するようにAndroidアプリケーションを構築する方法の小さな例については、こちらを参照してください
アディルフセイン

3

または、ここに示すようにViewModelを共有します。

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}


public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing_data_between_fragments


2

私の場合、引数をtargetFragmentに渡す必要がありました。しかし、「フラグメントはすでにアクティブです」という例外が発生しました。そのため、parentFragmentが実装したDialogFragmentでインターフェイスを宣言しました。parentFragmentがDialogFragmentを開始すると、それ自体がTargetFragmentとして設定されます。次に、DialogFragmentで私は呼び出しました

 ((Interface)getTargetFragment()).onSomething(selectedListPosition);

2

コトリンで

    // My DialogFragment
class FiltroDialogFragment : DialogFragment(), View.OnClickListener {
    
    var listener: InterfaceCommunicator? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        listener = context as InterfaceCommunicator
    }

    interface InterfaceCommunicator {
        fun sendRequest(value: String)
    }   

    override fun onClick(v: View) {
        when (v.id) {
            R.id.buttonOk -> {    
        //You can change value             
                listener?.sendRequest('send data')
                dismiss()
            }
            
        }
    }
}

//マイアクティビティ

class MyActivity: AppCompatActivity(),FiltroDialogFragment.InterfaceCommunicator {

    override fun sendRequest(value: String) {
    // :)
    Toast.makeText(this, value, Toast.LENGTH_LONG).show()
    }
}
 

私はそれが役立つことを願っています、改善できる場合は編集してください。私の英語はあまり上手ではありません


私の場合、ダイアログはアクティビティではなくフラグメントから作成されているため、このソリューションは機能しません。しかし、私はあなたが置いたスマイリーが好きです:)
Ssenyonjo

0

引数を送信して2番目のフラグメントから結果を受信する場合は、Fragment.setArgumentsを使用してこのタスクを実行できます

static class FirstFragment extends Fragment {
    final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 101: // receive the result from SecondFragment
                Object result = msg.obj;
                // do something according to the result
                break;
            }
        };
    };

    void onStartSecondFragments() {
        Message msg = Message.obtain(mUIHandler, 101, 102, 103, new Object()); // replace Object with a Parcelable if you want to across Save/Restore
                                                                               // instance
        putParcelable(new SecondFragment(), msg).show(getFragmentManager().beginTransaction(), null);
    }
}

static class SecondFragment extends DialogFragment {
    Message mMsg; // arguments from the caller/FirstFragment

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);
        mMsg = getParcelable(this);
    }

    void onClickOK() {
        mMsg.obj = new Object(); // send the result to the caller/FirstFragment
        mMsg.sendToTarget();
    }
}

static <T extends Fragment> T putParcelable(T f, Parcelable arg) {
    if (f.getArguments() == null) {
        f.setArguments(new Bundle());
    }
    f.getArguments().putParcelable("extra_args", arg);
    return f;
}
static <T extends Parcelable> T getParcelable(Fragment f) {
    return f.getArguments().getParcelable("extra_args");
}

-3

(まだ誰も言及していないので)オプションの1つとしてそれを持っているだけです-Ottoのようなイベントバスを使用できます。したがって、ダイアログでは次のようにします。

bus.post(new AnswerAvailableEvent(42));

呼び出し元(アクティビティまたはフラグメント)にサブスクライブしてもらいます。

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
   // TODO: React to the event somehow!
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.