173

質問:DialogFragmentから別のFragmentへのコールバックをどのように作成しますか。私の場合、関係するアクティビティは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を介してバンドルを介して渡されるものです。

リスナーである必要がある場合は、アクティビティを参照する方法があります。

public Dialog onCreateDialog(Bundle bundle) {
    OnClickListener listener = (OnClickListener) getActivity();
    ....
    return new AlertDialog.Builder(getActivity())
        ........
        .setAdapter(adapter, listener)
        .create();
}

ただし、アクティビティでイベントをリッスンしたくないので、フラグメントが必要です。実際には、OnClickListenerを実装する任意のJavaオブジェクトである可能性があります。

DialogFragmentを介してAlertDialogを提示するフラグメントの具体例を考えてみましょう。はい/いいえボタンがあります。これらのボタンの押下を、それを作成したフラグメントに戻すにはどうすればよいですか?

4

17 に答える 17

195

関連するアクティビティは、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();
}
}
于 2012-12-05T22:46:53.573 に答える
88

IllegalStateExceptionTargetFragmentソリューションは、アプリケーションが破棄されて再作成された後に作成される可能性があるため、ダイアログフラグメントに最適なオプションではないようです。この場合FragmentManager、ターゲットフラグメントが見つからなかったため、次のIllegalStateExceptionようなメッセージが表示されます。

「キーandroid:target_state:index1のフラグメントはもう存在しません」

Fragment#setTargetFragment()子と親のフラグメント間の通信ではなく、兄弟のフラグメント間の通信を目的としているようです。

したがって、別の方法はChildFragmentManager、アクティビティを使用するのではなく、親フラグメントのを使用して、このようなダイアログフラグメントを作成することFragmentManagerです。

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

そして、インターフェースを使用することにより、onCreateメソッドでDialogFragment親フラグメントを取得できます。

@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

于 2015-12-14T14:02:02.130 に答える
39

私はこのようなことをするためにこの簡単なステップに従いました。

  1. DialogFragmentCallbackInterfaceのようなメソッドを使用して、のようなインターフェイスを作成しますcallBackMethod(Object data)。データを渡すために呼び出すもの。
  2. DialogFragmentCallbackInterfaceこれで、フラグメントに次のようなインターフェイスを実装できます。MyFragment implements DialogFragmentCallbackInterface
  3. 作成時にDialogFragment、呼び出し元のフラグメントMyFragmentを、作成したターゲットフラグメントとして設定します。チェックsetTargetFragment(フラグメントフラグメント、int requestCode)DialogFragmentを使用します。myDialogFragment.setTargetFragment(this, 0)

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

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

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

于 2015-06-16T18:38:39.903 に答える
36

少し遅れるかもしれませんが、私と同じ質問で他の人を助けるかもしれません。

setTargetFragment表示する前にonを使用できDialog、ダイアログで呼び出しgetTargetFragmentて参照を取得できます。

于 2013-01-07T00:24:33.107 に答える
19

他のフラグメントとの通信ガイドには、フラグメントは関連するアクティビティを介して通信する必要があると記載されています。

多くの場合、たとえばユーザーイベントに基づいてコンテンツを変更するために、あるフラグメントが別のフラグメントと通信する必要があります。フラグメント間の通信はすべて、関連するアクティビティを通じて行われます。2つのフラグメントが直接通信することはありません。

于 2013-10-18T14:40:14.730 に答える
12

フラグメントクラスでを定義し、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){

    }
    ...
}
于 2012-12-05T23:43:54.620 に答える
8

公式文書によると:

Fragment#setTargetFragment

このフラグメントのオプションのターゲット。これは、たとえば、このフラグメントが別のフラグメントによって開始されていて、完了したときに最初のフラグメントに結果を返したい場合に使用できます。ここで設定されたターゲットは、FragmentManager#putFragmentを介してインスタンス間で保持されます。

Fragment#getTargetFragment

setTargetFragment(Fragment、int)によって設定されたターゲットフラグメントを返します。

だからあなたはこれを行うことができます:

// In your fragment

public class MyFragment extends Fragment implements OnClickListener {
    private void showDialog() {
        DialogFragment dialogFrag = MyDialogFragment.newInstance(this);
        // Add this
        dialogFrag.setTargetFragment(this, 0);
        dialogFrag.show(getFragmentManager, null);
    }
    ...
}

