Jak zapobiec zamknięciu okna dialogowego po kliknięciu przycisku


731

Mam okno dialogowe EditTextdo wprowadzania danych. Kiedy kliknę przycisk „tak” w oknie dialogowym, potwierdzi wejście, a następnie zamknie okno dialogowe. Jeśli jednak dane wejściowe są nieprawidłowe, chcę pozostać w tym samym oknie dialogowym. Za każdym razem, bez względu na dane wejściowe, okno dialogowe powinno zostać automatycznie zamknięte po kliknięciu przycisku „nie”. Jak mogę to wyłączyć? Nawiasem mówiąc, użyłem PositiveButton i NegativeButton jako przycisku w oknie dialogowym.

Odpowiedzi:


916

EDYTOWAĆ: Działa to tylko w API 8+, jak zauważono w niektórych komentarzach.

To późna odpowiedź, ale możesz dodać onShowListener do AlertDialog, gdzie możesz następnie zastąpić onClickListener przycisku.

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
Hej, lepiej późno niż wcale, szukałem właśnie tego, dziękuję, +1 :) To elegancki sposób na dodanie sprawdzania poprawności do twojego okna dialogowego, zwłaszcza gdy masz już klasę owijania pomocników do obsługi alertów
Guillaume,

11
Nie działa AlertDialog.Builder.setOnShowListener nie istnieje. developer.android.com/reference/android/app/…
Leandros

4
W API w wersji wcześniejszej niż 8 możesz wywołać d.getButton (AlertDialog.BUTTON_POSITIVE); ponieważ jest to metoda publiczna, ale musisz ją nazwać show (); zostało wydane, w przeciwnym razie otrzymasz po prostu zero
Hurda

13
Możesz uczynić to nawet odrobinę czystszym, ustawiając null OnClickListener w narzędziu do tworzenia okien dialogowych (zapisuje pusty nasłuchiwanie „// to zostanie przesłonięte”).
Steve Haley,

1
Działa również dobrze z DialogFragments, gdy AlertDialog jest tworzony w metodzie onCreateDialog (Bundes ZapisaneInstanceState).
Christian Lischnig,

654

Oto niektóre rozwiązania dla wszystkich typów okien dialogowych, w tym rozwiązanie dla AlertDialog.Builder, które będzie działać na wszystkich poziomach API (działa poniżej API 8, czego druga odpowiedź tutaj nie robi). Istnieją rozwiązania dla AlertDialogs za pomocą AlertDialog.Builder, DialogFragment i DialogPreference.

Poniżej znajdują się przykłady kodu pokazujące, jak zastąpić domyślną procedurę obsługi przycisków wspólnych i zapobiec zamknięciu okna dialogowego dla tych różnych form okien dialogowych. Wszystkie przykłady pokazują, jak zapobiec zamykaniu okna dialogowego przez przycisk dodatni.

Uwaga: opis działania zamknięcia okna dialogowego pod maską dla podstawowych klas Androida i dlaczego wybrano następujące podejścia po przykładach dla tych, którzy chcą więcej szczegółów


AlertDialog.Builder - Zmień domyślną procedurę obsługi przycisków natychmiast po 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 - przesłonięcie 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 - przesłonięcie 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.
                }
            });
}

Wyjaśnienie podejść:

Przeglądając kod źródłowy Androida, domyślna implementacja AlertDialog działa poprzez rejestrację wspólnej procedury obsługi przycisków dla wszystkich rzeczywistych przycisków w OnCreate (). Po kliknięciu przycisku wspólny moduł obsługi przycisku przekazuje zdarzenie click do dowolnego modułu obsługi przekazanego w setButton (), a następnie wywołuje zamknięcie okna dialogowego.

Jeśli chcesz uniemożliwić zamknięcie okna dialogowego po naciśnięciu jednego z tych przycisków, musisz zastąpić zwykłą procedurę obsługi przycisków dla rzeczywistego widoku przycisku. Ponieważ jest on przypisany w OnCreate (), należy go zastąpić po wywołaniu domyślnej implementacji OnCreate (). Funkcja OnCreate jest wywoływana w procesie metody show (). Możesz utworzyć niestandardową klasę Dialog i przesłonić OnCreate (), aby wywołać funkcję super.OnCreate (), a następnie przesłonić procedury obsługi przycisków, ale jeśli utworzysz niestandardowe okno dialogowe, nie otrzymasz Konstruktora za darmo, w takim przypadku o co chodzi ?

