Jelly Bean DatePickerDialog —キャンセルする方法はありますか?


148

--- モデレーターへの注意:今日(7月15日)、私は誰かがすでにこの問題に直面したことに気付きましたここに。しかし、私は問題についてより良い説明を提供したと思うので、これを複製として閉じることが適切かどうかはわかりません。他の質問を編集して、そこにこのコンテンツを貼り付ける必要があるかどうかはわかりませんが、他の誰かの質問をあまりにも変更することに抵抗があります。---

ここに変なものがあります。

問題は、ビルドするSDKに依存するとは思わない。重要なのは、デバイスのOSバージョンです。

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

DatePickerDialogジェリービーンに(?)に変更となりましたのみ提供して完了を [ ]ボタンます。以前のバージョンには[ キャンセル ]ボタンが含まれており、これはユーザーエクスペリエンスに影響を与える可能性があります(以前の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!");
            }
        });

期待される:

  • 戻るキーを押すか、ダイアログの外側をクリックしても何も起こりません
  • 「キャンセル」を押すと、ピッカーのキャンセルが印刷されます
  • 「セット」を押すと、ピッカーセットが印刷されます

電流:

  • 戻るキーを押すか、ダイアログの外側をクリックすると、ピッカーセットが印刷されます。
  • 「キャンセル」を押すと、ピッカーキャンセルが印刷されます。そしてピッカーセット!
  • 「セット」を押すと、ピッカーセットが印刷されます。そしてピッカーセット!

動作を示すログ行:

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!

その他の注意事項とコメント

  • それをaで包むことDatePickerFragmentは重要ではありません。問題を単純化しましたが、テストしました。

おめでとうございます。Androidのバグを見つけたようです。ここから報告できます
マイケルハンプトン

1
非常によく書かれたバグレポート。コードをテスト実行しなくても完全に理解できます。
Cheok Yan Cheng 2012

6
みんなにこの問題に投票することをお勧めします!問題34833
Bradley

ボタンの機能をオーバーライドして、ダイアログの外側のタッチのためにボタンの機能が無効になったように動作させることはできませんか?
Karthik Balakrishnan 2013年

2
バグは2年後もまだオープンです...信じられない。
erdomester 2014年

回答:


115

注:ロリポップのよう固定ここではソースクライアントで使用する自動化クラス(すべての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");

そして、それだけで十分です!クライアントコードでのフットプリントが非常に小さいため、ソリューションを好むため、私の答えを「受け入れられた」ままにしている理由は、基本的な問題(フレームワーククラスで呼び出されるリスナー)に対処し、構成の変更全体で正常に機能するためです。そして、このバグに悩まされていない以前の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そこにある問題の添付バージョンとして可能な修正(単純)を提案しました。

バグを回避するためのコンセプト

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.javanullいつでもチェックするのでmCallbackAPI 3 / 1.5の時代から ---もちろんハニカムをチェックできないようです)、それは例外をトリガーしません。Lollipopが問題を修正したことを考慮して、ここでは詳しく説明しません。デフォルトの実装を使用してください(提供したクラスでカバーされています)。

最初はを呼び出さないのが怖かったのですがclearFocus()、ここでテストしましたが、ログの行はきれいでした。したがって、私が提案したその行は結局必要ないかもしれませんが、私にはわかりません。

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

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

クライアントクラスのボイラープレートコードを最小限に抑えたかったので、自分のニーズに適したいくつかの仮定(ボタン名など)を採用しました。完全な使用例:

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");

11
研究の素晴らしい仕事!必要だったのは残念ですが、それでも素晴らしいことです。
CommonsWare

1
非常に素晴らしい!私は、テキストフィールドを更新し、正しいコールバックを呼び出す/呼び出さなかったこの問題について、壁に頭をぶつけてきました。ありがとうございました!提案された回避策が「将来の証拠」であるのか、それともバグが修正されたときに問題が発生すると思われるのでしょうか。
またがる

1
@RomainGuidoux最後に更新された回答を参照してください。リンク内のクラスには、そのメソッドをジェリーBeanでのみ呼び出すスマート機能があります。以下では、それをバイパスし、デフォルトのシステム実装を使用して、ondatesetのシステムコールをアクティビティにルーティングします。ジェリービーンでは、コールバックをルーティングする前に追加の措置(バグを回避する)が必要であり、そのハニカム+メソッドの呼び出しを伴います。しかし、これもJBでのみです。
davidcsb 2012

1
ここで素晴らしい仕事。この問題がどれほどばかげているかについての言葉はありません。
ビルフィリップス

5
TimePickerDialogはどうですか?TimePickerDialogにはgetTimePicker()がないようです
lokoko 2013

16

フラグメントを使用していない場合に備えて、David Cesarinoが投稿したソリューションに独自のリフを追加し、すべてのバージョン(2.1〜4.1)で簡単に修正できるようにします。

