質問:DialogFragmentから別のFragmentへのコールバックをどのように作成しますか。私の場合、関連するアクティビティはDialogFragmentを完全に認識しない必要があります。
私が持っていると考えてください
public class MyFragment extends Fragment implements OnClickListener
その後、ある時点でcould do
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を提示するフラグメントの具体例を考えてみましょう。はい/いいえボタンがあります。これらのボタンの押下を、それを作成したフラグメントに戻すにはどうすればよいですか?
関連するアクティビティは、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();
}
}
TargetFragmentソリューションは、アプリケーションが破棄および再作成された後にIllegalStateException
を作成する可能性があるため、ダイアログフラグメントに最適なオプションではないようです。この場合、FragmentManager
はターゲットフラグメントを見つけることができず、次のようなメッセージを含むIllegalStateException
を取得します。
「キーAndroid:target_stateのフラグメントはもう存在しません:インデックス1」
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=5452
たぶん少し遅いかもしれませんが、私がやったように同じ質問を持つ他の人を助けるかもしれません。
表示する前にsetTargetFragment
でDialog
を使用できます。ダイアログでgetTargetFragment
を呼び出して参照を取得できます。
この単純な手順に従って、このようなことを行いました。
callBackMethod(Object data)
などのメソッドを使用して、DialogFragmentCallbackInterface
name__などのインターフェイスを作成します。データを渡すために呼び出すもの。MyFragment implements DialogFragmentCallbackInterface
のようなDialogFragmentCallbackInterface
name__インターフェイスを実装できますDialogFragment
name__作成時に、MyFragment
name__を作成したターゲットフラグメントとして呼び出しフラグメントDialogFragment
name__を設定しますmyDialogFragment.setTargetFragment(this, 0)
checkを使用します setTargetFragment(フラグメントフラグメント、int requestCode)
MyDialogFragment dialogFrag = new MyDialogFragment();
dialogFrag.setTargetFragment(this, 1);
getTargetFragment()
を呼び出してターゲットフラグメントオブジェクトをDialogFragment
name__に取得し、それをDialogFragmentCallbackInterface
name__にキャストします。これで、このインターフェイスを使用してフラグメントにデータを送信できます。
DialogFragmentCallbackInterface callback =
(DialogFragmentCallbackInterface) getTargetFragment();
callback.callBackMethod(Object data);
これですべて完了です!フラグメントにこのインターフェースが実装されていることを確認してください。
Communicating with Other Fragments ガイドは、フラグメントが関連するアクティビティを介して通信する必要があることを示しています。
多くの場合、たとえばユーザーイベントに基づいてコンテンツを変更するために、あるフラグメントが別のフラグメントと通信する必要があります。フラグメント間の通信はすべて、関連付けられたアクティビティを通じて行われます。 2つのフラグメントが直接通信することはありません。
フラグメントクラスで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){
}
...
}
私は同様の問題に直面していました。私が見つけた解決策は:
James McCrackenが上記で説明したように、DialogFragmentでインターフェイスを宣言します。
アクティビティにインターフェースを実装します(フラグメントではありません!それは良い習慣ではありません)。
アクティビティのコールバックメソッドから、目的のジョブを実行するフラグメントの必須パブリック関数を呼び出します。
したがって、それは2段階のプロセスになります:DialogFragment-> Activityそして、Activity-> Fragment
フラグメントをリスナーに設定する正しい方法は、それがattachedのときに設定することです。私が抱えていた問題は、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() {
}
});
}
}
更新:
@CallbackFragment
と@Callback
を使用して、これらのキャストを生成するGistコードに基づいてライブラリを作成しました。
https://github.com/zeroarst/callbackfragment 。
また、この例では、フラグメントから別のフラグメントにコールバックを送信する例を示しています。
古い答え:
BaseCallbackFragment
と注釈@FragmentCallback
を作成しました。現在、Fragment
を拡張していますが、DialogFragment
に変更することもできます。次の順序で実装をチェックします:getTargetFragment()> getParentFragment()> context(activity)。
次に、それを拡張し、フラグメントでインターフェースを宣言し、注釈を付けるだけで、ベースフラグメントが残りを行います。注釈には、フラグメントにコールバックを実装させるかどうかを決定するためのパラメーターmandatory
もあります。
public class EchoFragment extends BaseCallbackFragment {
private FragmentInteractionListener mListener;
@FragmentCallback
public interface FragmentInteractionListener {
void onEcho(EchoFragment fragment, String echo);
}
}
https://Gist.github.com/zeroarst/3b3f32092d58698a4568cdb0919c9a9
Fragment LiveWallFilterFragment(receiveing fragment)からFragment DashboardLiveWall(calling fragment)に結果を取得していますこのように...
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 {
}
}
}
}
}
公式文書によると:
このフラグメントのオプションのターゲット。これは、たとえば、このフラグメントが別のフラグメントによって開始され、完了時に最初のフラグメントに結果を返したい場合に使用できます。ここで設定したターゲットは、FragmentManager#putFragmentを介してインスタンス間で保持されます。
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");
}
}
...
}
これを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