Tak więc, używając okna dialogowego tak, jak zostało zaprojektowane, ale kontrolując, kiedy jest ono usuwane, jednym z podejść jest wywołanie dialog.Show (), a następnie uzyskanie odwołania do przycisku za pomocą dialog.getButton () w celu zastąpienia modułu obsługi kliknięć. Innym podejściem jest użycie setOnShowListener () i implementacja znajdowania widoku przycisku i zastępowania modułu obsługi w OnShowListener. Różnica funkcjonalna między nimi to „prawie” zero, w zależności od tego, który wątek oryginalnie tworzy instancję okna dialogowego. Przeglądając kod źródłowy, onShowListener zostaje wywołany przez komunikat wysłany do modułu obsługi działającego w wątku, który utworzył to okno dialogowe. Tak więc, ponieważ Twój OnShowListener jest wywoływany przez wiadomość umieszczoną w kolejce komunikatów, technicznie możliwe jest, że wywołanie twojego odbiornika zostanie opóźnione po pewnym czasie po zakończeniu pokazu.

Dlatego uważam, że najbezpieczniejszym podejściem jest pierwsze: wywołać show.Dialog (), a następnie natychmiast w tej samej ścieżce wykonania zastąpić procedury obsługi przycisków. Ponieważ kod wywołujący metodę show () będzie działał w głównym wątku GUI, oznacza to, że dowolny kod, po którym następuje show (), zostanie wykonany przed jakimkolwiek innym kodem w tym wątku, podczas gdy czas metody OnShowListener jest na łasce kolejka wiadomości.


12
Jest to zdecydowanie najłatwiejsza implementacja i działa idealnie. Użyłem AlertDialog.Builder - Zmień domyślny moduł obsługi przycisków natychmiast po show () i działa jak urok.
Reinherd

1
@ sogger koleś, całkowicie odważnie zredagowałem twoją niesamowitą odpowiedź, ponieważ w części 1 miałeś lekceważenie (); zamiast wierzę dialog.dismiss (); wielkie dzięki za niesamowitą odpowiedź!
Fattie

Czy jest jakiś sposób, aby zapobiec zamknięciu a ProgressDialogpo kliknięciu na niego przycisku?
Joshua Pinter

1
święta krowa, im więcej wiem o Androidzie, tym bardziej czuję się zniesmaczony ... wszystko po to, aby proste okno dialogowe działało poprawnie. zastanawianie się, jak wyświetlić okno dialogowe, zajmuje kilka godzin
SpaceMonkey

1
@harsh_v zaktualizował odpowiedź, aby użyć onResume () dla następnej osoby, dzięki!
Sogger,

37

Alternatywne rozwiązanie

Chciałbym przedstawić alternatywną odpowiedź z perspektywy UX.

Dlaczego chcesz uniemożliwić zamknięcie okna dialogowego po kliknięciu przycisku? Przypuszczalnie dzieje się tak, ponieważ masz niestandardowe okno dialogowe, w którym użytkownik nie dokonał wyboru lub jeszcze nie wypełnił wszystkiego. A jeśli nie są ukończone, nie należy w ogóle pozwalać im klikać przycisku dodatniego. Wyłącz ją, aż wszystko będzie gotowe.

Inne odpowiedzi tutaj zawierają wiele sztuczek, które zastępują pozytywne kliknięcie przycisku. Gdyby to było ważne, czy Android nie stworzyłby dogodnej metody? Nie zrobili tego.

Zamiast tego przewodnik projektowania okien dialogowych pokazuje przykład takiej sytuacji. Przycisk OK jest wyłączony, dopóki użytkownik nie dokona wyboru. W ogóle nie są konieczne żadne nadrzędne sztuczki. Dla użytkownika jest oczywiste, że przed kontynuowaniem należy jeszcze coś zrobić.

wprowadź opis zdjęcia tutaj

Jak wyłączyć przycisk dodatni

Zobacz dokumentację Androida, aby utworzyć niestandardowy układ okna dialogowego . Zaleca się umieszczenie AlertDialogwewnątrzDialogFragment . Następnie wszystko, co musisz zrobić, to ustawić detektory na elementach układu, aby wiedziały, kiedy włączyć lub wyłączyć przycisk dodatni.

Przycisk dodatni można wyłączyć w następujący sposób:

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

