AndroidでFragmentの結果をActivityで受け取る

Fragment から Activity へ結果を返す(コールバック)したいケースは多いと思います。

 

真っ先に思い付く方法は、リスナーインターフェースを作成して Activity でそれを実装し、Fragmentからそのメソッドを実行してやる事だと思いますが、この方法には注意が必要です。

 

 

Activityは一度破棄されて再作成されるというプロセスを辿ることが意外とあります。

例えば端末を傾けた時、縦画面だった Activity は破棄されて横画面用の Activity が再作成されたり。

他にも、他のアプリを起動してバックグラウンドに行ったときメモリの状況によっては Activity が破棄される事があります。

 

Activityは(Fragmentも)破棄され再作成されるものと思ってコーディングする必要があります。

良くない例

FragmentResultListener

public interface FragmentResultListener {
    void onFragmentResult();
}

TestFragment

public class TestFragment extends DialogFragment {
    private FragmentResultListener mListener;



    public static TestFragment newInstance(FragmentResultListener listener) {
        TestFragment fragment = new TestFragment();
        mListener = listener;
        return fragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog);

        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                mListener.onFragmentResult();
            }
        });
        return builder.create();
    }
}

MainActivity

public class MainActivity extends Activity implements FragmentResultListener {

         :
         :

    private void test() {
        TestFragment fragment = TestFragment.newInstance(this);
        fragment.show(getFragmentManager(), "TestFragment");
    }

    public void onFragmentResult() {
         :
    }
}

この例では TestFragment の新しいインスタンスを生成する為の static なメソッドを用意していて、そこからリスナーをセットするようにしています。

 

通常はうまく動くような気がします。

但し、端末を傾けるなどして Activity が再作成されると (Fragmentも再作成される)  TestFragment が持っている mListener メンバ参照先がセットされないままになってしまいます。

 

簡単だけど汎用性に欠ける例

Fragment には getActivity() メソッドというものがあります。

これを使えば Activity が再作成された後であっても、意図した Activity を取得する事が出来ます。

 

TestFragment

public class TestFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog);

        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                MainActivity activity = (MainActivity)getActivity();
                activity.onFragmentResult();
            }
        });
        return builder.create();
    }
}

MainActivity

public class MainActivity extends Activity {

         :
         :

    private void test() {
        TestFragment fragment = new TestFragment();
        fragment.show(getFragmentManager(), "TestFragment");
    }

    public void onFragmentResult() {
         :
    }
}

getActivity()で Activity を取得する事でコールバックを確実に行えるようになります。(9~10行目)

しかしながら、取得した Activity を MainActivity にキャストしている為、汎用性はありません。

この Fragment が MainActivity からしか使われないならこれで良いかもしれません。

こんな風にしてみた

Activity には Intent を使い結果を受け取る仕組みがありますが、それを参考にアラートを表示するフラグメントの仕組みを作ってみました。

 

参考:AndroidでActivityから結果を受け取る

 

DialogFragmentResultListener

public interface DialogFragmentResultListener {
    void onDialogFragmentResult(int requestCode, int resultCode, Object data);
}

AlertDialogFragment

public class AlertDialogFragment extends DialogFragment {
    public static AlertDialogFragment newInstance(int targetRequestCode, String title, String message, String positiveBtn, String negativeBtn) {
        Bundle args = new Bundle();
        args.putSerializable("TITLE", title);
        args.putSerializable("MESSAGE", message);
        args.putSerializable("POSITIVEBUTTON", positiveBtn);
        args.putSerializable("NEGATIVEBUTTON", negativeBtn);

        AlertDialogFragment fragment = new AlertDialogFragment();
        fragment.setArguments(args);
        fragment.setTargetFragment(null, targetRequestCode);
        return fragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle args = getArguments();
        String title = args.getString("TITLE");
        String message = args.getString("MESSAGE");
        String positiveBtn = args.getString("POSITIVEBUTTON");
        String negativeBtn = args.getString("NEGATIVEBUTTON");

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppTheme_AlertDialog);

        builder.setTitle(title);
        builder.setIcon(android.R.drawable.ic_dialog_info);
        builder.setMessage(message);
        if (null != positiveBtn) {
            builder.setPositiveButton(positiveBtn, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    Activity activity = getActivity();
                    if (activity instanceof DialogFragmentResultListener) {
                        DialogFragmentResultListener listener = (DialogFragmentResultListener)activity;
                        listener.onDialogFragmentResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                    }
                }
            });
        }
        if (null != negativeBtn) {
            builder.setNegativeButton(negativeBtn, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    Activity activity = getActivity();
                    if (activity instanceof DialogFragmentResultListener) {
                        DialogFragmentResultListener listener = (DialogFragmentResultListener)activity;
                        listener.onDialogFragmentResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
                    }
                }
            });
        }
        return builder.create();
    }
}

MainActivity

public class MainActivity extends Activity implements DialogFragmentResultListener {
    //onDialogFragmentResultへ戻すRequestCode
    private static final int FRAGMENTREQUEST_TEST = 1;


         :
         :

    private void test() {
        String title   = getResources().getString(R.string.alert_title);
        String message = getResources().getString(R.string.alert_message);
        String yesbtn  = getResources().getString(R.string.alert_button_yes);
        String nobtn   = getResources().getString(R.string.alert_button_no);

        AlertDialogFragment fragment = AlertDialogFragment.newInstance(FRAGMENTREQUEST_TEST, title, message, yesbtn, nobtn);
        fragment.show(getSupportFragmentManager(), "AlertDialogFragment");
    }

    public void onDialogFragmentResult(int requestCode, int resultCode, Object data) {
        switch (requestCode) {
            case FRAGMENTREQUEST_TEST:
                  :
                break;
        }
    }
}