Come impedire la chiusura di una finestra di dialogo quando si fa clic su un pulsante


732

Ho una finestra di dialogo con EditTextper input. Quando faccio clic sul pulsante "Sì" nella finestra di dialogo, convaliderà l'input e quindi chiuderà la finestra di dialogo. Tuttavia, se l'input è errato, voglio rimanere nella stessa finestra di dialogo. Ogni volta che non importa quale sia l'input, la finestra di dialogo dovrebbe essere chiusa automaticamente quando faccio clic sul pulsante "no". Come posso disabilitarlo? A proposito, ho usato PositiveButton e NegativeButton per il pulsante nella finestra di dialogo.

Risposte:


916

EDIT: funziona solo su API 8+ come notato da alcuni dei commenti.

Questa è una risposta tardiva, ma è possibile aggiungere un onShowListener ad AlertDialog dove è possibile sovrascrivere onClickListener del pulsante.

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

7
Ehi, meglio tardi che mai, stavo cercando esattamente questo, grazie +1 :) Questo è un modo elegante di aggiungere la convalida al tuo dialogo, specialmente quando hai già una classe di wrapping di aiuto per gestire gli avvisi
Guillaume

11
Non funziona. AlertDialog.Builder.setOnShowListener non esiste. developer.android.com/reference/android/app/…
Leandros

4
Con API pre 8, è possibile chiamare d.getButton (AlertDialog.BUTTON_POSITIVE); come è metodo pubblico, ma devi chiamarlo show (); è stato emesso, altrimenti ne otterrai solo null
Hurda

13
Puoi renderlo anche un po 'più pulito impostando un OnClickListener null sul generatore di finestre di dialogo (salva il listener vuoto "// questo verrà ignorato").
Steve Haley,

1
Funziona bene anche con DialogFragments quando AlertDialog viene creato nel metodo onCreateDialog (Bundle savedInstanceState).
Christian Lischnig,

655

Ecco alcune soluzioni per tutti i tipi di finestre di dialogo, inclusa una soluzione per AlertDialog.Builder che funzionerà a tutti i livelli di API (funziona al di sotto dell'API 8, a cui l'altra risposta non corrisponde). Esistono soluzioni per AlertDialogs che utilizzano AlertDialog.Builder, DialogFragment e DialogPreference.

Di seguito sono riportati degli esempi di codice che mostrano come sovrascrivere il gestore dei pulsanti comuni predefinito e impedire la chiusura della finestra di dialogo per queste diverse forme di finestre di dialogo. Tutti gli esempi mostrano come impedire al pulsante positivo di chiudere la finestra di dialogo.

Nota: una descrizione di come funziona la chiusura della finestra di dialogo sotto il cofano per le classi Android di base e perché vengono scelti i seguenti approcci dopo gli esempi, per coloro che desiderano maggiori dettagli


AlertDialog.Builder: modifica il gestore pulsanti predefinito immediatamente dopo 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 - override 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 - override 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.
                }
            });
}

Spiegazione degli approcci:

Esaminando il codice sorgente Android, l'implementazione predefinita di AlertDialog funziona registrando un gestore di pulsanti comune su tutti i pulsanti effettivi in ​​OnCreate (). Quando si fa clic su un pulsante, il gestore pulsanti comune inoltra l'evento click a qualsiasi gestore passato in setButton (), quindi le chiamate chiudono la finestra di dialogo.

Se si desidera impedire la chiusura di una finestra di dialogo quando si preme uno di questi pulsanti, è necessario sostituire il gestore dei pulsanti comune per la visualizzazione effettiva del pulsante. Poiché è assegnato in OnCreate (), è necessario sostituirlo dopo aver chiamato l'implementazione predefinita di OnCreate (). OnCreate viene chiamato nel processo del metodo show (). È possibile creare una classe Dialog personalizzata e sovrascrivere OnCreate () per chiamare super.OnCreate () quindi sovrascrivere i gestori dei pulsanti, ma se si crea una finestra di dialogo personalizzata non si ottiene il Builder gratuitamente, nel qual caso qual è il punto ?