Oto cała praca DialogFragmentz wyłączonym przyciskiem dodatnim, takim jak na powyższym obrazku.

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

Niestandardowe okno dialogowe można uruchomić z działania takiego jak to:

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

Notatki

  • Ze względu na zwięzłość pominąłem interfejs komunikacyjny, aby przekazać informacje o wyborze użytkownika z powrotem do działania. W dokumentacji pokazuje, jak to zrobić, choć.
  • Przycisk jest nadal nullw onCreateDialogtak ja go wyłączył onResume. Ma to niepożądany efekt ponownego wyłączenia go, jeśli użytkownik przełączy się na inną aplikację, a następnie wróci bez zamykania okna dialogowego. Można to rozwiązać również przez odznaczenie dowolnych wyborów użytkownika lub przez wywołanie Runnablez w onCreateDialogcelu wyłączenia przycisku w następnej pętli uruchamiania.

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

Związane z


33

Napisałem prostą klasę (AlertDialogBuilder), której można użyć do wyłączenia funkcji automatycznego odrzucania po naciśnięciu przycisków okna dialogowego.

Jest kompatybilny również z Androidem 1.6, więc nie korzysta z OnShowListener (który jest dostępny tylko API> = 8).

Zamiast korzystać z AlertDialog.Builder, możesz użyć tego CustomAlertDialogBuilder. Najważniejsze jest to, że nie powinieneś wywoływać metody create () , a jedynie metodę show () . Dodałem metody takie jak setCanceledOnTouchOutside () i setOnDismissListener , abyś mógł nadal ustawiać je bezpośrednio w .

Przetestowałem to na Androidzie 1.6, 2.x, 3.x i 4.x, więc powinno działać całkiem dobrze. Jeśli znajdziesz jakieś problemy, prosimy o komentarz tutaj.

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

EDYCJA Oto mały przykład użycia 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();

Twoje zdrowie,

Yuvi


Miły. Ale nie działało dla mnie. Dialog zostaje jednak zwolniony.
Leandros,

Mmm, to brzmi dziwnie. Używam tego w mojej aplikacji i tylko przyciski, w których jawnie wywołuję dialog.dismiss (), zamkną okno dialogowe. Na jakiej wersji Androida testujesz? Czy możesz pokazać swój kod w miejscu, w którym korzystałeś z CustomAlertDialogBuilder?
YuviDroid

Myślę, że jest to spowodowane z tego powodu: (wywołanie dialog.show () po onClickListener) pastebin.com/uLnSu5v7 Po kliknięciu przycisku dodatniego zostaną one odrzucone, jeśli wartość logiczna jest prawdziwa ...
Leandros

Nie przetestowałem go za pomocą Activity.onCreateDialog (). Prawdopodobnie nie może działać w ten sposób. Zmienię „odpowiedź”, aby podać mały przykład tego, jak z niej korzystam.
YuviDroid

4
Działa to dla mnie przy bieżącej edycji! Jednak: Jeszcze jedno zastrzeżenie. Builder.getContext () jest dostępny tylko w API 11+. Dodaj pole Context mContexti ustaw je w konstruktorze.
Oleg Waszkiewicz

28

Oto coś, jeśli używasz DialogFragment- co jest i tak zalecanym sposobem obsługi okien dialogowych.

Co się dzieje z AlertDialog w setButton()sposobie (i wyobrażam sobie to samo z AlertDialogBuilder„s setPositiveButton()a setNegativeButton()) jest to, że przycisk można ustawić (na przykład AlertDialog.BUTTON_POSITIVE) z nim rzeczywiście wywołać dwa różneOnClickListener obiekty po naciśnięciu.

Pierwsza istota DialogInterface.OnClickListener , który jest parametrem setButton(), setPositiveButton()oraz setNegativeButton().

Drugi to View.OnClickListener , który zostanie ustawiony tak, aby automatycznie odrzucał twoje AlertDialognaciśnięcie dowolnego przycisku - i jest ustawiany AlertDialogsam.

Co można zrobić, to skorzystać setButton()z nulljak DialogInterface.OnClickListener, aby utworzyć przycisk, a następnie połączyć się niestandardową metodę działania wewnątrz View.OnClickListener. Na przykład,

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

    return alertDialog;
}

Następnie można przesłonić domyślne AlertDialog„s przyciski” View.OnClickListener(które inaczej byłoby zamknąć okno) w DialogFragment„s onResume()metody:

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