public class FixedDatePickerDialog extends DatePickerDialog {
  //I use a Calendar object to initialize it, but you can revert to Y,M,D easily
  public FixedDatePickerDialog(Calendar dateToShow, Context context, OnDateSetListener callBack) {
    super(context, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
    initializePicker(callBack);
  }

  public FixedDatePickerDialog(Calendar dateToShow, Context context, int theme,
    OnDateSetListener callBack) {
    super(context, theme, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
    initializePicker(callBack);
  }

  private void initializePicker(final OnDateSetListener callback) {
    try {
      //If you're only using Honeycomb+ then you can just call getDatePicker() instead of using reflection
      Field pickerField = DatePickerDialog.class.getDeclaredField("mDatePicker");
      pickerField.setAccessible(true);
      final DatePicker picker = (DatePicker) pickerField.get(this);
      this.setCancelable(true);
      this.setButton(DialogInterface.BUTTON_NEGATIVE, getContext().getText(android.R.string.cancel), (OnClickListener) null);
      this.setButton(DialogInterface.BUTTON_POSITIVE, getContext().getText(android.R.string.ok),
          new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
                picker.clearFocus(); //Focus must be cleared so the value change listener is called
                callback.onDateSet(picker, picker.getYear(), picker.getMonth(), picker.getDayOfMonth());
              }
          });
    } catch (Exception e) { /* Reflection probably failed*/ }
  }
}

ただ覚えておいてください:OKとキャンセルのためにボタン名を配線する場合は、ユーザー自身のものではなく、標準android.R.string.okandroid.R.string.cancelフィールドを使用する方が良いでしょう。そして返事をありがとう。
davidcsb 2012

ああ、いいですね。OKとキャンセルのテキストが表示されていることに気づきませんでした。ありがとう!
dmon

スーパーでのnullポインター例外の取得(コンテキスト、テーマ、null、dateToShow.get(YEAR)、dateToShow.get(MONTH)、dateToShow.get(DAY_OF_MONTH));
Kishore

エラー...ヌルを渡していますdateToShowか?もう1つのnullは実際には「修正」なので、そこにあるはずです。どちらのバージョンですか?
dmon

どちらimportを持っていたFieldか教えてください。6つのオプションがありますが、どれも機能しませんでした。
アディルマリク

8

バグが修正されるまで、DatePickerDialogまたはTimePickerDialogを使用しないことをお勧めします。TimePicker / DatePickerウィジェットでカスタムメイドのAlertDialogを使用します。

TimePickerDialogを変更します。

    final TimePicker timePicker = new TimePicker(this);
    timePicker.setIs24HourView(true);
    timePicker.setCurrentHour(20);
    timePicker.setCurrentMinute(15);

    new AlertDialog.Builder(this)
            .setTitle("Test")
            .setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", timePicker.getCurrentHour() + ":"
                            + timePicker.getCurrentMinute());
                }
            })
            .setNegativeButton(android.R.string.cancel,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog,
                                int which) {
                            Log.d("Picker", "Cancelled!");
                        }
                    }).setView(timePicker).show();

DatePickerDialogを変更します。

    final DatePicker datePicker = new DatePicker(this);
    datePicker.init(2012, 10, 5, null);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        datePicker.setCalendarViewShown(false);
    }

    new AlertDialog.Builder(this)
            .setTitle("Test")
            .setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", datePicker.getYear() + " "
                            + (datePicker.getMonth() + 1) + " "
                            + datePicker.getDayOfMonth());
                }
            })
            .setNegativeButton(android.R.string.cancel,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog,
                                int which) {
                            Log.d("Picker", "Cancelled!");
                        }
                    }).setView(datePicker).show();

4

David Cesarinoによるソリューションに基づいたTimePickerの1つ、「TL; DR:グローバルソリューションのための1-2-3の簡単なステップ」

TimePickerDialogは、DatePickerDialog.getDatePickerのような機能を提供しません。したがって、OnTimeSetListenerリスナーを提供する必要があります。DatePicker回避策ソリューションとの類似性を保つために、私は古いmListenerの概念を維持しました。必要に応じて変更できます。

呼び出しとリスナーは元のソリューションと同じです。含めるだけ

import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;

親クラスを拡張し、

... implements OnDateSetListener, OnTimeSetListener

実装する

 @Override
 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
 ...
 }

呼び出しの例

    Calendar cal = Calendar.getInstance();
    int hour = cal.get(Calendar.HOUR_OF_DAY);
    int minute = cal.get(Calendar.MINUTE);


    Bundle b = new Bundle();
    b.putInt(TimePickerDialogFragment.HOUR, hour);
    b.putInt(TimePickerDialogFragment.MINUTE, minute);

    DialogFragment picker = new TimePickerDialogFragment();
    picker.setArguments(b);
    picker.show(getSupportFragmentManager(), "frag_time_picker");

(キャンセルを処理するように更新されました)

public class TimePickerDialogFragment extends DialogFragment {

    public static final String HOUR = "Hour";
    public static final String MINUTE = "Minute";

    private boolean isCancelled = false; //Added to handle cancel
    private TimePickerDialog.OnTimeSetListener mListener;

    //Added to handle parent listener
    private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            if (!isCancelled)
            {
                mListener.onTimeSet(view,hourOfDay,minute);
            }
        }
    };
    //
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.mListener = (TimePickerDialog.OnTimeSetListener) activity;
    }

    @Override
    public void onDetach() {
        this.mListener = null;
        super.onDetach();
    }

    @TargetApi(11)
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle b = getArguments();
        int h = b.getInt(HOUR);
        int m = b.getInt(MINUTE);

        final TimePickerDialog picker = new TimePickerDialog(getActivity(), getConstructorListener(), h, m,DateFormat.is24HourFormat(getActivity()));

        //final TimePicker timePicker = new TimePicker(getBaseContext());
        if (hasJellyBeanAndAbove()) {
            picker.setButton(DialogInterface.BUTTON_POSITIVE,
                    getActivity().getString(android.R.string.ok),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            isCancelled = false; //Cancel flag, used in mTimeSetListener
                        }
                    });
            picker.setButton(DialogInterface.BUTTON_NEGATIVE,
                    getActivity().getString(android.R.string.cancel),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            isCancelled = true; //Cancel flag, used in mTimeSetListener
                        }
                    });
        }
        return picker;
    }
    private boolean hasJellyBeanAndAbove() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }

    private TimePickerDialog.OnTimeSetListener getConstructorListener() {
        return hasJellyBeanAndAbove() ? mTimeSetListener : mListener; //instead of null, mTimeSetListener is returned.
    }
}