Quindi, usando una finestra di dialogo nel modo in cui è progettata, ma controllando quando viene respinta, un approccio consiste nel chiamare dialog.Show () prima, quindi ottenere un riferimento al pulsante usando dialog.getButton () per sovrascrivere il gestore di clic. Un altro approccio è utilizzare setOnShowListener () e implementare la ricerca della vista pulsanti e la sostituzione del gestore in OnShowListener. La differenza funzionale tra i due è "quasi" nulla, a seconda di quale thread crea originariamente l'istanza di dialogo. Esaminando il codice sorgente, onShowListener viene chiamato da un messaggio inviato a un gestore in esecuzione sul thread che ha creato quella finestra di dialogo. Pertanto, poiché OnShowListener viene chiamato da un messaggio pubblicato nella coda dei messaggi, è tecnicamente possibile che la chiamata del proprio ascoltatore venga ritardata un po 'di tempo dopo il completamento dello spettacolo.

Pertanto, credo che l'approccio più sicuro sia il primo: chiamare show.Dialog (), quindi immediatamente nello stesso percorso di esecuzione sostituire i gestori di pulsanti. Dato che il tuo codice che chiama show () funzionerà sul thread della GUI principale, significa che qualunque codice tu segua show () verrà eseguito prima di qualsiasi altro codice su quel thread, mentre i tempi del metodo OnShowListener sono in balia di la coda dei messaggi.


12
Questa è di gran lunga l'implementazione più semplice e funziona perfettamente. Ho usato AlertDialog.Builder - Cambia gestore dei pulsanti predefinito subito dopo show () e funziona come un incantesimo.
Reinherd,

1
@sogger amico, ho modificato con audacia tutta la tua sorprendente risposta perché nella sezione 1 avevi licenziato (); invece di credo dialog.dismiss (); grazie mille per la fantastica risposta!
Fattie,

Esiste un modo per impedire la chiusura di ProgressDialogun pulsante quando si fa clic su di esso?
Joshua Pinter,

1
mucca santa, più so di Android, più mi sento disgustato ... tutto questo solo per far funzionare correttamente un semplice dialogo. ci vogliono ore solo per capire come visualizzare una finestra di dialogo
SpaceMonkey,

1
@harsh_v ha aggiornato la risposta per usare onResume () per la prossima persona, grazie!
Sogger

37

Una soluzione alternativa

Vorrei presentare una risposta alternativa dal punto di vista UX.

Perché dovresti impedire la chiusura di una finestra di dialogo quando si fa clic su un pulsante? Presumibilmente è perché hai una finestra di dialogo personalizzata in cui l'utente non ha fatto una scelta o non ha ancora completamente compilato tutto. E se non sono finiti, non dovresti consentire loro di fare clic sul pulsante positivo. Disattivalo fino a quando tutto è pronto.

Le altre risposte qui forniscono molti trucchi per ignorare il clic positivo sul pulsante. Se fosse importante farlo, Android non avrebbe creato un metodo conveniente per farlo? Non l'hanno fatto.

Invece, la guida alla progettazione di Dialogs mostra un esempio di tale situazione. Il pulsante OK è disabilitato fino a quando l'utente non effettua una scelta. Non sono necessari trucchi imperativi. È ovvio per l'utente che qualcosa deve ancora essere fatto prima di continuare.

inserisci qui la descrizione dell'immagine

Come disabilitare il pulsante positivo

Consulta la documentazione di Android per la creazione di un layout di dialogo personalizzato . Ti consiglia di posizionare il tuo AlertDialoginterno a DialogFragment. Quindi tutto ciò che devi fare è impostare i listener sugli elementi del layout per sapere quando abilitare o disabilitare il pulsante positivo.

Il pulsante positivo può essere disabilitato in questo modo:

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

Ecco un intero lavoro DialogFragmentcon un pulsante positivo disabilitato come potrebbe essere usato nell'immagine sopra.

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

La finestra di dialogo personalizzata può essere eseguita da un'attività come questa:

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

