Como impedir que uma caixa de diálogo seja fechada quando um botão é clicado

732

Eu tenho um diálogo com EditTextpara entrada. Quando clico no botão "yes" na caixa de diálogo, ele validará a entrada e fechará a caixa de diálogo. No entanto, se a entrada estiver errada, desejo permanecer na mesma caixa de diálogo. Sempre que qualquer que seja a entrada, a caixa de diálogo deve ser fechada automaticamente quando eu clicar no botão "não". Como posso desativar isso? A propósito, eu usei o PositiveButton e o NegativeButton para o botão na caixa de diálogo.

user304881
fonte

Respostas:

916

EDIT: Isso funciona apenas na API 8+, conforme observado por alguns dos comentários.

Essa é uma resposta tardia, mas você pode adicionar um onShowListener ao AlertDialog, onde poderá substituir o onClickListener do botão.

final AlertDialog dialog = new AlertDialog.Builder(context)
        .setView(v)
        .setTitle(R.string.my_title)
        .setPositiveButton(android.R.string.ok, null) //Set to null. We override the onclick
        .setNegativeButton(android.R.string.cancel, null)
        .create();

dialog.setOnShowListener(new DialogInterface.OnShowListener() {

    @Override
    public void onShow(DialogInterface dialogInterface) {

        Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View view) {
                // TODO Do something

                //Dismiss once everything is OK.
                dialog.dismiss();
            }
        });
    }
});
dialog.show();
Tom Bollwitt
fonte
7
Ei, antes tarde do que nunca, eu estava procurando exatamente isso, obrigado, +1 :) Essa é uma maneira elegante de adicionar validação à sua caixa de diálogo, especialmente quando você já tem uma classe de ajuda para lidar com alertas
Guillaume
11
Não funciona. AlertDialog.Builder.setOnShowListener não existe. developer.android.com/reference/android/app/…
Leandros
4
Com a API 8, você pode chamar d.getButton (AlertDialog.BUTTON_POSITIVE); como é um método público, mas você deve chamá-lo de show (); foi emitido, caso contrário, zou é nulo dele #
Hurda
13
Você pode tornar isso ainda mais fácil, definindo um OnClickListener nulo no construtor de diálogos (salva o ouvinte "// isto será substituído" vazio).
9788 Steve
1
Também funciona bem com DialogFragments quando o AlertDialog é criado no método onCreateDialog (Bundle savedInstanceState).
Christian Lischnig
655

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.

Sogger
fonte
12
Essa é de longe a implementação mais fácil e funciona perfeitamente. Eu usei AlertDialog.Builder - Altere o manipulador de botão padrão imediatamente após show () e ele está funcionando como um encanto.
Reinherd
1
@sogger dude, editei totalmente sua resposta incrível, porque na seção 1 você rejeitou (); em vez de eu acredito dialog.dismiss (); muito obrigado pela resposta incrível!
Fattie
Existe alguma maneira de impedir o fechamento de um ProgressDialogquando um botão é clicado nele?
18714 Joshua Pinter
1
vaca santa, quanto mais eu sei sobre o Android, mais me sinto enojado ... tudo isso apenas para que um simples diálogo funcione corretamente. que leva horas apenas para descobrir como para exibir um diálogo
spacemonkey
1
@harsh_v atualizou a resposta para usar onResume () da próxima pessoa, obrigado!
Sogger
37

Uma solução alternativa

Gostaria de apresentar uma resposta alternativa de uma perspectiva de UX.

Por que você deseja impedir que uma caixa de diálogo seja fechada quando um botão é clicado? Presumivelmente, é porque você tem uma caixa de diálogo personalizada na qual o usuário não fez uma escolha ou ainda não preencheu tudo completamente. E se eles não tiverem terminado, não permita que eles cliquem no botão positivo. Apenas desative-o até que tudo esteja pronto.

As outras respostas aqui oferecem muitos truques para substituir o clique do botão positivo. Se isso fosse importante, o Android não teria feito um método conveniente? Eles não fizeram.