s3 API 18
AbdelHady

3

誰かが簡単な回避策を望んでいる場合に備えて、ここに私が使用したコードがあります:

public void showCustomDatePicker () {

final DatePicker mDatePicker = (DatePicker) getLayoutInflater().
        inflate(R.layout.date_picker_view, null);
//Set an initial date for 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);
//Set the date now
mDatePicker.updateDate(year, month, day);

//create the dialog
AlertDialog.Builder mBuilder = new Builder(this);
//set the title
mBuilder.setTitle(getString(R.string.date_picker_title))
    //set our date picker
    .setView(mDatePicker)
    //set the buttons 
.setPositiveButton(android.R.string.ok, new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        //whatever method you choose to handle the date changes
            //the important thing to know is how to retrieve the data from the picker
        handleOnDateSet(mDatePicker.getYear(), 
                mDatePicker.getMonth(), 
                mDatePicker.getDayOfMonth());
    }
})
.setNegativeButton(android.R.string.cancel, new OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        dialog.dismiss();
    }
})
//create the dialog and show it.
.create().show();

}

ここで、layout.date_picker_viewは、唯一の要素であるため、DatePickerを使用した単純なレイアウトリソースです。

<!xml version="1.0" encoding="utf-8">
<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/date_picker"
android:layout_width="fill_parent"   
android:spinnersShown="true" 
android:calendarViewShown="false"
android:layout_height="fill_parent"/>

興味がある場合のための完全なチュートリアルです。


これは素晴らしく私のために働きました。理解しやすく、実装も簡単!4.4にテスト済み
2014年

3

私の簡単な解決策。もう一度発砲させたい場合は、「resetFired」を実行してください(たとえば、ダイアログを再度開いたとき)。

private class FixedDatePickerDialogListener implements DatePickerDialog.OnDateSetListener{
    private boolean fired;

    public void resetFired(){
        fired = false;
    }

    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (fired) {
            Log.i("DatePicker", "Double fire occurred.");
            return;//ignore and return.
        } 
        //put your code here to handle onDateSet
        fired = true;//first time fired 
    }
}

@AbdelHadyコードの編集は正しくありませんでしたが、わかりやすくするために編集しました。「isJellyBeanOrAbove()」メソッドを追加する必要はありません。利点はなく、不必要な複雑さを追加するだけです。resetFiredの呼び出しは安価です。
Daniel Ryan

ここでのコードは正しくありませonDateSetん。ダイアログが何らかの方法で閉じられているときに一度呼び出され、それが時間を設定することであった場合は、再度呼び出されるため、最初の呼び出しではなく2番目の呼び出しをキャッチする必要があります。あなたがしたように
AbdelHady 2014年

に関してはisJellyBeanOrAbove()、Jellybeanより前のバージョンには、この質問全体に関するバグはありません。また、2番目の呼び出しをキャッチしたいと考えると、このチェックを行わないとコードが実行されません。エミュレーターと実際のデバイス(バージョンが異なる)のコードを数回実行すると、魅力的に動作します
AbdelHady

それが反対票に値するかどうかはわかりません。私はこれを2年前に投稿しましたが、これは私たちの商用アプリケーションで使用されて以来、うまく機能しています。QAチームや何千人ものユーザーから報告されたバグはありません。これは、人々が拡張できる単純な答えであることを意味します。再度発砲させたい場合は、「resetFired」を呼び出します。
Daniel Ryan

私たちのアプリでは「isJellyBeanOrAbove」は必要ありません。正しい領域で「resetFired」を呼び出すと、アプリはすべてのバージョンで正常に動作します。
Daniel Ryan

3

Ankur Chaudharyの同様の問題に関する素晴らしい答えによると、特定のビューかどうTimePickerDialogかを内部で確認したonDateSet場合isShown()、最小限の労力で問題全体が解決され、ピッカーを拡張したり、コードの周りにある恐ろしいフラグを確認したりする必要はありません。またはOSのバージョンを確認する場合も、次のようにします。

public void onDateSet(DatePicker view, int year, int month, int day) {
    if (view.isShown()) {
        // read the date here :)
    }
}

そしてもちろん、onTimeSetアンクルの答えに従って同じことをすることができます


1
他のすべての中からベストアンサー!
hiew1 2015年

2

この状況を管理する方法は、フラグを使用して、onCancelメソッドとonDismissメソッドをオーバーライドすることでした。

onCancelは、ユーザーがダイアログの外側または戻るボタンに触れたときにのみ呼び出されます。onDismissは常に呼び出されます

onCancelメソッドでフラグを設定すると、onDismissメソッドでユーザーの意図(キャンセルアクションまたは完了アクション)をフィルターにかけることができます。アイデアを示すいくつかのコードの下。

public class DatePickerDialogFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {

