1. ホーム
  2. android

[解決済み] Jelly Bean DatePickerDialog --- キャンセルする方法はありますか?

2022-05-02 02:02:23

質問

--- モデレーターへの注意事項 今日(7月15日)、すでにこの問題に直面している人がいることに気づきました。 こちら . しかし、私は問題についてより良い説明を提供したと思うので、これを重複として閉じることが適切であるかどうかわからない。他の質問を編集して、そこにこの内容を貼り付けるべきかどうかわかりませんが、他人の質問をあまり変えるのは気が引けます。 ---

私は何かを持っている ここで

この問題は、どのSDKをベースにビルドするかには依存しないと思います。問題なのはデバイスOSのバージョンです。

問題点その1:デフォルトでの不整合

DatePickerDialog がJelly Beanで変更(?)され、現在は 完了 ボタンがあります。以前のバージョンでは キャンセル というボタンがあり、ユーザーエクスペリエンスに影響を与える可能性があります(矛盾、以前のAndroidバージョンからのマッスルメモリー)。

複製する。 基本的なプロジェクトを作成します。これを onCreate :

DatePickerDialog picker = new DatePickerDialog(
        this,
        new OnDateSetListener() {
            @Override
            public void onDateSet(DatePicker v, int y, int m, int d) {
                Log.d("Picker", "Set!");
            }
        },
        2012, 6, 15);
picker.show();

期待されること A キャンセル ボタンをダイアログに表示します。

現在 A キャンセル ボタンは表示されません。

スクリーンショットをご覧ください。 4.0.3 (OK)と 4.1.1 (おそらく間違っている?)。

問題点その2:解雇の動作がおかしい

ダイアログは、実際に呼び出すべきリスナーを呼び出し、その後 常に を呼び出します。 OnDateSetListener リスナーです。キャンセルしてもsetメソッドは呼び出され、設定しても2回メソッドが呼び出されます。

複製する。 1のコードを使用し、以下のコードを追加してください(これで1が解決することがわかりますが、視覚的/UI的にしか解決しません)。

picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("Picker", "Cancel!");
            }
        });

期待されること

  • BACKキーを押すか、ダイアログの外側をクリックすると、次のようになります。 何もしない .
  • キャンセル]を押すと、次のように表示されます。 ピッカーはキャンセルしてください .
  • Set"を押すと、次のように表示されます。 ピッカーがセットされました .

現在

  • BACKキーを押すか、ダイアログの外側をクリックすると、印刷されます。 ピッカーセット! .
  • キャンセルボタンを押すと印刷されます。 ピッカーがキャンセルされました。 そして ピッカーセット! .
  • Set"を押すと表示されます。 ピッカーセット! そして ピッカーセット! .

挙動を示すログ行。

07-15 12:00:13.415: D/Picker(21000): Set!

07-15 12:00:24.860: D/Picker(21000): Cancel!
07-15 12:00:24.876: D/Picker(21000): Set!

07-15 12:00:33.696: D/Picker(21000): Set!
07-15 12:00:33.719: D/Picker(21000): Set!

その他の注意事項

  • で囲むと DatePickerFragment は重要ではありません。問題を単純化しましたが、テストしてみました。

どのように解決するのですか?

注意 Lollipop時点で修正済み , ソースはこちら . 自動化された クライアントで使用するためのクラス (Androidの全バージョンに対応)を更新しました。

TL;DR: 1-2-3の死んだように簡単なステップで、グローバルなソリューションを実現します。

  1. ダウンロード これ クラスがあります。
  2. 実装 OnDateSetListener をアクティビティに追加します (または、ニーズに合わせてクラスを変更します)。
  3. このコードでダイアログをトリガーします(このサンプルでは Fragment ):

    Bundle b = new Bundle();
    b.putInt(DatePickerDialogFragment.YEAR, 2012);
    b.putInt(DatePickerDialogFragment.MONTH, 6);
    b.putInt(DatePickerDialogFragment.DATE, 17);
    DialogFragment picker = new DatePickerDialogFragment();
    picker.setArguments(b);
    picker.show(getActivity().getSupportFragmentManager(), "frag_date_picker");
    
    

そして、それだけでいいんです! 私がまだ私の回答をquot;accepted"としている理由は、クライアントコードのフットプリントが非常に小さいこと、根本的な問題(フレームワーククラスでリスナーが呼ばれること)に対処していること、設定の変更に関わらず正常に動作すること、このバグに悩まされない以前のAndroidバージョンのデフォルト実装にコードロジックをルーティングすること(クラスのソース参照)から、私がこの解決法を好むためです。

