Jelly Bean DatePickerDialog — 취소 할 수있는 방법이 있습니까?


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

예상 : A는 취소 대화 상자에 표시 버튼을 누릅니다.

현재 : 취소 나타나지 않습니다 버튼을 누릅니다.

스크린 샷 : 4.0.3 (확인) 및 4.1.1 (아마도 잘못 되었습니까?)

문제 # 2 : 잘못된 해고 동작

대화 상자는 실제로 호출해야하는 리스너를 호출 한 다음 항상OnDateSetListener 리스너를 호출합니다 . 취소하면 여전히 set 메소드가 호출되고 설정하면 메소드가 두 번 호출됩니다.

복제 : # 1 코드를 사용하지만 아래 코드를 추가하십시오 (이것은 # 1을 해결하지만 시각적 / UI 만 볼 수 있습니다).

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

예상 :

  • BACK 키를 누르거나 대화 상자 외부를 클릭하면 아무 작업도 수행되지 않습니다 .
  • "취소"를 누르면 피커 취소 가 인쇄됩니다 !.
  • "Set"을 누르면 Picker Set을 인쇄해야합니다 ! .

흐름:

  • BACK 키를 누르거나 대화 상자 외부를 클릭하면 Picker Set!.
  • "취소"를 누르면 피커 취소가 인쇄됩니다 ! 그리고 피커 세트!.
  • "설정"을 누르면 피커 세트가 인쇄됩니다 ! 그리고 피커 세트! .

동작을 보여주는 로그 라인 :

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

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

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

기타 메모 및 의견

  • 그것을 감싸는 DatePickerFragment것은 중요하지 않습니다. 문제를 단순화했지만 테스트했습니다.

축하합니다. Android에서 버그를 발견 한 것 같습니다. 여기에서 신고 할 수 있습니다 .
Michael Hampton

1
잘 작성된 버그 보고서. 코드를 테스트하지 않고도 완전히 이해할 수 있습니다.
Cheok Yan Cheng

6
모든 사람이이 이슈에 투표하도록 촉구합니다! 이슈 34833
브래들리

대화 상자 외부의 터치로 인해 해제 된 것처럼 작동하도록 버튼 기능을 재정의 할 수 없었습니까?
Karthik Balakrishnan 2016 년

2
버그는 2 년 후에도 여전히 열려 있습니다.
erdomester

답변:


115

참고 : 롤리팝의로 고정 , 여기 소스 . 클라이언트에서 사용하기위한 자동화 된 클래스 (모든 Android 버전과 호환)도 업데이트되었습니다.

TL; DR : 글로벌 솔루션의 1-2-3 데드이지 단계 :

  1. 다운로드 수업을 .
  2. OnDateSetListener활동을 구현 하거나 필요에 따라 수업을 변경하십시오.
  3. 이 코드로 대화 상자를 트리거하십시오 (이 샘플에서는 a 안에 사용합니다 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 버전의 기본 구현으로 코드 논리를 라우팅합니다 (클래스 소스 참조).

원래 답변 (역사적이고 교훈적인 이유로 유지됨) :

버그 소스

좋아, 실제로 버그이고 다른 사람이 이미 채운 것처럼 보입니다. 문제 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.javaA에 대한 검사 null가 읽을 때마다 mCallback( API의 일 이후 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는 마지막에 업데이트 된 답변을 참조하십시오. 링크의 클래스에는 젤리 빈에서만 해당 메소드를 호출하는 것이 좋습니다. 아래의 항목에서는이를 무시하고 기본 시스템 구현을 사용하여 ondateset에 대한 시스템 호출을 활동으로 라우팅합니다. 젤리 빈에서는 콜백을 라우팅하기 전에 추가 조치 (버그 방지)가 필요하며 그 벌집 + 메소드 호출과 관련이 있습니다. 그러나 다시 JB에서만.
davidcsb

1
멋진 일입니다. 이 문제가 얼마나 터무니 없는지에 대한 말이 없습니다.
Bill Phillips

5
TimePickerDialog는 어떻습니까? TimePickerDialog에 getTimePicker ()가없는 것 같습니다
lokoko

16

David Cesarino가 게시 한 솔루션에 내 자신의 리프를 추가 할 것입니다. Fragments를 사용하지 않고 모든 버전 (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*/ }
  }
}

다만 기억하십시오 : 버튼 이름을 확인하고 취소하기 위해 배선을 사용하는 경우 사용자 자신 대신 표준 android.R.string.okandroid.R.string.cancel필드 를 사용하는 것이 좋습니다 . 답장을 보내 주셔서 감사합니다.
davidcsb