    private boolean cancelDialog = false;
    private int year;
    private int month;
    private int day;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        DatePickerDialog dpd = new DatePickerDialog(getActivity(), this, year, month, day);
        return dpd;
    }

    public void setDatePickerDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        super.onCancel(dialog);
        cancelDialog = true;
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        if (!cancelDialog) {
          #put the code you want to execute if the user clicks the done button
        }
    }

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        setDatePickerDate(year, monthOfYear, dayOfMonth);
    }
}

1

アプリケーションがアクションバーを使用しない場合、非常に簡単な回避策があります。ちなみに、日付ピッカーのキャンセルには特別な意味があるため、一部のアプリはこの機能に依存していることに注意してください(たとえば、日付フィールドを空の文字列にクリアします。これは一部のアプリでは有効で意味のある入力です) )とブールフラグを使用して、OKで日付が2回設定されるのを防ぐことは、この場合は役に立ちません。

再 実際の修正では、新しいボタンや独自のダイアログを作成する必要はありません。ポイントは、古いバージョンのAndroidとバグのあるバージョン(4。)、および将来のがあることですが、後者はもちろん確実ではありません。Android 2では注意してください。では、android.app.DialogのonStop()は何も実行しない。4。*では、mActionBar.setShowHideAnimationEnabled(false)を実行します。これは、アプリにアクションバーがある場合にのみ重要です。Dialogから継承されるDatePickerDialogのonStop()は、mDatePicker.clearFocus()のみを提供します(Androidソース4.3に対する最新の修正として)。これは必須ではないようです。

したがって、onStop()を何も実行しないメソッドに置き換えると、多くの場合アプリが修正され、予見可能な将来もアプリが修正されないようになります。したがって、DatePickerDialogクラスを独自のクラスで拡張し、ダミーメソッドでonStop()をオーバーライドするだけです。また、要件に応じて、1つまたは2つのコンストラクタを提供する必要があります。また、互換性が最新バージョンのAndroidのみに制限されるため、アクティビティバーで直接何かをしようとするなど、この修正をやりすぎようとしないでください。また、バグはDatePickerDialog自体のonStop()にのみあり、DatePickerDialogのスーパークラスにはないため、DatePickerのonStop()のスーパーを呼び出すことができると便利です。ただし、カスタムクラスからsuper.super.onStop()を呼び出す必要があります。カプセル化の哲学に反するため、Javaでは許可されません。以下は、DatePickerDialogを検証するために使用した小さなクラスです。このコメントが誰かにとって役立つことを願っています。Wojtek Jarosz

public class myDatePickerDialog extends DatePickerDialog {

public myDatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth) {
    super(context, callBack, year, monthOfYear, dayOfMonth);
}

@Override
protected void onStop() {
    // Replacing tryNotifyDateSet() with nothing - this is a workaround for Android bug https://android-review.googlesource.com/#/c/61270/A

    // Would also like to clear focus, but we cannot get at the private members, so we do nothing.  It seems to do no harm...
    // mDatePicker.clearFocus();

    // Now we would like to call super on onStop(), but actually what we would mean is super.super, because
    // it is super.onStop() that we are trying NOT to run, because it is buggy.  However, doing such a thing
    // in Java is not allowed, as it goes against the philosophy of encapsulation (the Creators never thought
    // that we might have to patch parent classes from the bottom up :)
    // However, we do not lose much by doing nothing at all, because in Android 2.* onStop() in androd.app.Dialog //actually
    // does nothing and in 4.* it does:
    //      if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); 
    // which is not essential for us here because we use no action bar... QED
    // So we do nothing and we intend to keep this workaround forever because of users with older devices, who might
    // run Android 4.1 - 4.3 for some time to come, even if the bug is fixed in later versions of Android.
}   

}


ActionBarが使用されている場合でも、ActionBarを非表示/表示しない場合は、これは許容できる回避策です。物事をより安全にするには、onStartをオーバーライドして何もしないようにします(アニメーションの呼び出しは安全に解除されます)。また、表示/非表示を切り替えても、無効になっているのはアニメーションだけです。
Daniel

0


以下の概念を試してください。

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


onDateSet()メソッドが2回呼び出されます(uがemulator.itをチェックインしている場合、2回呼び出します。実際のデバイスを使用している場合は、正しく1回呼び出されます。エミュレータを使用している場合は、カウンターを使用します。実際のデバイスで作業している場合は、カウンター変数を無視します。実際のデバイスの
場合、私にとっては機能します。) ユーザーがDatePickerDialogのボタンをクリックしたとき。
このため、カウンタ値を維持する必要があります。mothodが最初に呼び出したときは何もせず、メソッドが2回目に呼び出したときに操作を実行します。
以下のコーディングスニペットを参照してください

   static int counter=0;       //Counter will be declared globally.

    DatePickerDialog picker = new DatePickerDialog(
            this,
            new OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker v, int y, int m, int d) {

                   counter++;
                   if(counter==1) return;
                   counter=0;
                   //Do the operations here

                }
            },
            2012, 6, 15);
    picker.show();



datepicker dilalogをキャンセルするために私のために働きます。エミュレーターのためにそれは問題ありません

DialogInterface.OnClickListener dialogOnClickListener=new DialogInterface.OnClickListener()
        {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub

                if(which==Dialog.BUTTON_NEGATIVE)
                {
                    Log.i(tagName, "dialog negative button clicked");
                    dialog.dismiss();
                }

            }

        };

        mDatePickerDialog.setButton(Dialog.BUTTON_NEGATIVE, "Cancel", dialogOnClickListener);


