---モデレーターへの注意:今日(7月15日)、誰かがすでにこの問題に直面していることに気づきました こちら 。しかし、私はこの問題のより良い説明を提供したと思うので、これを複製として閉じることが適切かどうかはわかりません。他の質問を編集してこのコンテンツをそこに貼り付ける必要があるかどうかはわかりませんが、他の誰かの質問をあまり変更するのは不安です。---
ここに何か奇妙ながあります。
問題は、ビルドするSDKに依存するとは思わない。重要なのはデバイスのOSバージョンです。
DatePickerDialog
はJelly Beanで変更(?)され、現在はDoneボタンのみが提供されています。以前のバージョンにはCancelボタンが含まれていたため、これがユーザーエクスペリエンスに影響する可能性があります(一貫性、以前のAndroidバージョン)の筋肉メモリ)。
Replicate:基本プロジェクトを作成します。これを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();
期待:に表示されるキャンセルボタンダイアログ。
Current:ACancelボタンは表示されません。
スクリーンショット:4.0. (OK)and 4.1.1 (おそらく間違っている?)。
ダイアログは、実際に呼び出すリスナーを呼び出してから、alwaysOnDateSetListener
リスナーを呼び出します。キャンセルするとsetメソッドが呼び出され、設定するとメソッドが2回呼び出されます。
Replicate:#1コードを使用しますが、以下のコードを追加します(これにより、#1が解決されます、ただし視覚的にのみ/ UI):
picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Picker", "Cancel!");
}
});
期待:
Current:
動作を示すログ行:
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バージョン)と互換性があります)も更新されました。
OnDateSetListener
を実装します(またはニーズに合わせてクラスを変更します)。このコードでダイアログをトリガーします(このサンプルでは、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、それは実際にバグであり、他の誰かがすでにそれを埋めているように見えます。 問題348 。
問題はおそらく_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.Java
_はnull
を読み込むたびにmCallback
をチェックするので( API 3/1.5の時代からのようです ---もちろんハニカムをチェックできません)、それは例外を引き起こしません。Lollipopが問題を修正したことを考慮して、私はそれを調べません。デフォルトの実装(私が提供したクラスでカバー)。
最初はclearFocus()
を呼び出さないことを恐れていましたが、ここでテストし、Log行はきれいでした。したがって、私が提案したその行は、結局必要でさえないかもしれませんが、私は知りません。
下のコメントで指摘したように、それは概念であり、次のことができます 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");
_
Fragmentsを使用していない場合に、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*/ }
}
}
バグが修正されるまで、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();
David Cesarino、「TL; DR:1-2-3グローバルソリューションの簡単な手順」によるソリューションに基づくTimePicker用
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.
}
}
誰かが簡単な回避策を望んでいる場合、私が使用したコードは次のとおりです。
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"/>
興味のある方のために 完全なチュートリアル をご覧ください。
Ankur Chaudharyの素晴らしい answer によると、同様のTimePickerDialog
の問題について、与えられたビューがisShown()
であるかどうかをonDateSet
でチェックすると、ピッカーを拡張したり、コードを回避する恐ろしいフラグをチェックしたり、OSバージョンをチェックしたりする必要なしに、最小限の労力で問題全体を解決します。次のようにします。
public void onDateSet(DatePicker view, int year, int month, int day) {
if (view.isShown()) {
// read the date here :)
}
}
そしてもちろん、Ankurの答えに従ってonTimeSet
についても同じことができます
私の簡単な解決策。再度起動するには、「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
}
}
この状況を管理する方法は、フラグを使用し、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);
}
}
アプリケーションがアクションバーを使用しない場合、非常に簡単な回避策があります。ちなみに、一部のアプリはこの機能に依存して動作することに注意してください。日付ピッカーのキャンセルは特別な意味を持つためです(たとえば、日付フィールドを空の文字列にクリアします。 )ブール値フラグを使用して、[OK]で日付が2回設定されないようにすることは、この場合には役に立ちません。
Re。実際の修正では、新しいボタンや独自のダイアログを作成する必要はありません。ポイントは、Androidの古いバージョン、バグのあるバージョン(4 .)および将来のバージョンの両方と互換性があることです。ただし、後者はもちろん確認することはできません。inAndroid 2。、Android.app.DialogのonStop()は何もしません。4。*では、mActionBar.setShowHideAnimationEnabled(false)はアプリにアクションバー:Dialogを継承するDatePickerDialogのonStop()は、mDatePicker.clearFocus()(Android sources 4.3)の最新の修正時点)にのみ貢献しますが、これは必須ではないようです。
したがって、多くの場合、onStop()を何もしないメソッドに置き換えることでアプリを修正し、予見可能な将来にわたってアプリをそのままにしておく必要があります。したがって、DatePickerDialogクラスを独自のクラスで拡張し、ダミーメソッドであるonStop()をオーバーライドします。また、要件に応じて、1つまたは2つのコンストラクターを提供する必要があります。また、この修正を無理やりしようとするような誘惑に遭わないよう注意してください。 Androidのみの最新バージョンに互換性を制限します。また、DatePickerのonStopのスーパーを呼び出すことができるのはいいことです。 ()バグはDatePickerDialog自体のonStop()のみにあり、DatePickerDialogのスーパークラスにはないためですが、これには、カスタムクラスから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.
}
}
Lambda Expressionsを使用した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);
}
}
キャンセルボタンの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;
}
}
}
以下の概念を試してください。
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回呼び出します。エミュレータを使用している場合はcounter.ifを使用します実際のデバイスで動作している場合、カウンター変数を無視します。
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エミュレーターのバグ。
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
}
}
上記のデイビッド・セサリーノの答えが好きでしたが、壊れたダイアログの代わりとなるもので、キャンセルされていない/キャンセル動作が間違っているダイアログで動作するものが必要でした。ドロップイン置換として機能する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));
}
}
単純な解決策は、ブール値を使用して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();
日付ピッカー、時間ピッカー、番号ピッカーを使用しています。ユーザーが数値を選択するたびに、ピッカーが却下される前に、数値ピッカーは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を呼び出すように上記のコードを更新したため、バグが存在するかどうかに関係なく機能します。
ここに投稿されたいくつかの提案をテストした後、私はこのソリューションが最も簡単だと個人的に思います。 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();
}
});
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に起動します。