Em vez disso, o guia de design de Diálogos mostra um exemplo dessa situação. O botão OK é desativado até que o usuário faça uma escolha. Não são necessários truques de substituição. É óbvio para o usuário que algo ainda precisa ser feito antes de prosseguir.

insira a descrição da imagem aqui

Como desativar o botão positivo

Veja o documentação Android para criar um layout de caixa de diálogo personalizado . Recomenda que você coloque o seu AlertDialoginterior a DialogFragment. Depois, basta definir os ouvintes nos elementos de layout para saber quando ativar ou desativar o botão positivo.

O botão positivo pode ser desativado assim:

AlertDialog dialog = (AlertDialog) getDialog();
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);

Aqui está um trabalho inteiro DialogFragment com um botão positivo desativado, como pode ser usado na imagem acima.

import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;

public class MyDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        // inflate the custom dialog layout
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.my_dialog_layout, null);

        // add a listener to the radio buttons
        RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.radio_group);
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                // enable the positive button after a choice has been made
                AlertDialog dialog = (AlertDialog) getDialog();
                dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
            }
        });

        // build the alert dialog
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(view)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        // TODO: use an interface to pass the user choice back to the activity
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        MyDialogFragment.this.getDialog().cancel();
                    }
                });
        return builder.create();
    }

    @Override
    public void onResume() {
        super.onResume();

        // disable positive button by default
        AlertDialog dialog = (AlertDialog) getDialog();
        dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
    }
}

O diálogo personalizado pode ser executado a partir de uma atividade como esta:

MyDialogFragment dialog = new MyDialogFragment();
dialog.show(getFragmentManager(), "MyTag");

Notas

  • Por uma questão de brevidade, omiti a interface de comunicação para passar as informações de escolha do usuário de volta à atividade. A documentação mostra como isso é feito, no entanto.
  • O botão ainda está nulldentro, onCreateDialogentão eu o desativei onResume. Isso tem o efeito indesejável de desativá-lo novamente se o usuário alternar para outro aplicativo e depois voltar sem descartar a caixa de diálogo. Isso pode ser resolvido desmarcando também qualquer opção do usuário ou chamando Runnablede onCreateDialogpara desativar o botão no próximo ciclo de execução.

    view.post(new Runnable() {
        @Override
        public void run() {
            AlertDialog dialog = (AlertDialog) getDialog();
            dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
        }
    });

Relacionado

Suragch
fonte
33

Eu escrevi uma classe simples (um AlertDialogBuilder) que você pode usar para desativar o recurso de dispensa automática ao pressionar os botões da caixa de diálogo.

Também é compatível com o Android 1.6, portanto, não utiliza o OnShowListener (que está disponível apenas API> = 8).

Portanto, em vez de usar o AlertDialog.Builder, você pode usar este CustomAlertDialogBuilder. A parte mais importante é que você não deve chamar create () , mas apenas o método show () . Adicionei métodos como setCanceledOnTouchOutside () e setOnDismissListener para que você ainda possa configurá-los diretamente no construtor.

Eu testei no Android 1.6, 2.x, 3.xe 4.x, por isso deve funcionar muito bem. Se você encontrar alguns problemas, comente aqui.

package com.droidahead.lib.utils;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.View.OnClickListener;

public class CustomAlertDialogBuilder extends AlertDialog.Builder {
    /**
     * Click listeners
     */
    private DialogInterface.OnClickListener mPositiveButtonListener = null;
    private DialogInterface.OnClickListener mNegativeButtonListener = null;
    private DialogInterface.OnClickListener mNeutralButtonListener = null;

    /**
     * Buttons text
     */
    private CharSequence mPositiveButtonText = null;
    private CharSequence mNegativeButtonText = null;
    private CharSequence mNeutralButtonText = null;

    private DialogInterface.OnDismissListener mOnDismissListener = null;

    private Boolean mCancelOnTouchOutside = null;

    public CustomAlertDialogBuilder(Context context) {
        super(context);
    }