実際のデバイスでは動作しますが、エミュレータでは正しく動作しません。Androidエミュレータのバグだと思います。


0

単純な解決策は、ブール値を使用して2回目の実行をスキップすることです。

boolean isShow = false; // define global variable


// when showing time picker
TimePickerDialog timeDlg = new TimePickerDialog( this, new OnTimeSetListener()
            {

                @Override
                public void onTimeSet( TimePicker view, int hourOfDay, int minute )
                {
                    if ( isShow )
                    {
                        isShow = false;
                        // your code
                    }

                }
            }, 8, 30, false );

timeDlg.setButton( TimePickerDialog.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    isShow = false;
                }
            } );
timeDlg.setButton( TimePickerDialog.BUTTON_POSITIVE, "Set", new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick( DialogInterface dialog, int which )
                {
                    isShow = true;
                }
            } );

timeDlg.show();

繰り返しになりますが、私があなたの前に他の人に言ったように、これはバグを解決しませ。私は失礼に聞こえたくありませんが、皆さんはここに投稿する前に独自のソリューションをテストする必要があります...私の要点を証明するために、コードを使用して、ユーザーにダイアログをキャンセルして押してもらい、意味を確認してもらいます。バグの原因は、onStopメソッドを呼び出すべきではないときに呼び出すことです... 2回発生するのはバグの結果です。
davidcsb 2013年

私が十分に明確でなかった場合は、私をさせてください:ダイアログの呼び出しをキャンセルするには、押してくださいonDateSet。したがって、壊れた。
davidcsb 2013年

エラーを表示していただきありがとうございます。回答を編集して、戻るボタンで機能するようにします。あなたの答えは機能していますが、DatePicker dp = picker.getDatePicker(); getTimePicker()メソッドが追加されていないため、はTimePickersと連携していません。したがって、これは有効な回答になります
chamikaw 2013年

2回目の実行をスキップしようとしていますか?!!、何度か試してみましたが、ダイアログを閉じるonDateSetと1回呼び出されますが、「完了」または「設定」を選択すると2回呼び出されます。したがって、最初の1つだけをスキップする必要があるため、2回呼び出された場合にのみ、正しい日付になります
AbdelHady

0

onCancel()をオーバーライドし、setOnDismissListener()を使用して、否定的なユーザーアクションを検出できます。また、DatePickerDialog.BUTTON_POSITIVEを使用すると、ユーザーが新しい日付を設定したいことがわかります。

 DatePickerDialog mDPD = new DatePickerDialog(
                      getActivity(), mOnDateSetListener, mYear, mMonth, mDay);
 mDPD.setOnCancelListener(new OnCancelListener() {
    @Override
    public void onCancel(DialogInterface dialog) {
        // do something onCancek
        setDate = false;
    }
 });

 mDPD.setOnDismissListener(new OnDismissListener() {
    @Override
    public void onDismiss(DialogInterface arg0) {
        // do something onDismiss
        setDate = false;
    }
});

mDPD.setButton(DatePickerDialog.BUTTON_POSITIVE, "Finish", new DatePickerDialog.OnClickListener() {

    @Override
    public void onClick(DialogInterface dialog, int which) {
        // user set new date
        setDate = true;
    }
});

次に、setDateを確認します。

public void onDateSet(DatePicker view, int year, int month, int day) {
    if(setDate){
        //do something with new date
    }
}

0

これは、キャンセルボタンのDatePickerDialogと、戻るボタンで破棄するための私の回避策クラスです。DatePickerDialogのスタイルでコピーして使用(リスナーはステートフルであるため、使用時に新しいインスタンスを作成する必要があります。それ以外の場合は、機能させるためにより多くのコードが必要です)

使用する:

new FixedDatePickerDialog(this,
            new FixedOnDateSetListener() {

                @Override
                public void onDateSet(DatePicker view, int year,
                        int monthOfYear, int dayOfMonth) {
                    if (isOkSelected()) {
                        // when DONE button is clicked
                    }
                }

            }, year, month, day).show();

クラス:

public class FixedDatePickerDialog extends DatePickerDialog {
private final FixedOnDateSetListener fixedCallback;
public FixedDatePickerDialog(Context context,
        FixedOnDateSetListener callBack, int year, int monthOfYear,
        int dayOfMonth) {
    super(context, callBack, year, monthOfYear, dayOfMonth);
    fixedCallback = callBack;
    this.setButton(DialogInterface.BUTTON_NEGATIVE,
            context.getString(R.string.cancel), this);
    this.setButton(DialogInterface.BUTTON_POSITIVE,
            context.getString(R.string.done), this);
}

@Override
public void onClick(DialogInterface dialog, int which) {
    if (which == BUTTON_POSITIVE) {
        fixedCallback.setOkSelected(true);
    } else {
        fixedCallback.setOkSelected(false);
    }
    super.onClick(dialog, which);
}

public abstract static class FixedOnDateSetListener implements
        OnDateSetListener {
    private boolean okSelected = false;

    @Override
    abstract public void onDateSet(DatePicker view, int year,
            int monthOfYear, int dayOfMonth);

    public void setOkSelected(boolean okSelected) {
        this.okSelected = okSelected;
    }

    public boolean isOkSelected() {
        return okSelected;
    }
}

}


0

