148

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

私はここで奇妙なものを持っています。

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

問題 #1: デフォルトでの矛盾

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

想定:ダイアログに [キャンセル] ボタンが表示されます。

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

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

問題 #2: 間違った却下動作

ダイアログは、実際に呼び出す必要があるリスナーを呼び出してから、常にリスナーを呼び出しOnDateSetListenerます。キャンセルすると set メソッドが呼び出され、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 キーを押すか、ダイアログの外側をクリックしても何も起こりません
  • 「キャンセル」を押すと、Picker Cancel!が出力されます。.
  • 「設定」を押すと、ピッカー セットが印刷されます。.

現時点の:

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

動作を示すログ行:

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ません。問題を単純化しましたが、テストしました。
4

19 に答える 19

115

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

そして、それだけです!私がまだ答えを「受け入れられた」としている理由は、クライアントコードのフットプリントが非常に小さく、基本的な問題(フレームワーククラスで呼び出されているリスナー)に対処し、構成の変更全体で正常に機能するため、私がまだ自分のソリューションを好むためです。そして、このバグに悩まされていない以前の 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.javaa をチェックするため( API 3/1.5 の時代から--- もちろん Honeycomb をチェックできないようです)、例外は発生しません。nullmCallbackLollipop が問題を修正したことを考えると、私はそれを調べるつもりはありません。デフォルトの実装 (私が提供したクラスでカバーされています) を使用してください。

最初は を呼び出さないことを恐れてい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");
于 2012-07-15T16:46:41.493 に答える
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*/ }
  }
}
于 2012-09-10T03:48:39.183 に答える
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();
于 2013-03-03T21:24:39.520 に答える
4

David Cesarinoによるソリューション「TL;DR: 1-2-3 dead easy steps for a global solution」に基づく TimePicker 用のもの

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

Calling と Listener は元のソリューションと同じです。含めるだけ

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.
    }
}
于 2013-11-27T13:14:48.977 に答える
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 
    }
}
于 2012-10-04T22:58:22.260 に答える
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);
    }
}
于 2013-09-01T01:17:04.903 に答える
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 を検証するために使用した小さなクラスです。このコメントが誰かの役に立てば幸いです。ヴォイテク・ヤロス

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.
}   

}

于 2013-09-13T08:51:51.400 に答える
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 に戻ります。

于 2016-09-22T10:20:35.400 に答える
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 回呼び出します (エミュレーターをチェックインしている場合は 2 回呼び出します。実際のデバイスを使用している場合は、1 回だけ正しく呼び出します。エミュレーターを使用している場合はカウンターを使用します。実際のデバイスで作業している場合は、カウンターを使用します。カウンター変数を無視します。実際のデバイスでは、私のために働いています。)
ユーザーが DatePickerDialog のボタンをクリックしたとき。
このため、メソッドが最初に呼び出されたときにカウンター値を維持し、メソッドが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();



デートピッカーのダイラログをキャンセルするには、私のために働いています。

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エミュレータのバグだと思います。

于 2012-09-20T10:17:35.090 に答える
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();
于 2013-06-13T10:29:58.357 に答える
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);
    }
}
于 2016-01-27T11:44:36.107 に答える
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 で正式に修正されました。私のカスタム ボタンは、バグが存在しないときに onDateSet と onTimeSet が呼び出される唯一の場所であるダイアログの onClick ハンドラーを呼び出さなかったため、私のアプローチはバグが存在するデバイスでのみ機能しました。ダイアログの onClick を呼び出すように上記のコードを更新したので、バグが存在するかどうかに関係なく機能するようになりました。

于 2014-11-20T22:53:36.803 に答える
0

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

これは、API レベル 3 以降で機能するはずです。したがって、基本的に Android の任意のバージョン (具体的には、jellybean と lollipop でテストしました)。

日付ピッカー ダイアログ:

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));
    }
}
于 2015-05-06T02:46:34.247 に答える
-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();
}
于 2013-04-12T15:55:44.713 に答える