オリジナルの回答(歴史的、教訓的な理由から保管されています)。

バグ元

OK、確かにバグで、すでに誰かが埋めているようだ。 課題34833 .

この問題は、おそらく DatePickerDialog.java . と書かれているところ。

private void tryNotifyDateSet() {
    if (mCallBack != null) {
        mDatePicker.clearFocus();
        mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
                mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
    }
}

@Override
protected void onStop() {
    tryNotifyDateSet();
    super.onStop();
}

そうだったかもしれませんね。

@Override
protected void onStop() {
    // instead of the full tryNotifyDateSet() call:
    if (mCallBack != null) mDatePicker.clearFocus();
    super.onStop();
}

さて、どなたかAndroidにパッチ/バグレポートを提案する方法を教えていただけると幸いです。一方、私は可能性のある修正(単純なもの)を、添付のバージョンとして提案しました。 DatePickerDialog.java を、そこのIssueに追加してください。

不具合回避のための考え方

リスナーを設定する null をコンストラクタで作成し、独自の BUTTON_POSITIVE ボタンは後で . 以上、詳細は下記になります。

問題が発生するのは DatePickerDialog.java は、ソースを見ればわかるように、グローバル変数( mCallBack ) には、コンストラクタで渡されたリスナーが格納されています。

    /**
 * @param context The context the dialog is to run in.
 * @param callBack How the parent is notified that the date is set.
 * @param year The initial year of the dialog.
 * @param monthOfYear The initial month of the dialog.
 * @param dayOfMonth The initial day of the dialog.
 */
public DatePickerDialog(Context context,
        OnDateSetListener callBack,
        int year,
        int monthOfYear,
        int dayOfMonth) {
    this(context, 0, callBack, year, monthOfYear, dayOfMonth);
}

    /**
 * @param context The context the dialog is to run in.
 * @param theme the theme to apply to this dialog
 * @param callBack How the parent is notified that the date is set.
 * @param year The initial year of the dialog.
 * @param monthOfYear The initial month of the dialog.
 * @param dayOfMonth The initial day of the dialog.
 */
public DatePickerDialog(Context context,
        int theme,
        OnDateSetListener callBack,
        int year,
        int monthOfYear,
        int dayOfMonth) {
    super(context, theme);

    mCallBack = callBack;
    // ... rest of the constructor.
}

そこで、仕掛けとしては null リスナーとして格納し、独自のボタンセットをロールバックします (以下は、#1 のオリジナルコードを更新したものです)。

    DatePickerDialog picker = new DatePickerDialog(
        this,
        null, // instead of a listener
        2012, 6, 15);
    picker.setCancelable(true);
    picker.setCanceledOnTouchOutside(true);
    picker.setButton(DialogInterface.BUTTON_POSITIVE, "OK",
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("Picker", "Correct behavior!");
            }
        });
    picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("Picker", "Cancel!");
            }
        });
picker.show();

これで、上に投稿したような修正が可能なので、うまくいくでしょう。

また DatePickerDialog.java をチェックします。 null を読み取るたびに mCallback ( API 3/1.5の時代から、どうやら ---もちろんHoneycombは確認できません)、例外が発生することはありません。 ロリポップが問題を修正したことを考えると、私はそれを調べるつもりはありません: ただ、デフォルトの実装(私が提供したクラスでカバーされています)を使用してください。

最初、私は clearFocus() しかし、ここでテストしたところ、Logの行はきれいでした。だから、私が提案した行は、結局必要ないかもしれませんが、私は知りません。

以前のAPIレベルとの互換性 (編集)

下のコメントで指摘したように、あれはコンセプトであって、できるのは 私が使用しているクラスをGoogle Driveのアカウントからダウンロードします。 . 私が使った方法では、バグの影響を受けていないバージョンでは、デフォルトのシステム実装が使われます。

クライアントクラスのボイラープレートコードを最小限に減らしたかったので、私のニーズに適したいくつかの仮定(ボタン名など)を取りました。完全な使用例です。

class YourActivity extends SherlockFragmentActivity implements OnDateSetListener

// ...

Bundle b = new Bundle();
b.putInt(DatePickerDialogFragment.YEAR, 2012);
b.putInt(DatePickerDialogFragment.MONTH, 6);
b.putInt(DatePickerDialogFragment.DATE, 17);
DialogFragment picker = new DatePickerDialogFragment();
picker.setArguments(b);
picker.show(getActivity().getSupportFragmentManager(), "fragment_date_picker");