日付ピッカー、時間ピッカー、数値ピッカーを使用しています。数値ピッカーは、ユーザーが数値を選択するたびに、ピッカーが閉じられる前にonValueChangedを呼び出すので、ピッカーが閉じられたときにのみ、値を使用して何かを行うための構造がすでにありました。

public int interimValue;
public int finalValue;

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    this.interimValue = newVal;
}

public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    this.finalValue = this.interimValue;
}

これを拡張して、ボタンのカスタムonClickListenersを設定し、どのボタンがクリックされたかを確認する引数を付けました。これで、最終的な値を設定する前に、どのボタンがタップされたかを確認できます。

public int interimValue;
public int finalValue;
public boolean saveButtonClicked;

public void setup() {
    picker.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.BUTTON_SAVE), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            picker.onClick(dialog, which); // added for Android 5.0
            onButtonClicked(true);
        }
    });
    picker.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.BUTTON_CANCEL), new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            picker.onClick(dialog, which); // added for Android 5.0
            onButtonClicked(false);
        }
    });
}

public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
    this.interimValue = newVal;
}

public void onButtonClicked(boolean save) {
    this.saveButtonClicked = save;
}

public void onDismiss(DialogInterface dialog) {
    super.onDismiss(dialog);
    if (this.saveButtonClicked) {
        // save
        this.finalValue = this.interimValue;
    } else {
        // cancel
    }
}

そして、それを拡張して、日付と時刻のピッカーの日付と時刻の型、および数値のピッカーのint型を処理するように拡張しました。

上記のいくつかのソリューションよりも簡単だと思ったので、これを投稿しましたが、すべてのコードを含めたので、それほど簡単ではないようです。しかし、それは私がすでに持っていた構造にうまく適合します。

Lollipopの更新:どうやらこのバグはすべてのAndroid 4.1-4.4デバイスで発生するわけではありません。日付ピッカーがonDateSetおよびonTimeSetコールバックを呼び出していないユーザーからいくつかのレポートを受け取ったためです。そして、このバグはAndroid 5.0で正式に修正されました。カスタムボタンがダイアログのonClickハンドラーを呼び出さなかったため、私のアプローチはバグが存在するデバイスでのみ機能しました。これは、バグが存在しないときにonDateSetおよびonTimeSetが呼び出される唯一の場所です。上記のコードを更新してダイアログのonClickを呼び出すようにしたので、バグが存在するかどうかに関係なく機能します。


0

私は上記のDavid Cesarinoの答えが好きでしたが、壊れたダイアログの代わりとなるもので、キャンセルが欠落しているか、キャンセルの動作が正しくない可能性のあるすべてのダイアログで機能するものが必要でした。以下は、ドロップイン置換として機能するDatePickerDialog / TimePickerDialogの派生クラスです。これらはカスタムビューではありません。システムダイアログを使用しますが、キャンセル/戻るボタンの動作を変更して、期待どおりに動作するようにします。

これはAPIレベル3以上で動作するはずです。つまり、基本的にはどのバージョンのAndroidでも(特にジェリ​​ービーンとロリポップでテストしました)。

DatePickerDialog:

package snappy_company_name_here;

import android.content.Context;
import android.content.DialogInterface;
import android.widget.DatePicker;

/**
 * This is a modified version of DatePickerDialog that correctly handles cancellation behavior since it's broken on jellybean and
 * kitkat date pickers.
 *
 * Here is the bug: http://code.google.com/p/android/issues/detail?id=34833
 * Here is an SO post with a bunch of details: http://stackoverflow.com/questions/11444238/jelly-bean-datepickerdialog-is-there-a-way-to-cancel
 *
 * @author stuckj, created on 5/5/15.
 */
public class DatePickerDialog extends android.app.DatePickerDialog implements DialogInterface.OnClickListener
{
    final CallbackHelper callbackHelper;

    // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
    private static class CallbackHelper implements OnDateSetListener
    {
        private final OnDateSetListener callBack;
        private boolean dialogButtonPressHandled = false; // To prevent setting the date when the dialog is dismissed...

        // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
        public CallbackHelper(final OnDateSetListener callBack)
        {
            this.callBack = callBack;
        }