    public CustomAlertDialogBuilder setOnDismissListener (DialogInterface.OnDismissListener listener) {
        mOnDismissListener = listener;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
        mNegativeButtonListener = listener;
        mNegativeButtonText = text;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
        mNeutralButtonListener = listener;
        mNeutralButtonText = text;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
        mPositiveButtonListener = listener;
        mPositiveButtonText = text;
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNegativeButton(int textId, DialogInterface.OnClickListener listener) {
        setNegativeButton(getContext().getString(textId), listener);
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setNeutralButton(int textId, DialogInterface.OnClickListener listener) {
        setNeutralButton(getContext().getString(textId), listener);
        return this;
    }

    @Override
    public CustomAlertDialogBuilder setPositiveButton(int textId, DialogInterface.OnClickListener listener) {
        setPositiveButton(getContext().getString(textId), listener);
        return this;
    }

    public CustomAlertDialogBuilder setCanceledOnTouchOutside (boolean cancelOnTouchOutside) {
        mCancelOnTouchOutside = cancelOnTouchOutside;
        return this;
    }



    @Override
    public AlertDialog create() {
        throw new UnsupportedOperationException("CustomAlertDialogBuilder.create(): use show() instead..");
    }

    @Override
    public AlertDialog show() {
        final AlertDialog alertDialog = super.create();

        DialogInterface.OnClickListener emptyOnClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) { }
        };


        // Enable buttons (needed for Android 1.6) - otherwise later getButton() returns null
        if (mPositiveButtonText != null) {
            alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, mPositiveButtonText, emptyOnClickListener);
        }

        if (mNegativeButtonText != null) {
            alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, mNegativeButtonText, emptyOnClickListener);
        }

        if (mNeutralButtonText != null) {
            alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, mNeutralButtonText, emptyOnClickListener);
        }

        // Set OnDismissListener if available
        if (mOnDismissListener != null) {
            alertDialog.setOnDismissListener(mOnDismissListener);
        }

        if (mCancelOnTouchOutside != null) {
            alertDialog.setCanceledOnTouchOutside(mCancelOnTouchOutside);
        }

        alertDialog.show();

        // Set the OnClickListener directly on the Button object, avoiding the auto-dismiss feature
        // IMPORTANT: this must be after alert.show(), otherwise the button doesn't exist..
        // If the listeners are null don't do anything so that they will still dismiss the dialog when clicked
        if (mPositiveButtonListener != null) {
            alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mPositiveButtonListener.onClick(alertDialog, AlertDialog.BUTTON_POSITIVE);
                }
            });
        }

        if (mNegativeButtonListener != null) {
            alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mNegativeButtonListener.onClick(alertDialog, AlertDialog.BUTTON_NEGATIVE);
                }
            });
        }

        if (mNeutralButtonListener != null) {
            alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    mNeutralButtonListener.onClick(alertDialog, AlertDialog.BUTTON_NEUTRAL);
                }
            });
        }

        return alertDialog;
    }   
}

EDIT Aqui está um pequeno exemplo de como usar o CustomAlertDialogBuilder:

// Create the CustomAlertDialogBuilder
CustomAlertDialogBuilder dialogBuilder = new CustomAlertDialogBuilder(context);

// Set the usual data, as you would do with AlertDialog.Builder
dialogBuilder.setIcon(R.drawable.icon);
dialogBuilder.setTitle("Dialog title");
dialogBuilder.setMessage("Some text..");

// Set your buttons OnClickListeners
dialogBuilder.setPositiveButton ("Button 1", new DialogInterface.OnClickListener() {
    public void onClick (DialogInterface dialog, int which) {
        // Do something...

        // Dialog will not dismiss when the button is clicked
        // call dialog.dismiss() to actually dismiss it.
    }
});

// By passing null as the OnClickListener the dialog will dismiss when the button is clicked.               
dialogBuilder.setNegativeButton ("Close", null);

// Set the OnDismissListener (if you need it)       
dialogBuilder.setOnDismissListener(new DialogInterface.OnDismissListener() {
    public void onDismiss(DialogInterface dialog) {
        // dialog was just dismissed..
    }
});