아, 좋아, 나는 그들이 확인과 취소 텍스트를 제공하는 것을 보지 못했다. 감사!
dmon September

super (context, theme, null, dateToShow.get (YEAR), dateToShow.get (MONTH), dateToShow.get (DAY_OF_MONTH))에서 nullpointer 예외가 발생합니다.
Kishore

Errr ... null을 전달하고 dateToShow있습니까? 다른 널은 실제로 "수정"이 있으므로 거기에 있어야합니다. 어떤 버전입니까?
dmon

import당신은 Field무엇 을 가지고 있었는지 알려주시겠습니까 ? 6 가지 옵션이 있는데 그중 아무것도 작동하지 않았습니다.
Adil Malik

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의 하나 인 "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에서 테스트
erdomester

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

@AbdelH 코드 편집이 잘못되었지만 더 명확하게 편집했습니다. "isJellyBeanOrAbove ()"메소드를 추가 할 필요가 없으며, 장점이 없으며 불필요한 복잡성을 추가합니다. resetFired 호출은 저렴합니다.
Daniel Ryan

onDateSet대화 상자가 어떤 방법으로 닫히면 한 번 호출 되기 때문에 여기의 코드가 올바르지 않으며, 그 평균이 시간을 설정하는 경우 다시 호출되므로 첫 번째 호출이 아닌 두 번째 호출을 잡아야합니다. 당신이했던 것처럼
AbdelHady

에 관해서는 isJellyBeanOrAbove(), Jellybean보다 낮은 버전에는이 모든 질문에 관한 버그가 없으며 두 번째 호출을 잡기를 원할 때 코드를 확인하지 않으면 코드가 실행되지 않습니다. 에뮬레이터 및 실제 장치 (다른 버전의)에 여러 번 코드 및 그것은 매력처럼 작동
AbdelHady

그것이 공감할 가치가 있는지 확실하지 않습니다. 나는 2 년 전에 이것을 게시했는데, 그 이후로 상용 응용 프로그램에 있었고 제대로 작동했습니다. QA 팀 또는 수천 명의 사용자가 버그를보고하지 않았습니다. 이것은 사람들이 확장 할 수있는 간단한 답변입니다. 다시 시작하려면 "resetFired"로 전화하십시오.
Daniel Ryan

우리의 응용 프로그램에서 "isJellyBeanOrAbove"는 필요하지 않습니다. 올바른 영역에서 "resetFired"를 호출하면 모든 버전에서 앱이 제대로 작동합니다.
Daniel Ryan

3

비슷한 문제 에 대한 Ankur Chaudhary의 훌륭한 대답 에 따르면 , 주어진보기 를 보았는지 TimePickerDialog여부를 내부에서 확인 하면 선택기를 확장하거나 코드 주위를 돌고있는 끔찍한 깃발을 확인할 필요없이 최소한의 노력으로 전체 문제를 해결할 것입니다 또는 OS 버전을 확인하려면 다음을 수행하십시오.onDateSetisShown()

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

물론 onTimeSetAnkur의 답변에 따라 동일한 작업을 수행 할 수 있습니다


1
다른 모든 것 중에서 가장 좋은 답변!
hiew1

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에서 날짜가 두 번 설정되는 것을 방지하면이 경우 도움이되지 않습니다.

레. 실제 수정의 경우 새 단추 나 사용자 자신의 대화 상자를 만들 필요가 없습니다. 요점은 안드로이드의 구 버전, 버기 버전 ​​(4. ) 및 미래 버전 모두와 호환되어야 하지만 후자는 확실하지 않습니다. Android 2 에서는 android.app.Dialog에 대한 onStop ()이 전혀 수행하지 않으며, 4. *에서는 앱에 작업 표시 줄이있는 경우에만 중요한 mActionBar.setShowHideAnimationEnabled (false)를 수행합니다. Dialog에서 상속받은 DatePickerDialog의 onStop ()은 mDatePicker.clearFocus () (Android 소스 4.3의 최신 수정 사항) 만 제공하며 필수는 아닙니다.