// then

public class MyialogFragment extends DialogFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Then get it
        Fragment fragment = getTargetFragment();
        if (fragment instanceof OnClickListener) {
            listener = (OnClickListener) fragment;
        } else {
            throw new RuntimeException("you must implement OnClickListener");
        }
    }
    ...
}
于 2019-08-17T02:07:49.957 に答える
6

推奨されるアプローチは、新しいFragmentResultAPIを使用することです。

これを使用することで、 onAttach(context)やsetTargetFragment()オーバーライドする必要がなくなります。これは現在非推奨です


1-親フラグメントに結果リスナーを追加しますonCreate

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    childFragmentManager.setFragmentResultListener("requestKey", this) { key, bundle ->
        val result = bundle.getString("bundleKey")
    }

}

2-フラグメントで、結果を設定します(たとえば、ボタンクリックリスナーで)。

button.setOnClickListener {
    val result = "resultSample"

    setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}

ドキュメントの詳細:https ://developer.android.com/guide/fragments/communicate#fragment-result

それが役に立てば幸い!

于 2021-10-18T20:21:37.813 に答える
5

リスナーをフラグメントに設定する正しい方法は、リスナーがアタッチされたときにそれを設定することです。私が抱えていた問題は、onAttachFragment()が呼び出されなかったことです。調査の結果、getChildFragmentManagerの代わりにgetFragmentManagerを使用していたことがわかりました。

これが私がそれをする方法です:

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() {

            }
        });
    }
}
于 2017-06-22T07:34:25.607 に答える
5

更新:誰かが興味を持っている場合に共有できるビューモデルを使用してこれを行う簡単な方法があることに注意してください。

Kotlinの皆さん、ここに行きます!

したがって、私たちが抱えている問題は、MainActivityアクティビティを作成したことです。そのアクティビティにフラグメントを作成しました。次に、それを呼び出すFragmentA上にダイアログフラグメントを作成します。通過せずに結果を元に戻すにはどうすればよいですか?FragmentAFragmentBFragmentBFragmentAMainActivity

ノート:

  1. FragmentAの子フラグメントですMainActivity。で作成されたフラグメントを管理するために、FragmentAそれを使用childFragmentManagerします!
  2. FragmentAはの親フラグメントであり、内部FragmentBからアクセスするにはを使用します。FragmentAFragmentBparenFragment

そうは言っても、内部FragmentAでは、

class FragmentA : Fragment(), UpdateNameListener {
    override fun onSave(name: String) {
        toast("Running save with $name")
    }

    // call this function somewhere in a clickListener perhaps
    private fun startUpdateNameDialog() {
        FragmentB().show(childFragmentManager, "started name dialog")
    }
}

これがダイアログフラグメントFragmentBです。

class FragmentB : DialogFragment() {

    private lateinit var listener: UpdateNameListener

    override fun onAttach(context: Context) {
        super.onAttach(context)
        try {
            listener = parentFragment as UpdateNameListener
        } catch (e: ClassCastException) {
            throw ClassCastException("$context must implement UpdateNameListener")
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            val binding = UpdateNameDialogFragmentBinding.inflate(LayoutInflater.from(context))
            binding.btnSave.setOnClickListener {
                val name = binding.name.text.toString()
                listener.onSave(name)
                dismiss()
            }
            builder.setView(binding.root)
            return builder.create()
        } ?: throw IllegalStateException("Activity can not be null")
    }
}

これが2つをリンクするインターフェースです。

interface UpdateNameListener {
    fun onSave(name: String)
}

それでおしまい。

于 2020-05-04T21:42:45.123 に答える
2

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

  1. James McCrackenが上で説明したように、DialogFragmentでインターフェースを宣言します。

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

  3. アクティビティのコールバックメソッドから、必要なジョブを実行するフラグメント内の必須のパブリック関数を呼び出します。

したがって、これは2段階のプロセスになります。DialogFragment-> Activity、次にActivity->Fragmentです。

于 2015-08-22T10:37:24.883 に答える
1

フラグメントLiveWallFilterFragment(フラグメントの受信)からフラグメントDashboardLiveWall(フラグメントの呼び出し)に結果を取得しています...

 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 {
                    }
                }
            }
        }
    }