// (optional) set whether to dismiss dialog when touching outside
dialogBuilder.setCanceledOnTouchOutside(false);

// Show the dialog
dialogBuilder.show();

Felicidades,

Yuvi

YuviDroid
fonte
Agradável. Mas não funcionou para mim. O Diálogo é demitido, no entanto.
Leandros
Mmm isso soa estranho. Estou usando isso no meu aplicativo e apenas os botões nos quais eu chamo explicitamente dialog.dismiss () descartarão a caixa de diálogo. Em qual versão do Android você está testando? Você pode mostrar seu código onde você usou o CustomAlertDialogBuilder?
YuviDroid 4/03/12
Eu acho que ela é causada por causa disso: (dialog.show call () após OnClickListener) pastebin.com/uLnSu5v7 Se eu clicar positiveButton eles se demitido se boolean é verdade ...
Leandros
Não testei usando Activity.onCreateDialog (). Provavelmente não pode funcionar dessa maneira. Vou editar a 'resposta' para incluir um pequeno exemplo de como o uso.
YuviDroid
4
Isso funciona para mim com a edição atual! No entanto: mais uma ressalva. Builder.getContext () está disponível apenas na API 11+. Adicione um campo Context mContexte defina-o no construtor.
Oleg Vaskevich
28

Aqui está algo se você estiver usando DialogFragment- que é a maneira recomendada de lidar com as caixas de diálogo de qualquer maneira.

O que acontece com o AlertDialog setButton()método (e imagino o mesmo com AlertDialogBuilder's setPositiveButton()e setNegativeButton()) é que o botão definido (por exemplo AlertDialog.BUTTON_POSITIVE) com ele vai realmente provocar dois diferentes OnClickListenerobjetos quando pressionado.

O primeiro ser DialogInterface.OnClickListener , que é um parâmetro para setButton(), setPositiveButton(), e setNegativeButton().

O outro é o View.OnClickListener , que será configurado para dispensar automaticamente o seu AlertDialogquando qualquer botão for pressionado - e é definido por AlertDialogsi só.

O que você pode fazer é usar setButton()com nullcomo DialogInterface.OnClickListener, para criar o botão e, em seguida, chamar o seu costume método de ação dentro View.OnClickListener. Por exemplo,

@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
    AlertDialog alertDialog = new AlertDialog(getActivity());
    // set more items...
    alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", null);

    return alertDialog;
}

Então, você pode substituir o padrão AlertDialog's botões' View.OnClickListener(que de outra forma descartar o diálogo) no DialogFragment's onResume()método:

@Override
public void onResume()
{
    super.onResume();
    AlertDialog alertDialog = (AlertDialog) getDialog();
    Button okButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
    okButton.setOnClickListener(new View.OnClickListener() { 
        @Override
        public void onClick(View v)
        {
            performOkButtonAction();
        }
    });
}

private void performOkButtonAction() {
    // Do your stuff here
}

Você precisará definir isso no onResume()método, pois getButton()retornará nullaté depois que a caixa de diálogo for exibida!

Isso deve fazer com que seu método de ação personalizado seja chamado apenas uma vez e a caixa de diálogo não será descartada por padrão.

Zhuiguang Liu
fonte
21

Inspirado na resposta de Tom, acredito que a idéia aqui é:

  • Defina o onClickListenerdurante a criação da caixa de diálogo paranull
  • Em seguida, defina um onClickListenerapós o diálogo ser mostrado.

Você pode substituir o onShowListenerTom semelhante. Como alternativa, você pode

  1. pressione o botão depois de chamar o AlertDialog show()
  2. defina os botões onClickListenercomo a seguir (acho que um pouco mais legível).

Código:

AlertDialog.Builder builder = new AlertDialog.Builder(context);
// ...
final AlertDialog dialog = builder.create();
dialog.show();
// now you can override the default onClickListener
Button b = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
b.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Log.i(TAG, "ok button is clicked");
        handleClick(dialog);
    }
});
ericn
fonte
8