따라서 onStop ()을 아무 것도하지 않는 메서드로 바꾸면 앱을 수정하고 가까운 미래에도 앱을 유지할 수 있습니다. 따라서 단순히 자신의 DatePickerDialog 클래스를 확장하고 더미 메소드로 onStop ()을 재정의하십시오. 또한 요구 사항에 따라 하나 또는 두 개의 생성자를 제공해야합니다. 또한 활동 막대로 직접 무언가를 시도 하여이 수정 프로그램을 과도하게 사용하려고 시도해서는 안됩니다. 예를 들어 최신 버전의 Android에만 호환성이 제한되기 때문입니다. 또한 버그는 DatePickerDialog 자체의 onStop ()에만 있고 DatePickerDialog의 슈퍼 클래스에는 없기 때문에 DatePicker의 onStop ()에 대해 super를 호출하는 것이 좋을 것입니다. 그러나 사용자 정의 클래스에서 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.
}   

}


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 () 메소드는 두 번 호출합니다 (u가 에뮬레이터에서 체크인하는 경우 두 번 호출합니다. 실제 장치를 사용하는 경우 올바르게 단일 시간을 호출합니다. 에뮬레이터를 사용하는 경우 카운터를 사용하십시오. 실제 장치에서 작업하는 경우
사용자가 DatePickerDialog에서 버튼을 클릭하면 카운터 변수를 무시하십시오 .
이를 위해 카운터 값을 유지해야하며 mothod가 처음 호출 될 때 아무 작업도 수행하지 않고 메소드가 두 번째 호출 할 때 작업을 수행해야합니다.
아래 코딩 스 니펫을 참조하십시오

   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

간단한 해결책은 부울을 사용하여 두 번째 실행을 건너 뛰는 것입니다.

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메서드를 호출하면 안된다는 것입니다. 두 번 실행하면 버그가 발생합니다.
davidcsb 2016 년

확실하지 않은 경우 대화를 취소하려면 뒤로를 누르십시오 onDateSet. 따라서 깨진.
davidcsb 2016 년

오류를 보여 주셔서 감사합니다. 뒤로 버튼과 함께 작동하도록 답변을 편집합니다. 귀하의 답변은 작동하지만 DatePicker dp = picker.getDatePicker (); getTimePicker () 메소드가 추가되지 않았으므로 TimePickers와 작동하지 않습니다. 그래서 이것은 정답
chamikaw

우리가 두 번째 실행을 건너 뛰는 방법은 무엇입니까? !!, 대화 상자를 닫을 때 onDateSet한 번 호출되지만 "완료"또는 "설정"을 선택하면 두 번 호출됩니다. 따라서 우리는 첫 번째 것만 건너 뛸 필요가 있습니다. 그래서 두 번 호출되면 & 만 정확한 날짜가 있습니다
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 업데이트 : 날짜 및 시간 선택기가 onDateSet 및 onTimeSet 콜백을 호출하지 않은 사용자로부터 몇 가지 보고서를 받았기 때문에이 버그는 모든 Android 4.1-4.4 장치에서 발생하지 않습니다. 그리고 버그는 안드로이드 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 's answer과 매우 유사합니다.
stuckj

0

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

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"을 전달한 다음 "확인"버튼을 클릭하면 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로 설정하는 대신 계속 유지할 수 있으며 예상 대로이 작업을 수행 할 수 있습니다. 핵심은 "확인"또는 "및"취소 버튼을 암시 적으로 설정하는 것입니다. 나는 그것을 테스트했고 그것은 나에게 감사하게 작동한다. 리스너는 두 번 실행되지 않습니다.

이 예를보십시오.

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대화 상자에서 수행하는 작업에 관계없이 여전히 호출됩니다. 나는 심지어는 알고 테스트 할 필요가 없습니다, 당신은 단지 볼 필요 소스 : 당신이 그것을 설정하지 않으면 null, tryNotifyTimeSet()리스너의 호출 onTimeSet()은 모두를 onClick()하고 onStop().
davidcsb

즉, 네이티브 클래스가 리스너에 대한 참조를 보유 하지 않도록하십시오 (즉, 생성자에 전달). 버튼을 다루는 방법은 관련이 없습니다.
davidcsb

안녕 데이비드, 당신은 이것을 테스트하지 않았고 작동하지 않을 것이라고 가정했습니다. 이것은 현재 내 응용 프로그램에서 사용중인 정확한 코드이며 챔피언처럼 작동합니다.
악어

1) 나는 그것이 작동하지 않는다고 결코 말하지 않았다. 나는 당신이 말한대로 작동하지 않았다고 말했다. 다시 읽어주십시오. 2) 당신은 불필요하게 변경에 사람이 시간을 낭비하고, LSP 및 SRP를 위반 하는 모든 사용자 전체 로 시작하는 변경이 필요하지 않은 클라이언트 논리를. 3) 귀하의 답변은 질문을 해결합니다. 그렇지 않으면 제거하기 위해 "답변이 아님"으로 표시하지만 4) 귀하의 답변은 여전히 매우 비효율적이며 근본적인 디자인 문제를 해결하지 못합니다. ), 따라서 downvote 만 있습니다. 6) 장애인 뒤로 하고 그것을 부울 강제로 모달 창을했다. 따라서 6 가지 심각한 문제가 있습니다!
davidcsb
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.