Appunti

  • Per brevità, ho omesso l'interfaccia di comunicazione per riportare le informazioni sulla scelta dell'utente all'attività. La documentazione mostra come ciò avvenga, comunque.
  • Il pulsante è ancora nullattivo, onCreateDialogquindi l'ho disabilitato onResume. Ciò ha l'effetto indesiderabile di disabilitarlo nuovamente se l'utente passa a un'altra app e quindi ritorna senza chiudere la finestra di dialogo. Ciò potrebbe essere risolto anche deselezionando qualsiasi scelta dell'utente o chiamando un Runnableda onCreateDialogper disabilitare il pulsante sul ciclo di esecuzione successivo.

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

Relazionato


33

Ho scritto una semplice classe (un AlertDialogBuilder) che puoi usare per disabilitare la funzione di eliminazione automatica quando premi i pulsanti della finestra di dialogo.

È compatibile anche con Android 1.6, quindi non utilizza OnShowListener (che è disponibile solo API> = 8).

Quindi, invece di utilizzare AlertDialog.Builder, puoi utilizzare questo CustomAlertDialogBuilder. La parte più importante è che non dovresti chiamare create () , ma solo il metodo show () . Ho aggiunto metodi come setCanceledOnTouchOutside () e setOnDismissListener in modo da poterli ancora impostare direttamente sul builder.

L'ho provato su Android 1.6, 2.x, 3.xe 4.x, quindi dovrebbe funzionare abbastanza bene. In caso di problemi, commentare qui.

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

MODIFICA Ecco un piccolo esempio su come utilizzare 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();

Saluti,

Yuvi


Bello. Ma non ha funzionato per me. La finestra di dialogo viene comunque respinta.
Leandros,

Mmm, sembra strano. Lo sto usando nella mia app e solo i pulsanti in cui chiamo esplicitamente dialog.dismiss () chiuderanno la finestra di dialogo. Su quale versione di Android stai testando? Puoi mostrare il tuo codice dove hai usato CustomAlertDialogBuilder?
YuviDroid

Penso che sia causato a causa di questo: (chiama dialog.show () dopo onClickListener) pastebin.com/uLnSu5v7 Se clicco sul pulsante positivo vengono eliminati se il valore booleano è vero ...
Leandros

Non l'ho provato usando Activity.onCreateDialog (). Probabilmente non può funzionare in questo modo. Modificherò la "risposta" per includere un piccolo esempio su come la uso.
YuviDroid

4
Questo funziona per me con l'attuale modifica! Tuttavia: un altro avvertimento. Builder.getContext () è disponibile solo su API 11+. Aggiungi invece un campo Context mContexte impostalo nel costruttore.
Oleg Vaskevich,

28

Ecco qualcosa che stai usando DialogFragment, che è comunque il modo consigliato di gestire i dialoghi.

Quello che succede con il setButton()metodo di AlertDialog (e immagino lo stesso con AlertDialogBuilder's setPositiveButton()e setNegativeButton()) è che il pulsante che imposti (ad es. AlertDialog.BUTTON_POSITIVE) Con esso attiverà effettivamente DUE differentiOnClickListener oggetti quando viene premuto.

Il primo essere DialogInterface.OnClickListener , che è un parametro setButton(), setPositiveButton()e setNegativeButton().

L'altro è View.OnClickListener , che verrà impostato per chiudere automaticamente il tuo AlertDialogquando viene premuto uno qualsiasi dei suoi pulsanti - ed è impostato da AlertDialogsolo.

Quello che puoi fare è usare setButton()con nullcome DialogInterface.OnClickListener, per creare il pulsante e quindi chiamare il tuo metodo di azione personalizzato all'interno View.OnClickListener. Per esempio,

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

    return alertDialog;
}

Poi, si può ignorare l'impostazione predefinita AlertDialog'pulsanti s' View.OnClickListener(che altrimenti chiudere la finestra) nella DialogFragment's onResume()metodo:

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

Dovrai impostarlo nel onResume()metodo perché getButton()tornerà nullfino a quando non verrà mostrata la finestra di dialogo!