Para a pré API 8, resolvi o problema usando um sinalizador booleano, um ouvinte de dispensa e um diálogo de chamada. Mostre novamente se o conteúdo do editText não estava correto. Como isso:

case ADD_CLIENT:
        LayoutInflater factoryClient = LayoutInflater.from(this);
        final View EntryViewClient = factoryClient.inflate(
                R.layout.alert_dialog_add_client, null);

        EditText ClientText = (EditText) EntryViewClient
                .findViewById(R.id.client_edit);

        AlertDialog.Builder builderClient = new AlertDialog.Builder(this);
        builderClient
                .setTitle(R.string.alert_dialog_client)
                .setCancelable(false)
                .setView(EntryViewClient)
                .setPositiveButton("Save",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                    int whichButton) {
                                EditText newClient = (EditText) EntryViewClient
                                        .findViewById(R.id.client_edit);
                                String newClientString = newClient
                                        .getText().toString();
                                if (checkForEmptyFields(newClientString)) {
                                    //If field is empty show toast and set error flag to true;
                                    Toast.makeText(getApplicationContext(),
                                            "Fields cant be empty",
                                            Toast.LENGTH_SHORT).show();
                                    add_client_error = true;
                                } else {
                                    //Here save the info and set the error flag to false
                                    add_client_error = false;
                                }
                            }
                        })
                .setNegativeButton("Cancel",
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                    int id) {
                                add_client_error = false;
                                dialog.cancel();
                            }
                        });
        final AlertDialog alertClient = builderClient.create();
        alertClient.show();

        alertClient
                .setOnDismissListener(new DialogInterface.OnDismissListener() {

                    @Override
                    public void onDismiss(DialogInterface dialog) {
                        //If the error flag was set to true then show the dialog again
                        if (add_client_error == true) {
                            alertClient.show();
                        } else {
                            return;
                        }

                    }
                });
        return true;
Steve
fonte
estranho onDismiss não ser chamado, o meu é nível api 21
DuckDuckGo
7

A resposta neste link é uma solução simples e compatível com a API 3. É muito semelhante à solução de Tom Bollwitt, mas sem usar o OnShowListener menos compatível.

Sim você pode. Você basicamente precisa:

  1. Crie a caixa de diálogo com DialogBuilder
  2. show () a caixa de diálogo
  3. Encontre os botões na caixa de diálogo mostrada e substitua o onClickListener

Fiz pequenas adaptações no código de Kamen desde que eu estava estendendo uma EditTextPreference.

@Override
protected void showDialog(Bundle state) {
  super.showDialog(state);

  class mocl implements OnClickListener{
    private final AlertDialog dialog;
    public mocl(AlertDialog dialog) {
          this.dialog = dialog;
      }
    @Override
    public void onClick(View v) {

        //checks if EditText is empty, and if so tells the user via Toast
        //otherwise it closes dialog and calls the EditTextPreference's onClick
        //method to let it know that the button has been pressed

        if (!IntPreference.this.getEditText().getText().toString().equals("")){
        dialog.dismiss();
        IntPreference.this.onClick(dialog,DialogInterface.BUTTON_POSITIVE);
        }
        else {
            Toast t = Toast.makeText(getContext(), "Enter a number!", Toast.LENGTH_SHORT);
            t.show();
        }

    }
  }

  AlertDialog d = (AlertDialog) getDialog();
  Button b = d.getButton(DialogInterface.BUTTON_POSITIVE);
  b.setOnClickListener(new mocl((d)));
}

Que divertido!

lukeuser
fonte
4

Este código funcionará para você, porque eu tive um problema semelhante e isso funcionou para mim. :)

1- Substitua o método Onstart () na sua classe de diálogo de fragmento.

@Override
public void onStart() {
    super.onStart();
    final AlertDialog D = (AlertDialog) getDialog();
    if (D != null) {
        Button positive = (Button) D.getButton(Dialog.BUTTON_POSITIVE);
        positive.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                if (edittext.equals("")) {
   Toast.makeText(getActivity(), "EditText empty",Toast.LENGTH_SHORT).show();
                } else {
                D.dismiss(); //dissmiss dialog
                }
            }
        });
    }
}
Luis Nuñez
fonte
3