        @Override
        public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth)
        {
            if (!dialogButtonPressHandled && (callBack != null))
            {
                callBack.onDateSet(view, year, monthOfYear, dayOfMonth);
            }
        }
    }

    /**
     * Sets the positive and negative buttons to use the dialog callbacks we define.
     */
    private void setButtons(final Context context)
    {
        setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this);
        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which)
    {
        // ONLY call the super method in the positive case...
        if (which == DialogInterface.BUTTON_POSITIVE)
        {
            super.onClick(dialog, which);
        }

        callbackHelper.dialogButtonPressHandled = true;
    }

    @Override
    public void onBackPressed()
    {
        getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private DatePickerDialog(final Context context,
                             final OnDateSetListener callBack,
                             final int year,
                             final int monthOfYear,
                             final int dayOfMonth,
                             final CallbackHelper callbackHelper)
    {
        super(context, callbackHelper, year, monthOfYear, dayOfMonth);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @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(final Context context,
                            final OnDateSetListener callBack,
                            final int year,
                            final int monthOfYear,
                            final int dayOfMonth)
    {
        this(context, callBack, year, monthOfYear, dayOfMonth, new CallbackHelper(callBack));
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private DatePickerDialog(final Context context, final int theme, final OnDateSetListener listener, final int year,
                             final int monthOfYear, final int dayOfMonth, final CallbackHelper callbackHelper)
    {
        super(context, theme, callbackHelper, year, monthOfYear, dayOfMonth);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context The context the dialog is to run in.
     * @param theme the theme to apply to this dialog
     * @param listener 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(final Context context, final int theme, final OnDateSetListener listener, final int year,
                            final int monthOfYear, final int dayOfMonth)
    {
        this(context, theme, listener, year, monthOfYear, dayOfMonth, new CallbackHelper(listener));
    }
}

TimePickerDialog:

package snappy_company_name_here;

import android.content.Context;
import android.content.DialogInterface;
import android.widget.TimePicker;

/**
 * This is a modified version of TimePickerDialog that correctly handles cancellation behavior since it's broken on jellybean and
 * kitkat date pickers.
 *
 * Here is the bug: http://code.google.com/p/android/issues/detail?id=34833
 * Here is an SO post with a bunch of details: http://stackoverflow.com/questions/11444238/jelly-bean-datepickerdialog-is-there-a-way-to-cancel
 *
 * @author stuckj, created on 5/5/15.
 */
public class TimePickerDialog extends android.app.TimePickerDialog implements DialogInterface.OnClickListener
{
    final CallbackHelper callbackHelper;

    // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
    private static class CallbackHelper implements OnTimeSetListener
    {
        private final OnTimeSetListener callBack;
        private boolean dialogButtonPressHandled = false; // To prevent setting the date when the dialog is dismissed...

        // NOTE: Must be static since we're using it in a super constructor call. Which is annoying, but necessary
        public CallbackHelper(final OnTimeSetListener callBack)
        {
            this.callBack = callBack;
        }

        @Override
        public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute)
        {
            if (!dialogButtonPressHandled && (callBack != null))
            {
                callBack.onTimeSet(view, hourOfDay, minute);
            }
        }
    }

    /**
     * Sets the positive and negative buttons to use the dialog callbacks we define.
     */
    private void setButtons(final Context context)
    {
        setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this);
        setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), this);
    }

    @Override
    public void onClick(final DialogInterface dialog, final int which)
    {
        // ONLY call the super method in the positive case...
        if (which == DialogInterface.BUTTON_POSITIVE)
        {
            super.onClick(dialog, which);
        }

        callbackHelper.dialogButtonPressHandled = true;
    }

    @Override
    public void onBackPressed()
    {
        getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private  TimePickerDialog(final Context context,
                              final OnTimeSetListener callBack,
                              final int hourOfDay, final int minute, final boolean is24HourView, final CallbackHelper callbackHelper)
    {
        super(context, callbackHelper, hourOfDay, minute, is24HourView);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context Parent.
     * @param callBack How parent is notified.
     * @param hourOfDay The initial hour.
     * @param minute The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public TimePickerDialog(final Context context,
                            final OnTimeSetListener callBack,
                            final int hourOfDay, final int minute, final boolean is24HourView)
    {
        this(context, callBack, hourOfDay, minute, is24HourView, new CallbackHelper(callBack));
    }

    // Need this so we can both pass callbackHelper to the super class and save it off as a variable.
    private TimePickerDialog(final Context context, final int theme, final OnTimeSetListener callBack, final int hourOfDay,
                            final int minute, final boolean is24HourView, final CallbackHelper callbackHelper)
    {
        super(context, theme, callbackHelper, hourOfDay, minute, is24HourView);
        this.callbackHelper = callbackHelper;
        setButtons(context);
    }

    /**
     * @param context Parent.
     * @param theme the theme to apply to this dialog
     * @param callBack How parent is notified.
     * @param hourOfDay The initial hour.
     * @param minute The initial minute.
     * @param is24HourView Whether this is a 24 hour view, or AM/PM.
     */
    public TimePickerDialog(final Context context, final int theme, final OnTimeSetListener callBack, final int hourOfDay,
                            final int minute, final boolean is24HourView)
    {
        this(context, theme, callBack, hourOfDay, minute, is24HourView, new CallbackHelper(callBack));
    }
}

これは、上記のThe Seaの答えにかなり似ています。
stuckj

0

ラムダ式を使用したClearButtonを使用した作業バージョン:

public class DatePickerFragment extends DialogFragment {
    private OnDateSelectListener dateSelectListener;
    private OnDateClearListener dateClearListener;

    public void setDateSelectListener(OnDateSelectListener dateSelectListener) {
        this.dateSelectListener = dateSelectListener;
    }

    public void setDateClearListener(OnDateClearListener dateClearListener) {
        this.dateClearListener = dateClearListener;
    }

    @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
        DatePickerDialog dialog = new DatePickerDialog(getActivity(), null, year, month, day);
        dialog.setCancelable(true);
        dialog.setCanceledOnTouchOutside(true);
        dialog.setTitle("Select Date");
        dialog.setButton(BUTTON_POSITIVE, ("Done"), (dialog1, which) -> {
            DatePicker dp = dialog.getDatePicker();
            dialog.dismiss();
            dateSelectListener.onDateSelect(dp.getYear(), dp.getMonth(), dp.getDayOfMonth());
        });
        dialog.setButton(BUTTON_NEUTRAL, ("Clear"), (dialog1, which) -> {
            dialog.dismiss();
            dateClearListener.onDateClear();
        });
        dialog.setButton(BUTTON_NEGATIVE, ("Cancel"), (dialog1, which) -> {
            if (which == DialogInterface.BUTTON_NEGATIVE) {
                dialog.cancel();
            }
        });
        dialog.getDatePicker().setCalendarViewShown(false);
        return dialog;
    }


    public interface OnDateClearListener {
        void onDateClear();
    }

    public interface OnDateSelectListener {
        void onDateSelect(int year, int monthOfYear, int dayOfMonth);
    }
}