Questo dovrebbe far sì che il tuo metodo di azione personalizzato venga chiamato solo una volta e la finestra di dialogo non verrà chiusa per impostazione predefinita.


21

Ispirato dalla risposta di Tom, credo che l'idea qui sia:

  • Impostare onClickListenerdurante la creazione della finestra di dialogo sunull
  • Quindi impostare un onClickListenerdopo che viene visualizzata la finestra di dialogo.

È possibile ignorare il onShowListener Tom simile. In alternativa, puoi

  1. ottenere il pulsante dopo aver chiamato AlertDialog show()
  2. imposta i pulsanti ' onClickListenercome segue (penso che sia leggermente più leggibile).

Codice:

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

8

Per pre API 8 ho risolto il problema usando un flag booleano, un listener di rifiuto e chiamando dialog.show di nuovo nel caso in cui il contenuto del editText non fosse corretto. Come questo:

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;

strano onDismiss non chiamato, il mio è api livello 21
duckduckgo

7

La risposta a questo link è una soluzione semplice, compatibile con l'API 3. È molto simile alla soluzione di Tom Bollwitt, ma senza utilizzare OnShowListener meno compatibile.

Si, puoi. Fondamentalmente devi:

  1. Crea la finestra di dialogo con DialogBuilder
  2. mostra () la finestra di dialogo
  3. Trova i pulsanti nella finestra di dialogo mostrata e sovrascrivi il loro onClickListener

Ho fatto piccoli adattamenti al codice di Kamen da quando stavo estendendo un 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)));
}

Così divertente!


4

Questo codice funzionerà per te, perché ho avuto un problema simile e questo ha funzionato per me. :)

1- Sostituisci metodo Onstart () nella classe framment-dialog.

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

3

Per ProgressDialogs

Per evitare che la finestra di dialogo venga automaticamente chiusa, è necessario impostare il OnClickListenerdopo che ProgressDialogviene mostrato, in questo modo:

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.

    }

});

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

3

puoi aggiungere builder.show (); dopo il messaggio di convalida prima del reso;

come questo

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

}

3

Per evitare che la finestra di dialogo si chiuda quando si fa clic e dovrebbe chiudersi solo quando Internet è disponibile

Sto provando a fare la stessa cosa, poiché non voglio che la finestra di dialogo venga chiusa fino a quando e a meno che Internet non sia connesso.

Ecco il mio codice:

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

    }

Ed ecco il mio codice gestore connettività:

 private boolean ifConnected()
{
    ConnectivityManager connectivityManager= (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo();
   return networkInfo!=null && networkInfo.isConnected();
}

Questo è intelligente, ma ricevo questo messaggio di errore:The specified child already has a parent. You must call removeView() on the child's parent first
Dan Chaltiel il

2

Se stai usando material design, suggerirei di dare un'occhiata ai dialoghi sui materiali . Ha risolto diversi problemi relativi ai bug Android attualmente aperti (vedi 78088 ), ma soprattutto per questo ticket ha un autoDismissflag che può essere impostato quando si utilizza il Builder.


1

Utilizza un layout personalizzato per il tuo DialogFragmente aggiungi un LinearLayoutsotto il tuo contenuto che può essere definito come senza bordi per adattarsi a Google Material Design. Quindi trova i pulsanti appena creati e sovrascrivi i loro OnClickListener.

Esempio:

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>

Questo è il risultato finale.


0

Potrebbe essere costruito nel modo più semplice:

Finestra di avviso con visualizzazione personalizzata e con due pulsanti (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 of Positive Button of Alert 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
        }

    }
}

Fatto


-1

Questa è probabilmente una risposta molto tardiva, ma usare setCancelable farà il trucco.

alertDial.setCancelable(false);

10
Dai documenti: "Imposta se questa finestra di dialogo è cancellabile con il tasto BACK". Questo non ha nulla a che fare con il pulsante positivo che
chiude

3
Non funziona per me, ma ignora ancora quando si fa clic sul pulsante positivo
Hugo

1
Questo non ha nulla a che fare con l'OP
MatPag

1
Non risponde alla domanda
Kevin Crain,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.