Android DialogFragmentとDialog


244

を使用して、DialogFragmentシンプルではなくを使用することをお勧めしますが、シンプルな[はい/いいえ]の確認メッセージボックスに分離を使用するのはばかげています。この場合のベストプラクティスは何ですか?DialogFragments APIDialogFragment


5
簡単に言うと、とりわけ、単純な、DialogまたはAlertDialog.Builder::create()::show()画面を回転させると消えるダイアログを作成します。
user1032613

回答:


83

はい、使用します。DialogFragmentそして、onCreateDialogとにかく単にAlertDialogビルダーを使用して、AlertDialogはい/いいえ確認ボタン付きのシンプルなものを作成できます。コードはほとんどありません。

フラグメント内のイベントの処理に関しては、さまざまな方法がありますが、メッセージHandlerをmy Fragmentで定義DialogFragmentし、コンストラクターを介してviaに渡し、さまざまなクリックイベントに応じてメッセージをフラグメントのハンドラーに返します。繰り返しになりますが、次の方法でうまくいきます。

ダイアログでメッセージを保持し、コンストラクタでインスタンス化します。

private Message okMessage;
...
okMessage = handler.obtainMessage(MY_MSG_WHAT, MY_MSG_OK);

onClickListenerダイアログにを実装し、必要に応じてハンドラーを呼び出します。

public void onClick(.....
    if (which == DialogInterface.BUTTON_POSITIVE) {
        final Message toSend = Message.obtain(okMessage);
        toSend.sendToTarget();
    }
 }

編集する

ASとMessageparcelableであるあなたは、それを保存することができますonSaveInstanceStateし、それを復元します

outState.putParcelable("okMessage", okMessage);

次に onCreate

if (savedInstanceState != null) {
    okMessage = savedInstanceState.getParcelable("okMessage");
}

4
問題はokMes​​sageではありません-問題はokMes​​sageの問題でありtarget、バンドルからロードするとnullになります。Messageのターゲットがnullであり、を使用sendToTargetすると、Messageがnullではなく、そのターゲットが原因で、NullPointerExceptionが発生します。
hrnt

2
Dialogの代わりにDialogFragmentを使用する利点は何ですか?
Raphael Petegrosso

79
DialogFragmentを使用する利点は、ダイアログのすべてのライフサイクルが自動的に処理されることです。「ダイアログがリークしました...」というエラーは二度と発生しません。DialogFragmentに移動し、Dialogsを忘れます。
Snicolas 2013年

6
コンストラクタを介してokMes​​sageを渡す代わりに、setArguments()とgetArguments()を使用する必要があると思います。
pjv 2013年

1
まあ私はビルダーをかなり簡単に使用し、私はこのandroid:configChanges = "locale | keyboardHidden | orientation | screenSize"を使用してアクティビティ管理を処理し、アプリケーションに問題はありません...
Renetik

67

YesNoDialogやOkDialogなどの一般的なDialogFragmentサブクラスを作成し、アプリでダイアログを頻繁に使用する場合は、タイトルとメッセージを渡すことができます。

public class YesNoDialog extends DialogFragment
{
    public static final String ARG_TITLE = "YesNoDialog.Title";
    public static final String ARG_MESSAGE = "YesNoDialog.Message";

    public YesNoDialog()
    {

    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        Bundle args = getArguments();
        String title = args.getString(ARG_TITLE);
        String message = args.getString(ARG_MESSAGE);

        return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_CANCELED, null);
                }
            })
            .create();
    }
}