于 2017-05-04T06:27:22.717 に答える
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

于 2017-05-30T05:04:04.907 に答える
1
    this is work for me
    i think you can set callback in display method in your fragment,
    

    **in my fragment**

    val myDialogFragment=MyDialogFragment()
    myDialogFragment.display(fragmentManager!!,this)

//私のフラグメントはCallbackDialogFragmentを実装しているので、これをdisplayメソッドに設定します

    **in dialog fragment**

    lateinit var callBackResult: CallbackDialogFragment


    fun display(fragmentManager: FragmentManager, callback: CallbackDialogFragment) {
        callBackResult = callback
        show(fragmentManager,"dialogTag")
    }
于 2021-03-05T11:18:17.187 に答える
0

私はこれをRxAndroidでエレガントな方法で解決しました。DialogFragmentのコンストラクターでオブザーバーを受け取り、observableにサブスクライブし、コールバックが呼び出されたときに値をプッシュします。次に、フラグメントでオブザーバーの内部クラスを作成し、インスタンスを作成して、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

于 2017-06-20T05:05:40.800 に答える
0

より改善された方法は、とを使用することnewInstanceですinterface

これが必要なフラグメントですDailogFragment

public class Fragment extends Fragment implements returnPinInterface {
....
....
public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup 
container,Bundle savedInstanceState) {

// A simple call to show DialogFragment

   btnProceed.setOnClickListener(v -> {
        fragment = DailogFragment.newInstance(this);
        fragment.show(getChildFragmentManager(),null );
        fragment.setCancelable(false);
    });


   //Grab whatever user clicked/selected/ or typed
   @Override
   public void onPinReturn(String s) {
      Log.d("ReturnedPin", s);
   }
}

これがDialogFragmentです

public class PinDialogFragment extends DialogFragment {

 //Create a static variable to help you receive instance of fragment you 
 //passed
public static Fragment fragm;


  // Create new Instance and grab the object passed in Fragment up 
  //there
  public static PinDialogFragment newInstance(Fragment frag) {
    PinDialogFragment fragment = new PinDialogFragment();
    fragm = frag;
    return fragment;
  }

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
   View v = inflater.inflate(R.layout.fragment_pin, container, false);

  //
   btn.setOnClickListener(btn ->                         
   listener.onReturnPin("1234"));
   
   return v;
}


//Use the Fragm to instantiate your Interface

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (fragm instanceof ReturnPinInterface) {
        listener = (ReturnPinInterface) fragm;
    } else {
        throw new RuntimeException("you must implement ReturnPinInterface");
    }
}
}

楽しみ!

于 2021-04-27T21:22:45.673 に答える
0

Androidの公式ドキュメントで使用するonAttachことをお勧めします。したがって、その方法を利用できます。

親フラグメントがリスナーを実装していることを確認してください。例OnPopupButtonClickListener

public interface OnPopupButtonClickListener {
   void onPositiveButtonClicked();
   void onNegativeButtonClicked();
}

親フラグメントで、次DialogFragmentを使用してインスタンスを表示しgetChildFragmentManager()ます。

PopupDialogFragment dialogFragment = new PopupDialogFragment();
dialogFragment.show(getChildFragmentManager(), "PopupDialogFragment");

インスタンスを拡張するダイアログクラスで、次のDialogFragmentメソッドを追加します:(getParentFragment()カスタムリスナーインターフェイスを実装する親フラグメントを取得していることに注意してくださいOnPopupButtonClickListener

@Override
public void onAttach(@NonNull @NotNull Context context) {
   super.onAttach(context);
   // Verify that the host activity implements the callback interface
   try {
      listener = (OnPopupButtonClickListener) getParentFragment();
   } catch (ClassCastException e) {
      // The activity doesn't implement the interface, throw exception
      throw new ClassCastException(getActivity().toString()
                    + " must implement OnPopupButtonClickListener");
   }
}

DialogFragmentダイアログでは、たとえば次のように、必要なときにいつでもリスナーを使用できます。

Button positiveButton = view.findViewById(R.id.positiveButton);
positiveButton.setOnClickListener(v -> {
    if (listener != null) {
       listener.onPositiveButtonClicked();
       getDialog().dismiss();
    }
});
于 2021-07-29T08:45:36.440 に答える