Będziesz musiał ustawić to w onResume()metodzie, ponieważ getButton()powróci nulldo momentu wyświetlenia okna dialogowego!

Powinno to spowodować wywołanie niestandardowej metody akcji tylko raz, a okno dialogowe nie zostanie domyślnie odrzucone.


21

Zainspirowany odpowiedzią Toma, myślę, że pomysł jest następujący:

  • Ustaw onClickListenerpodczas tworzenia okna dialogowego wartośćnull
  • Następnie ustaw onClickListenerpo wyświetleniu okna dialogowego.

Możesz zastąpić onShowListenerpodobnego Toma. Alternatywnie możesz

  1. dostać przycisk po wywołaniu AlertDialog show()
  2. ustaw przyciski onClickListenerw następujący sposób (myślę, że nieco bardziej czytelny).

Kod:

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

W przypadku wersji wcześniejszych niż API 8 rozwiązałem problem za pomocą flagi boolean, nasłuchiwania z odrzucenia i wywołania okna dialogowego. Pokaż ponownie, jeśli zawartość edytowanego tekstu nie jest poprawna. Lubię to:

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;

Dziwne, nie zadzwoń, mój jest api poziom 21
duckduckgo

7

Odpowiedź pod tym linkiem jest prostym rozwiązaniem, które jest kompatybilne bezpośrednio z API 3. Jest bardzo podobne do rozwiązania Toma Bollwitta, ale bez korzystania z mniej kompatybilnego OnShowListener.

Tak, możesz. Zasadniczo musisz:

  1. Utwórz okno dialogowe za pomocą DialogBuilder
  2. pokaż () okno dialogowe
  3. Znajdź przyciski w pokazanym oknie dialogowym i zastąp ich przyciski onClickListener

Wprowadziłem drobne zmiany w kodzie Kamena od czasu rozszerzenia 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)));
}

Taka zabawa!


4

Ten kod będzie działał dla ciebie, ponieważ miałem podobny problem i to zadziałało dla mnie. :)

1- Zastąp metodę Onstart () w klasie fragmentu okna dialogowego.

@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

Dla ProgressDialogs

Aby zapobiec automatycznemu zamykaniu okna dialogowego, musisz ustawić następujące OnClickListenerpo nim ProgressDialog, tak:

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

możesz dodać builder.show (); po potwierdzeniu wiadomość przed powrotem;

lubię to

    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

Aby zapobiec zamykaniu okna dialogowego po kliknięciu i powinno ono zamykać się tylko wtedy, gdy Internet jest dostępny

Próbuję zrobić to samo, ponieważ nie chcę, aby okno dialogowe było zamknięte, dopóki Internet nie zostanie podłączony.

Oto mój kod:

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

    }

A oto mój kod menedżera łączności:

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

To sprytne, ale pojawia się ten komunikat o błędzie:The specified child already has a parent. You must call removeView() on the child's parent first
Dan Chaltiel,

2

Jeśli używasz material design, proponuję sprawdzić okna dialogowe materiałów . Naprawiono dla mnie kilka problemów związanych z aktualnie otwartymi błędami Androida (patrz 78088 ), ale co najważniejsze dla tego biletu ma autoDismissflagę, którą można ustawić podczas korzystania z Builder.


1

Użyj niestandardowy układ dla twojego DialogFragmenti dodaj LinearLayoutmocy swojej treści, które mogą być stylizowany jako obramowania, aby dopasować Google projektowania materiałów. Następnie znajdź nowo utworzone przyciski i zastąp jeOnClickListener .

Przykład:

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>

To jest wynik końcowy.


0

Można go zbudować w najprostszy sposób:

Okno dialogowe alarmu z widokiem niestandardowym i dwoma przyciskami (dodatni i ujemny).

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
        }

    }
}

Gotowy


-1

Prawdopodobnie jest to bardzo późna odpowiedź, ale załatwienie problemu zrobi setCancelable.

alertDial.setCancelable(false);

10
Z dokumentów: „Ustawia, czy to okno dialogowe można anulować za pomocą przycisku BACK”. Nie ma to nic wspólnego z dodatnim przyciskiem
zamykającym

3
Nie działa dla mnie, nadal zwalniam po kliknięciu pozytywnego przycisku
Hugo

1
To nie ma nic wspólnego z OP
MatPag,

1
Nie odpowiada na pytanie
Kevin Crain,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.