次に、以下を使用して呼び出します。

    DialogFragment dialog = new YesNoDialog();
    Bundle args = new Bundle();
    args.putString(YesNoDialog.ARG_TITLE, title);
    args.putString(YesNoDialog.ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(this, YES_NO_CALL);
    dialog.show(getFragmentManager(), "tag");

そして、結果をで処理しonActivityResultます。


はい、DialogFragmentはすべてのライフサイクルイベントを処理します。
ashishduh 2014年

1
ローテーション後も古いダイアログがまだ存在し、古い存在しないフラグメント(dialog.setTargetFragment(this、YES_NO_CALL);)に割り当てられたままなので、ローテーション後にgetTargetFragment()。onActivityResultが機能しないため、そうではないと思います
Malachiasz

7
YES_NO_CALLgetFragmentManager()onActivityResult
msysmilu 2015年

2
YES_NO_CALLリクエストコードであるカスタムintです。 getFragmentManager()アクティビティのフラグメントマネージャを取得し onActivityResult()ます。これはフラグメントライフサイクルコールバックメソッドです。
ashishduh 2015年

3
getFragmentManager()をgetSupportFragmentManager();に置き換えます。
Avinash Verma 2017年

33

AlertDialogではなくDialogFragmentを使用します。


  • APIレベル13の導入以来

    Activity のshowDialogメソッドは非推奨です。コード内の別の場所でダイアログを呼び出すことはお勧めしません。ダイアログを自分で管理する必要があるためです(方向の変更など)。

  • 差分DialogFragment-AlertDialog

    彼らはそんなに違うのですか?DialogFragmentに関するAndroidリファレンスから:

    DialogFragmentは、アクティビティのウィンドウの上に浮かぶダイアログウィンドウを表示するフラグメントです。このフラグメントには、フラグメントの状態に基づいて適切に表示されるDialogオブジェクトが含まれています。ダイアログの制御(表示、非表示、閉じるタイミングの決定)は、ダイアログを直接呼び出すのではなく、ここのAPIを介して行う必要があります。

  • その他の注意事項

    • フラグメントは、画面サイズの異なるデバイスの多様性により、Androidフレームワークの自然な進化です。
    • DialogFragmentsおよびFragmentsは、現在使用されているすべてのバージョンのAndroidでクラスを使用できるようにするサポートライブラリで使用できます。

28

の使用をお勧めしDialogFragmentます。

確かに、「はい/いいえ」ダイアログを作成することは、かなり単純なタスクであることを考えるとかなり複雑ですが、同様のダイアログボックスを作成するDialogことも驚くほど複雑です。

(アクティビティのライフサイクルは複雑になりActivityます-ダイアログボックスのライフサイクルを管理する必要があります-そして、Activity.showDialog8未満のAPIレベルを使用している場合、カスタムメッセージ(カスタムメッセージなど)を渡す方法はありません)

良い点は、通常、DialogFragmentかなり簡単に独自の抽象化を構築できることです。


警告ダイアログのコールバックをどのように処理しますか(はい、いいえ)?
Alexey Zakharov

最も簡単な方法は、Stringパラメーターを取るホストアクティビティにメソッドを実装することです。たとえば、ユーザーが「はい」をクリックすると、ダイアログは「同意する」パラメータでアクティビティのメソッドを呼び出します。これらのパラメーターは、ダイアログを表示するときに指定されます。
2011年

5
しかし、私はアクティビティではなくフラグメント内のコールバックが必要です。setTargetFragmentを使用して、インターフェースにキャストできます。しかし、それは地獄です。
Alexey Zakharov

また、ターゲットにタグを設定して使用することにより目的の断片をフェッチすることができFragmentManagerさんをfindFragmentByTag。しかし、そうです、かなりのコードが必要です。
hrnt

@AlexeyZakharov私は、これは後半5年程度である知っていますが、渡すことができますFragment this し、あなたの持っているActivity extendsあなたにInterface。ただし、スレッド処理に注意してください。同時実行性が確認されていない場合、必ずしも必要ではないときにインターフェイス呼び出しをポップオフする可能性があります。これがメモリと循環依存スパゲッティで何をするのかわからないのですが、他の誰かが口に入れたいのでしょうか?他のオプションはMessage/ですHandlerが、それでも並行性の問題がある可能性があります。
トリックノロジー2016年

8

ビルダーパターンを使用した一般的なAlertDialogFragment

私のプロジェクトでは、問題があることに気づく前にAlertDialog.Builderすでに多くのことをすでに使用しています。ただし、アプリのどこにもそれほど多くのコードを変更したくありませんでした。さらに、ダイアログフラグメントとホルダーフラグメント間で通信するために何千ものコールバックメソッドを実装OnClickListenersする必要がなく、必要な場所で(つまり、を使用するsetPositiveButton()場合setNegativeButton()など)匿名クラスとして渡すのが本当に好きです。私の意見では、非常に複雑で複雑なコードにつながります。特に、1つのフラグメントに複数の異なるダイアログがあり、現在表示されているダイアログをコールバック実装で区別する必要がある場合。

したがって、私は、一般的な作成するために、異なるアプローチを組み合わせてAlertDialogFragment使用することができますヘルパークラスのように正確に AlertDialog


解決

PLEASE NOTEあなたが使用していない場合は、コードの一部を変更する必要がある場合がありますので、私は、私のコードでJava 8のラムダ式を使用していますことをラムダ式をまだ。)