For ProgressDialogs

Para impedir que a caixa de diálogo seja descartada automaticamente, você deve definir o OnClickListenerseguinte após a ProgressDialogexibição, da seguinte maneira:

connectingDialog = new ProgressDialog(this);

connectingDialog.setCancelable(false);
connectingDialog.setCanceledOnTouchOutside(false);

// Create the button but set the listener to a null object.
connectingDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
        (DialogInterface.OnClickListener) null )

// Show the dialog so we can then get the button from the view.
connectingDialog.show();

// Get the button from the view.
Button dialogButton = connectingDialog.getButton( DialogInterface.BUTTON_NEGATIVE);

// Set the onClickListener here, in the view.
dialogButton.setOnClickListener( new View.OnClickListener() {

    @Override
    public void onClick ( View v ) {

        // Dialog will not get dismissed until you call dismiss() explicitly.

    }

});
Joshua Pinter
fonte
3
public class ComentarDialog extends DialogFragment{
private EditText comentario;

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {

    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    LayoutInflater inflater = LayoutInflater.from(getActivity());
    View v = inflater.inflate(R.layout.dialog_comentar, null);
    comentario = (EditText)v.findViewById(R.id.etxt_comentar_dialog);

    builder.setTitle("Comentar")
           .setView(v)
           .setPositiveButton("OK", null)
           .setNegativeButton("CANCELAR", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {

               }
           });

    return builder.create();
}

@Override
public void onStart() {
    super.onStart();

    //Obtenemos el AlertDialog
    AlertDialog dialog = (AlertDialog)getDialog();

    dialog.setCanceledOnTouchOutside(false);
    dialog.setCancelable(false);//Al presionar atras no desaparece

    //Implementamos el listener del boton OK para mostrar el toast
    dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(TextUtils.isEmpty(comentario.getText())){
               Toast.makeText(getActivity(), "Ingrese un comentario", Toast.LENGTH_SHORT).show();
               return;
            }
            else{
                ((AlertDialog)getDialog()).dismiss();
            }
        }
    });

    //Personalizamos
    Resources res = getResources();

    //Buttons
    Button positive_button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
    positive_button.setBackground(res.getDrawable(R.drawable.btn_selector_dialog));

    Button negative_button =  dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
    negative_button.setBackground(res.getDrawable(R.drawable.btn_selector_dialog));

    int color = Color.parseColor("#304f5a");

    //Title
    int titleId = res.getIdentifier("alertTitle", "id", "android");
    View title = dialog.findViewById(titleId);
    if (title != null) {
        ((TextView) title).setTextColor(color);
    }

    //Title divider
    int titleDividerId = res.getIdentifier("titleDivider", "id", "android");
    View titleDivider = dialog.findViewById(titleDividerId);
    if (titleDivider != null) {
        titleDivider.setBackgroundColor(res.getColor(R.color.list_menu_divider));
    }
}
}
Eragonz91
fonte
3

você pode adicionar builder.show (); após a mensagem de validação antes do retorno;

