Aqui estão algumas soluções para todos os tipos de caixas de diálogo, incluindo uma solução para AlertDialog.Builder que funcionará em todos os níveis da API (funciona abaixo da API 8, que a outra resposta aqui não). Existem soluções para AlertDialogs usando AlertDialog.Builder, DialogFragment e DialogPreference.
Abaixo estão os exemplos de código que mostram como substituir o manipulador de botão comum padrão e impedir que o diálogo seja fechado para essas diferentes formas de diálogo. Todos os exemplos mostram como impedir que o botão positivo feche a caixa de diálogo.
Nota: Uma descrição de como o fechamento da caixa de diálogo funciona sob o capô para as classes android básicas e por que as seguintes abordagens são escolhidas a seguir após os exemplos, para quem deseja obter mais detalhes
AlertDialog.Builder - Alterar manipulador de botão padrão imediatamente após show ()
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Test for preventing dialog close");
builder.setPositiveButton("Test",
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
//Do nothing here because we override this button later to change the close behaviour.
//However, we still need this because on older versions of Android unless we
//pass a handler the button doesn't get instantiated
}
});
final AlertDialog dialog = builder.create();
dialog.show();
//Overriding the handler immediately after show is probably a better approach than OnShowListener as described below
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Boolean wantToCloseDialog = false;
//Do stuff, possibly set wantToCloseDialog to true then...
if(wantToCloseDialog)
dialog.dismiss();
//else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
}
});
DialogFragment - substituir onResume ()
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Test for preventing dialog close");
builder.setPositiveButton("Test",
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
//Do nothing here because we override this button later to change the close behaviour.
//However, we still need this because on older versions of Android unless we
//pass a handler the button doesn't get instantiated
}
});
return builder.create();
}
//onStart() is where dialog.show() is actually called on
//the underlying dialog, so we have to do it there or
//later in the lifecycle.
//Doing it in onResume() makes sure that even if there is a config change
//environment that skips onStart then the dialog will still be functioning
//properly after a rotation.
@Override
public void onResume()
{
super.onResume();
final AlertDialog d = (AlertDialog)getDialog();
if(d != null)
{
Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Boolean wantToCloseDialog = false;
//Do stuff, possibly set wantToCloseDialog to true then...
if(wantToCloseDialog)
d.dismiss();
//else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
}
});
}
}
DialogPreference - substitui showDialog ()
@Override
protected void onPrepareDialogBuilder(Builder builder)
{
super.onPrepareDialogBuilder(builder);
builder.setPositiveButton("Test", this); //Set the button here so it gets created
}
@Override
protected void showDialog(Bundle state)
{
super.showDialog(state); //Call show on default first so we can override the handlers
final AlertDialog d = (AlertDialog) getDialog();
d.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Boolean wantToCloseDialog = false;
//Do stuff, possibly set wantToCloseDialog to true then...
if(wantToCloseDialog)
d.dismiss();
//else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
}
});
}
Explicação das abordagens:
Examinando o código-fonte do Android, a implementação padrão AlertDialog funciona registrando um manipulador de botão comum em todos os botões reais do OnCreate (). Quando um botão é clicado, o manipulador de botão comum encaminha o evento click para qualquer manipulador que você tenha passado em setButton () e então chama demitido o diálogo.
Se você deseja impedir que uma caixa de diálogo seja fechada quando um desses botões é pressionado, você deve substituir o manipulador de botão comum para a visualização real do botão. Como é atribuído em OnCreate (), você deve substituí-lo após a implementação padrão de OnCreate () ser chamada. OnCreate é chamado no processo do método show (). Você pode criar uma classe Dialog personalizada e substituir OnCreate () para chamar o super.OnCreate () e substituir os manipuladores de botão, mas se você criar uma caixa de diálogo personalizada, não obterá o Builder gratuitamente; nesse caso, qual é o objetivo ?
Portanto, ao usar uma caixa de diálogo da maneira como é projetada, mas ao controlar quando é descartada, uma abordagem é chamar dialog.Show () primeiro e, em seguida, obter uma referência ao botão usando dialog.getButton () para substituir o manipulador de cliques. Outra abordagem é usar setOnShowListener () e implementar encontrar a exibição do botão e substituir o manipulador no OnShowListener. A diferença funcional entre os dois é 'quase' nula, dependendo de qual thread cria originalmente a instância do diálogo. Examinando o código-fonte, o onShowListener é chamado por uma mensagem postada em um manipulador em execução no encadeamento que criou esse diálogo. Portanto, como o OnShowListener é chamado por uma mensagem postada na fila de mensagens, é tecnicamente possível que a chamada para o ouvinte seja atrasada algum tempo após a conclusão da exibição.
Portanto, acredito que a abordagem mais segura é a primeira: chamar show.Dialog (); em seguida, imediatamente no mesmo caminho de execução, substitua os manipuladores de botão. Como o código que chama show () estará operando no encadeamento principal da GUI, significa que o código que você segue show () será executado antes de qualquer outro código nesse encadeamento, enquanto o tempo do método OnShowListener está à mercê de a fila de mensagens.