/**
 * Helper class for dialog fragments to show a {@link AlertDialog}. It can be used almost exactly
 * like a {@link AlertDialog.Builder}
 * <p />
 * Creation Date: 22.03.16
 *
 * @author felix, http://flx-apps.com/
 */
public class AlertDialogFragment extends DialogFragment {
    protected FragmentActivity activity;
    protected Bundle args;
    protected String tag = AlertDialogFragment.class.getSimpleName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activity = getActivity();
        args = getArguments();
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = setDialogDefaults(new AlertDialog.Builder(getActivity())).create();

        if (args.containsKey("gravity")) {
            dialog.getWindow().getAttributes().gravity = args.getInt("gravity");
        }

        dialog.setOnShowListener(d -> {
            if (dialog != null && dialog.findViewById((android.R.id.message)) != null) {
                ((TextView) dialog.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
            }
        });
        return dialog;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);

        if (args.containsKey("onDismissListener")) {
            Parcelable onDismissListener = args.getParcelable("onDismissListener");
            if (onDismissListener != null && onDismissListener instanceof ParcelableOnDismissListener) {
                ((ParcelableOnDismissListener) onDismissListener).onDismiss(this);
            }
        }
    }

    /**
     * Sets default dialog properties by arguments which were set using {@link #builder(FragmentActivity)}
     */
    protected AlertDialog.Builder setDialogDefaults(AlertDialog.Builder builder) {
        args = getArguments();
        activity = getActivity();

        if (args.containsKey("title")) {
            builder.setTitle(args.getCharSequence("title"));
        }

        if (args.containsKey("message")) {
            CharSequence message = args.getCharSequence("message");
            builder.setMessage(message);
        }

        if (args.containsKey("viewId")) {
            builder.setView(getActivity().getLayoutInflater().inflate(args.getInt("viewId"), null));
        }

        if (args.containsKey("positiveButtonText")) {
            builder.setPositiveButton(args.getCharSequence("positiveButtonText"), (dialog, which) -> {
                onButtonClicked("positiveButtonListener", which);
            });
        }

        if (args.containsKey("negativeButtonText")) {
            builder.setNegativeButton(args.getCharSequence("negativeButtonText"), (dialog, which) -> {
                onButtonClicked("negativeButtonListener", which);
            });
        }

        if (args.containsKey("neutralButtonText")) {
            builder.setNeutralButton(args.getCharSequence("neutralButtonText"), (dialog, which) -> {
                onButtonClicked("neutralButtonListener", which);
            });
        }

        if (args.containsKey("items")) {
            builder.setItems(args.getStringArray("items"), (dialog, which) -> {
                onButtonClicked("itemClickListener", which);
            });
        }

        // @formatter:off
        // FIXME this a pretty hacky workaround: we don't want to show the dialog if onClickListener of one of the dialog's button click listener were lost
        //       the problem is, that there is no (known) solution for parceling a OnClickListener in the long term (only for state changes like orientation change,
        //       but not if the Activity was completely lost)
        if (
                (args.getParcelable("positiveButtonListener") != null && !(args.getParcelable("positiveButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("negativeButtonListener") != null && !(args.getParcelable("negativeButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("neutralButtonListener") != null && !(args.getParcelable("neutralButtonListener") instanceof ParcelableOnClickListener)) ||
                (args.getParcelable("itemClickListener") != null && !(args.getParcelable("itemClickListener") instanceof ParcelableOnClickListener))
        ) {
            new DebugMessage("Forgot onClickListener. Needs to be dismissed.")
                    .logLevel(DebugMessage.LogLevel.VERBOSE)
                    .show();
            try {
                dismissAllowingStateLoss();
            } catch (NullPointerException | IllegalStateException ignored) {}
        }
        // @formatter:on

        return builder;
    }

    public interface OnDismissListener {
        void onDismiss(AlertDialogFragment dialogFragment);
    }

    public interface OnClickListener {
        void onClick(AlertDialogFragment dialogFragment, int which);
    }

    protected void onButtonClicked(String buttonKey, int which) {
        ParcelableOnClickListener parcelableOnClickListener = getArguments().getParcelable(buttonKey);
        if (parcelableOnClickListener != null) {
            parcelableOnClickListener.onClick(this, which);
        }
    }

    // region Convenience Builder Pattern class almost similar to AlertDialog.Builder
    // =============================================================================================

    public AlertDialogFragment builder(FragmentActivity activity) {
        this.activity = activity;
        this.args = new Bundle();
        return this;
    }

    public AlertDialogFragment addArguments(Bundle bundle) {
        args.putAll(bundle);
        return this;
    }

    public AlertDialogFragment setTitle(int titleStringId) {
        return setTitle(activity.getString(titleStringId));
    }

    public AlertDialogFragment setTitle(CharSequence title) {
        args.putCharSequence("title", title);
        return this;
    }

    public AlertDialogFragment setMessage(int messageStringId) {
        return setMessage(activity.getString(messageStringId));
    }

    public AlertDialogFragment setMessage(CharSequence message) {
        args.putCharSequence("message", message);
        return this;
    }

    public AlertDialogFragment setPositiveButton(int textStringId, OnClickListener onClickListener) {
        return setPositiveButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setPositiveButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("positiveButtonText", text);
        args.putParcelable("positiveButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNegativeButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNegativeButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNegativeButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("negativeButtonText", text);
        args.putParcelable("negativeButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setNeutralButton(int textStringId, AlertDialogFragment.OnClickListener onClickListener) {
        return setNeutralButton(activity.getString(textStringId), onClickListener);
    }

    public AlertDialogFragment setNeutralButton(CharSequence text, AlertDialogFragment.OnClickListener onClickListener) {
        args.putCharSequence("neutralButtonText", text);
        args.putParcelable("neutralButtonListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setOnDismissListener(OnDismissListener onDismissListener) {
        if (onDismissListener == null) {
            return this;
        }

        Parcelable p = new ParcelableOnDismissListener() {
            @Override
            public void onDismiss(AlertDialogFragment dialogFragment) {
                onDismissListener.onDismiss(dialogFragment);
            }
        };
        args.putParcelable("onDismissListener", p);
        return this;
    }

    public AlertDialogFragment setItems(String[] items, AlertDialogFragment.OnClickListener onClickListener) {
        args.putStringArray("items", items);
        args.putParcelable("itemClickListener", createParcelableOnClickListener(onClickListener));
        return this;
    }

    public AlertDialogFragment setView(int viewId) {
        args.putInt("viewId", viewId);
        return this;
    }

    public AlertDialogFragment setGravity(int gravity) {
        args.putInt("gravity", gravity);
        return this;
    }

    public AlertDialogFragment setTag(String tag) {
        this.tag = tag;
        return this;
    }

    public AlertDialogFragment create() {
        setArguments(args);
        return AlertDialogFragment.this;
    }

    public AlertDialogFragment show() {
        create();
        try {
            super.show(activity.getSupportFragmentManager(), tag);
        }
        catch (IllegalStateException e1) {

            /**
             * this whole part is used in order to attempt to show the dialog if an
             * {@link IllegalStateException} was thrown (it's kinda comparable to
             * {@link FragmentTransaction#commitAllowingStateLoss()} 
             * So you can remove all those dirty hacks if you are sure that you are always
             * properly showing dialogs in the right moments
             */

            new DebugMessage("got IllegalStateException attempting to show dialog. trying to hack around.")
                    .logLevel(DebugMessage.LogLevel.WARN)
                    .exception(e1)
                    .show();

            try {
                Field mShownByMe = DialogFragment.class.getDeclaredField("mShownByMe");
                mShownByMe.setAccessible(true);
                mShownByMe.set(this, true);
                Field mDismissed = DialogFragment.class.getDeclaredField("mDismissed");
                mDismissed.setAccessible(true);
                mDismissed.set(this, false);
            }
            catch (Exception e2) {
                new DebugMessage("error while showing dialog")
                        .exception(e2)
                        .logLevel(DebugMessage.LogLevel.ERROR)
                        .show();
            }
            FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
            transaction.add(this, tag);
            transaction.commitAllowingStateLoss(); // FIXME hacky and unpredictable workaround
        }
        return AlertDialogFragment.this;
    }

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        throw new NoSuchMethodError("Please use AlertDialogFragment.show()!");
    }

    protected ParcelableOnClickListener createParcelableOnClickListener(AlertDialogFragment.OnClickListener onClickListener) {
        if (onClickListener == null) {
            return null;
        }

        return new ParcelableOnClickListener() {
            @Override
            public void onClick(AlertDialogFragment dialogFragment, int which) {
                onClickListener.onClick(dialogFragment, which);
            }
        };
    }

    /**
     * Parcelable OnClickListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnClickListener extends ResultReceiver implements AlertDialogFragment.OnClickListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnClickListener() {
            super(null);
        }

        @Override
        public abstract void onClick(AlertDialogFragment dialogFragment, int which);
    }

    /**
     * Parcelable OnDismissListener (can be remembered on screen rotation)
     */
    public abstract static class ParcelableOnDismissListener extends ResultReceiver implements AlertDialogFragment.OnDismissListener {
        public static final Creator<ResultReceiver> CREATOR = ResultReceiver.CREATOR;

        ParcelableOnDismissListener() {
            super(null);
        }

        @Override
        public abstract void onDismiss(AlertDialogFragment dialogFragment);
    }


    // =============================================================================================
    // endregion
}

使用法

// showing a normal alert dialog with state loss on configuration changes (like device rotation)
new AlertDialog.Builder(getActivity())
        .setTitle("Are you sure? (1)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

// showing a dialog fragment using the helper class with no state loss on configuration changes
new AlertDialogFragment.builder(getActivity())
        .setTitle("Are you sure? (2)")
        .setMessage("Do you really want to do this?")
        .setPositiveButton("Yes", (dialog, which) -> Toast.makeText(getContext(), "Yes clicked", Toast.LENGTH_SHORT).show())
        .setNegativeButton("Cancel", null)
        .show();

私がこれをここに投稿しているのは、私の解決策を共有するためだけでなく、皆さんにあなたの意見を求めたかったからでもあります。


3
これは非常に興味深いアイデアですが、API設計は機能しないと思います。OnClickListenerをsetPositiveButton()に渡した場合、デバイスが回転してフラグメントがバンドル引数から再作成されると、OnClickListenerはParcelableから適切に再作成されません。基本的な問題は、ローテーション中にリスナーを再作成できないことですが、APIインターフェース(インターフェースを取る)はリスナーを要求します。これが当てはまらないといいのですが(私はこのアイデアが好きです)。
Xargs 2016年

1
いい考えですが、@ Xargsが言うように、機能しません。渡されたリスナーは、ローテーション時に正しく再作成されません。
Graham Borland 2016年

私の結果は、実際には回転やアプリの再開(ホーム画面に移動した後など)で機能しますが、アクティビティが完全に破棄された後に復元された場合(OnClickListenerは実際に失われます)では機能しません。(Android 4.4.4およびAndroid 5.1.1でテスト済み)
flxapps 2017

私はこの正確な実装をテストしていませんが、テストした結果から、フラグメントバンドルに渡されたパーセル可能なリスナーは再作成時に正しく呼び出されます。理由はわかりませんが、うまくいくようです。
Saad Farooq 2017年

@flxapps、カスタムビューの場合、どのようにして子ビューを取得し、それらのプロパティを変更するか、リスナーを適用できますか?クラスではダイアログインスタンスを返さないため、
誰かが子

5

@ashishduhの答えを少し単純化することをお勧めします。

public class AlertDialogFragment extends DialogFragment {
public static final String ARG_TITLE = "AlertDialog.Title";
public static final String ARG_MESSAGE = "AlertDialog.Message";

public static void showAlert(String title, String message, Fragment targetFragment) {
    DialogFragment dialog = new AlertDialogFragment();
    Bundle args = new Bundle();
    args.putString(ARG_TITLE, title);
    args.putString(ARG_MESSAGE, message);
    dialog.setArguments(args);
    dialog.setTargetFragment(targetFragment, 0);
    dialog.show(targetFragment.getFragmentManager(), "tag");
}

public AlertDialogFragment() {}

@NonNull
@Override
public AlertDialog onCreateDialog(Bundle savedInstanceState)
{
    Bundle args = getArguments();
    String title = args.getString(ARG_TITLE, "");
    String message = args.getString(ARG_MESSAGE, "");

    return new AlertDialog.Builder(getActivity())
            .setTitle(title)
            .setMessage(message)
            .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, null);
                }
            })
            .create();
}

これにより、(クラスの)ユーザーがコンポーネントの内部に精通する必要がなくなり、使用が非常に簡単になります。

AlertDialogFragment.showAlert(title, message, this);

PS私の場合、私が作成したのはシンプルなアラートダイアログが必要だったためです。このアプローチは、はい/いいえまたは必要なその他のタイプに適用できます。


1

単純な「はい」または「いいえ」ダイアログにはDialogを使用します。

oncreate、requestアクセス許可などのライフサイクルを把握する必要があるより複雑なビューが必要な場合は、ライフサイクルのオーバーライドを行うためにダイアログフラグメントを使用します。したがって、呼び出し側のアクティビティと通信することなくダイアログが動作するために必要なアクセス許可とその他のコードを分離します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.