como isso

    public void login()
{
    final AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setView(R.layout.login_layout);
    builder.setTitle("Login");



    builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int id)
        {
            dialog.cancel();
        }
    });// put the negative button before the positive button, so it will appear

    builder.setPositiveButton("Ok", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int id)
        {
            Dialog d = (Dialog) dialog;
            final EditText etUserName = (EditText) d.findViewById(R.id.etLoginName);
            final EditText etPassword = (EditText) d.findViewById(R.id.etLoginPassword);
            String userName = etUserName.getText().toString().trim();
            String password = etPassword.getText().toString().trim();

            if (userName.isEmpty() || password.isEmpty())
            {

                Toast.makeText(getApplicationContext(),
                        "Please Fill all fields", Toast.LENGTH_SHORT).show();
                builder.show();// here after validation message before retrun
                               //  it will reopen the dialog
                              // till the user enter the right condition
                return;
            }

            user = Manager.get(getApplicationContext()).getUserByName(userName);

            if (user == null)
            {
                Toast.makeText(getApplicationContext(),
                        "Error ethier username or password are wrong", Toast.LENGTH_SHORT).show();
                builder.show();
                return;
            }
            if (password.equals(user.getPassword()))
            {
                etPassword.setText("");
                etUserName.setText("");
                setLogged(1);
                setLoggedId(user.getUserId());
                Toast.makeText(getApplicationContext(),
                        "Successfully logged in", Toast.LENGTH_SHORT).show();
               dialog.dismiss();// if every thing is ok then dismiss the dialog
            }
            else
            {
                Toast.makeText(getApplicationContext(),
                        "Error ethier username or password are wrong", Toast.LENGTH_SHORT).show();
                builder.show();
                return;
            }

        }
    });

    builder.show();

}
Pessoa x Pessoa212
fonte
3

Para impedir que a caixa de diálogo seja fechada quando clicada, ela deve ser fechada apenas quando a Internet estiver disponível

Estou tentando fazer a mesma coisa, pois não quero que a caixa de diálogo seja fechada até e a menos que a Internet esteja conectada.

Aqui está o meu código:

AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setTitle("Internet Not Connected");
    if(ifConnected()){

        Toast.makeText(this, "Connected or not", Toast.LENGTH_LONG).show();
    }
    else{
        builder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
               if(!ifConnected())
               {
                   builder.show();
               }
            }
        }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                finish();
            }
        });
        builder.show();

    }

E aqui está o meu código do gerenciador de conectividade:

 private boolean ifConnected()
{
    ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
   return networkInfo!=null && networkInfo.isConnected();
}
karan1.singh
fonte
Esta é inteligente, mas eu recebo essa mensagem de erro:The specified child already has a parent. You must call removeView() on the child's parent first
Dan Chaltiel
2

Se você estiver usando material design, sugiro verificar as caixas de diálogo de materiais . Ele corrigiu vários problemas para mim relacionados aos bugs do Android atualmente abertos (consulte 78088 ), mas o mais importante para esse ticket é que ele possui um autoDismisssinalizador que pode ser definido ao usar o Builder.

theblang
fonte
1

Use um layout personalizado para você DialogFragmente adicione um LinearLayoutsob seu conteúdo, que pode ser estilizado como sem bordas para corresponder ao Design de materiais do Google. Em seguida, encontre os botões recém-criados e substitua os deles OnClickListener.

Exemplo:

public class AddTopicFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // Get the layout inflater
        LayoutInflater inflater = getActivity().getLayoutInflater();
        final View dialogView = inflater.inflate(R.layout.dialog_add_topic, null);

        Button saveTopicDialogButton = (Button) dialogView.findViewById(R.id.saveTopicDialogButton);
        Button cancelSaveTopicDialogButton = (Button) dialogView.findViewById(R.id.cancelSaveTopicDialogButton);

        final AppCompatEditText addTopicNameET = (AppCompatEditText) dialogView.findViewById(R.id.addTopicNameET);
        final AppCompatEditText addTopicCreatedByET = (AppCompatEditText) dialogView.findViewById(R.id.addTopicCreatedByET);

        saveTopicDialogButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // validate inputs
                if(addTopicNameET.getText().toString().trim().isEmpty()){
                    addTopicNameET.setError("Topic name can't be empty");
                    addTopicNameET.requestFocus();
                }else if(addTopicCreatedByET.getText().toString().trim().isEmpty()){
                    addTopicCreatedByET.setError("Topic created by can't be empty");
                    addTopicCreatedByET.requestFocus();
                }else {
                    // save topic to database
                    Topic topic = new Topic();
                    topic.name = addTopicNameET.getText().toString().trim();
                    topic.createdBy = addTopicCreatedByET.getText().toString().trim();
                    topic.createdDate = new Date().getTime();
                    topic.save();
                    AddTopicFragment.this.dismiss();
                }
            }
        });

        cancelSaveTopicDialogButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AddTopicFragment.this.dismiss();
            }
        });

        // Inflate and set the layout for the dialog
        // Pass null as the parent view because its going in the dialog layout
        builder.setView(dialogView)
               .setMessage(getString(R.string.add_topic_message));

        return builder.create();
    }

}

