Nota: Corrigido a partir do pirulito , fonte aqui . Classe automatizada para uso em clientes (compatível com todas as versões do Android) atualizada também.
TL; DR: 1-2-3 etapas fáceis para uma solução global:
- Faça o download desta aula.
- Implemente
OnDateSetListener
em sua atividade (ou mude a classe para atender às suas necessidades).
Acione a caixa de diálogo com este código (neste exemplo, eu o uso dentro de 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");
E é tudo o que é preciso! O motivo pelo qual ainda mantenho minha resposta como "aceita" é porque ainda prefiro minha solução, uma vez que possui uma pegada muito pequena no código do cliente, aborda a questão fundamental (o ouvinte sendo chamado na classe framework), funciona bem nas alterações de configuração e direciona a lógica do código para a implementação padrão nas versões anteriores do Android, não afetadas por esse bug (consulte a fonte da classe).
Resposta original (mantida por razões históricas e didáticas):
Origem do bug
OK, parece que é realmente um bug e alguém já o preencheu. Edição 34833 .
Descobri que o problema é, possivelmente, em DatePickerDialog.java
. Onde se lê:
private void tryNotifyDateSet() {
if (mCallBack != null) {
mDatePicker.clearFocus();
mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
}
}
@Override
protected void onStop() {
tryNotifyDateSet();
super.onStop();
}
Eu acho que poderia ter sido:
@Override
protected void onStop() {
// instead of the full tryNotifyDateSet() call:
if (mCallBack != null) mDatePicker.clearFocus();
super.onStop();
}
Agora, se alguém puder me dizer como posso propor um relatório de patch / bug para o Android, ficarei feliz em fazê-lo. Enquanto isso, sugeri uma possível correção (simples) como uma versão anexada doDatePickerDialog.java
na edição de lá.
Conceito para evitar o bug
Defina o ouvinte como null
no construtor e crie seu próprio BUTTON_POSITIVE
botão posteriormente . É isso aí, detalhes abaixo.
O problema ocorre porque DatePickerDialog.java
, como você pode ver na fonte, chama uma variável global ( mCallBack
) que armazena o ouvinte que foi passado no construtor:
/**
* @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.
}
Portanto, o truque é fornecer um null
ouvinte para ser armazenado como ouvinte e, em seguida, role seu próprio conjunto de botões (abaixo está o código original de # 1, atualizado):
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();
Agora ele funcionará devido à possível correção que eu postei acima.
E como DatePickerDialog.java
verifica a null
sempre que lê mCallback
( desde os dias da API 3 / 1.5, parece - não é possível verificar o Honeycomb, é claro), isso não acionará a exceção.Considerando que o Lollipop corrigiu o problema, não vou investigar: basta usar a implementação padrão (abordada na classe que forneci).
No começo, eu tinha medo de não ligar clearFocus()
, mas testei aqui e as linhas de log estavam limpas. Portanto, essa linha que propus pode até não ser necessária, mas não sei.
Compatibilidade com os níveis anteriores da API (editado)
Como apontei no comentário abaixo, esse era um conceito, e você pode fazer o download da classe que estou usando na minha conta do Google Drive . Do jeito que eu usei, a implementação padrão do sistema é usada em versões não afetadas pelo bug.
Tomei algumas premissas (nomes de botões etc.) adequadas às minhas necessidades, porque queria reduzir ao mínimo o código padrão nas classes de clientes. Exemplo de uso completo:
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");