0

TimePickerDialogの回避策は次のとおりです。

TimePickerDialog createTimePickerDialog(Context context, int themeResId, TimePickerDialog.OnTimeSetListener orignalListener,
                                                         int hourOfDay, int minute, boolean is24HourView) {
        class KitKatTimeSetListener implements TimePickerDialog.OnTimeSetListener {
            private int hour;
            private int minute;

            private KitKatTimeSetListener() {
            }

            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                this.hour = hourOfDay;
                this.minute = minute;
            }

            private int getHour() { return hour; }
            private int getMinute() {return minute; }
        };

        KitKatTimeSetListener kitkatTimeSetListener = new KitKatTimeSetListener();
        TimePickerDialog timePickerDialog = new TimePickerDialog(context, themeResId, kitkatTimeSetListener, hourOfDay, minute, is24HourView);

        timePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(android.R.string.ok), (dialog, which) -> {
            timePickerDialog.onClick(timePickerDialog, DialogInterface.BUTTON_POSITIVE);
            orignalListener.onTimeSet(new TimePicker(context), kitkatTimeSetListener.getHour(), kitkatTimeSetListener.getMinute());
            dialog.cancel();
        });
        timePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), (dialog, which) -> {
            dialog.cancel();
        });

        return timePickerDialog;
    }

すべてのイベントをラッパーKitKatSetTimeListenerに委任し、BUTTON_POSITIVEがクリックされた場合にのみ、元のOnTimeSetListenerに発火します。


0

ここに投稿された提案のいくつかをテストした後、私は個人的にこのソリューションが最も簡単だと思います。DatePickerDialogコンストラクターでリスナーとして「null」を渡し、「OK」ボタンをクリックすると、onDateSearchSetListenerを呼び出します。

datePickerDialog = new DatePickerDialog(getContext(), null, dateSearch.get(Calendar.YEAR), dateSearch.get(Calendar.MONTH), dateSearch.get(Calendar.DAY_OF_MONTH));
    datePickerDialog.setCancelable(false);
    datePickerDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.dialog_ok), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.d("Debug", "Correct");
            onDateSearchSetListener.onDateSet(datePickerDialog.getDatePicker(), datePickerDialog.getDatePicker().getYear(), datePickerDialog.getDatePicker().getMonth(), datePickerDialog.getDatePicker().getDayOfMonth());
        }
    });
    datePickerDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.dialog_cancel), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.d("Debug", "Cancel");
            dialog.dismiss();
        }
    });

-1

私はこの投稿が1年近くここにあることを知っていますが、私の発見を投稿する必要があると思いました。リスナーを(mullに設定する代わりに)維持し、期待どおりにこの作業を継続することができます。重要なのは、暗黙的に「OK」または(および)「キャンセル」ボタンを設定することです。私はそれをテストしました、そしてそれは私にとってありがたいことに働きます。リスナーは2回起動されません。

この例を見て、

private void setTime(){
final Calendar c = Calendar.getInstance();
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);

final TimePickerDialog timepicker = new TimePickerDialog(this.getActivity(),
        timePickerListener,
        hour, 
        minute, 
        DateFormat.is24HourFormat(getActivity()));

timepicker.setButton(DialogInterface.BUTTON_POSITIVE, "Print", new    
    android.content.DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog,int which) {
            print = true;
            timepicker.dismiss();           
        }
});

timepicker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new 
    android.content.DialogInterface.OnClickListener(){
        @Override
        public void onClick(DialogInterface dialog,int which){
            print = false;
            timepicker.dismiss();       
        }
});

timepicker.setCancelable(false);
timepicker.show();
}

単純明快で、あなたが言う方法では機能しません。timePickerListenerダイアログで何をしても、まだ呼び出されます。私もそれを知ってテストする必要はありません、あなただけを見てする必要がありますソース:あなたはそれを設定しない場合はnulltryNotifyTimeSet()リスナーがあります呼び出します。onTimeSet()その中の両方をonClick()してonStop()
davidcsb 2013

言い換えれば、ネイティブクラスにリスナーへの参照を保持させないでください(つまり、コンストラクターに渡します)。ボタンの扱い方は関係ありません。
davidcsb 2013

こんにちはデビッド、あなたはこれをテストしなかった、そしてあなたはそれが機能しないと思った。これは、私が現在アプリで使用している正確なコードであり、大胆に機能します。
クロコダイル

1)うまくいかなかったと言ったことはありません。私はそれがあなたが言ったように機能しなかったと言いました。もう一度お読みください。2)あなたは不変更する工数を無駄に、LSPおよびSRPを違反し、すべてのあなたの全体で開始するすべての変更を必要としなかったクライアントロジックを。3)あなたの答えは質問に答えます、そうでなければ、私は削除するために「答えではない」としてフラグを立てますが、4)あなたの答えはまだ(それについて申し訳ありません)非常に非効率的であり、基本的な設計問題(リスナーと呼ばれます)対処していません)、したがって、反対票のみ。6)あなた無効に背中ブール値を強制することモーダルウィンドウました。だから、そこには6つの深刻な問題があります!
davidcsb 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.