dialog_add_topic.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:errorEnabled="true">

        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/addTopicNameET"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Topic Name"
            android:inputType="textPersonName"
            android:maxLines="1" />

    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:errorEnabled="true">

        <android.support.v7.widget.AppCompatEditText
            android:id="@+id/addTopicCreatedByET"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Created By"
            android:inputType="textPersonName"
            android:maxLines="1" />

    </android.support.design.widget.TextInputLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:text="@string/cancel"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cancelSaveTopicDialogButton"
            style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" />

        <Button
            android:text="@string/save"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/saveTopicDialogButton"
            style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog" />

    </LinearLayout>


</LinearLayout>

Esse é o resultado final.

Ibrahim Hassan
fonte
0

Pode ser construído da maneira mais fácil:

Caixa de diálogo de alerta com exibição personalizada e com dois botões (positivo e negativo).

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()).setTitle(getString(R.string.select_period));
builder.setPositiveButton(getString(R.string.ok), null);

 builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {

    // Click of Cancel Button

   }
 });

  LayoutInflater li = LayoutInflater.from(getActivity());
  View promptsView = li.inflate(R.layout.dialog_date_picker, null, false);
  builder.setView(promptsView);

  DatePicker startDatePicker = (DatePicker)promptsView.findViewById(R.id.startDatePicker);
  DatePicker endDatePicker = (DatePicker)promptsView.findViewById(R.id.endDatePicker);

  final AlertDialog alertDialog = builder.create();
  alertDialog.show();

  Button theButton = alertDialog.getButton(DialogInterface.BUTTON_POSITIVE);
  theButton.setOnClickListener(new CustomListener(alertDialog, startDatePicker, endDatePicker));

CustomClickLister do botão positivo do alerta Dailog :

private class CustomListener implements View.OnClickListener {
        private final Dialog dialog;
        private DatePicker mStartDp, mEndDp;
    public CustomListener(Dialog dialog, DatePicker dS, DatePicker dE) {
        this.dialog = dialog;
        mStartDp = dS;
        mEndDp = dE;
    }

    @Override
    public void onClick(View v) {

        int day1  = mStartDp.getDayOfMonth();
        int month1= mStartDp.getMonth();
        int year1 = mStartDp.getYear();
        Calendar cal1 = Calendar.getInstance();
        cal1.set(Calendar.YEAR, year1);
        cal1.set(Calendar.MONTH, month1);
        cal1.set(Calendar.DAY_OF_MONTH, day1);


        int day2  = mEndDp.getDayOfMonth();
        int month2= mEndDp.getMonth();
        int year2 = mEndDp.getYear();
        Calendar cal2 = Calendar.getInstance();
        cal2.set(Calendar.YEAR, year2);
        cal2.set(Calendar.MONTH, month2);
        cal2.set(Calendar.DAY_OF_MONTH, day2);

        if(cal2.getTimeInMillis()>=cal1.getTimeInMillis()){
            dialog.dismiss();
            Log.i("Dialog", "Dismiss");
            // Condition is satisfied so do dialog dismiss
            }else {
            Log.i("Dialog", "Do not Dismiss");
            // Condition is not satisfied so do not dialog dismiss
        }

    }
}

Feito

Hiren Patel
fonte
-1

Provavelmente, essa é uma resposta muito tardia, mas o uso de setCancelable fará o truque.

alertDial.setCancelable(false);
Navneeth T
fonte
10
Dos documentos: "Define se este diálogo é cancelável com a tecla BACK". Isto não tem nada a ver com o botão positiva rejeitando o diálogo ..
clauziere
3
Não funciona para mim, ainda demitir quando clicar no botão positivo
Hugo
1
Isto não tem nada a ver com o OP
MatPag
1
Não resolve a questão #
Kevin Crain