Jak zamknąć okno dialogowe, klikając poza oknem?

156

Zaimplementowałem niestandardowe okno dialogowe dla mojej aplikacji. Chcę zaimplementować, że gdy użytkownik kliknie poza oknem dialogowym, okno dialogowe zostanie zamknięte. Co mam z tym zrobić?

Shreyash Mahajan
źródło

Odpowiedzi:

356

Możesz użyć polecenia, dialog.setCanceledOnTouchOutside(true);które zamknie okno dialogowe, jeśli dotkniesz poza nim.

Coś jak,

  Dialog dialog = new Dialog(context)
  dialog.setCanceledOnTouchOutside(true);

Lub jeśli Twój Dialog jest inny niż model,

1 - Ustaw flagę - FLAG_NOT_TOUCH_MODALdla atrybutu okna dialogowego

Window window = this.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);

2 - Dodaj kolejną flagę do właściwości okna, FLAG_WATCH_OUTSIDE_TOUCH- ta opcja służy do odbierania przez okno dialogowe zdarzenia dotyku poza jego widocznym obszarem.

3 - Zastąp onTouchEvent()okno dialogowe i sprawdź typ akcji. jeśli typ akcji to „ MotionEvent.ACTION_OUTSIDE” oznacza, że ​​użytkownik wchodzi w interakcję poza obszarem dialogu. W takim przypadku możesz zamknąć okno dialogowe lub zdecydować, co chcesz wykonać. wyświetlić zwykły druk?

public boolean onTouchEvent(MotionEvent event)  
{  

       if(event.getAction() == MotionEvent.ACTION_OUTSIDE){  
        System.out.println("TOuch outside the dialog ******************** ");  
               this.dismiss();  
       }  
       return false;  
}  

Aby uzyskać więcej informacji, zobacz Jak zamknąć niestandardowe okno dialogowe oparte na punktach dotykowych? i jak zamknąć okno dialogowe niemodalne po dotknięciu poza obszarem dialogu

user370305
źródło
9
Działa to świetnie, z wyjątkiem tego, że aktywność pod spodem również reaguje na zdarzenie dotykowe. Czy jest jakiś sposób, aby temu zapobiec?
howettl
Tak. window.setFlags (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); powoduje ten problem. Poniżej zamieściłem rozwiązanie :)
Unknownweirdo
Czy możliwe jest propagowanie zdarzeń „on-touch-outside” do działań pod spodem również przy użyciu niestandardowego okna dialogowego?
jc
1
@howettl Rozwiązałem twój problem w moim rozwiązaniu, które zamieściłem poniżej, gdzie nie muszę ustawiać żadnych flag w oknie.
Roman Nazarevych
@MuhammedRefaat - spójrz na ten wątek groups.google.com/forum/#!topic/android-developers/VhaiIMl6E_w . Ładnie to opisali.
user370305
18

Po prostu użyj

dialog.setCanceledOnTouchOutside(true);
Ebin Sebastian
źródło
Wiem, że to powinna być prawidłowa odpowiedź, ale nie działa na mnie i po prostu nie wiem dlaczego.
IgniteCoders
16

Możesz użyć tej implementacji onTouchEvent. Zapobiega reagowaniu pod działaniem na zdarzenie dotykowe (jak wspomniano w Howettl).

@Override
public boolean onTouchEvent ( MotionEvent event ) {
  // I only care if the event is an UP action
  if ( event.getAction () == MotionEvent.ACTION_UP ) {
    // create a rect for storing the window rect
    Rect r = new Rect ( 0, 0, 0, 0 );
    // retrieve the windows rect
    this.getWindow ().getDecorView ().getHitRect ( r );
    // check if the event position is inside the window rect
    boolean intersects = r.contains ( (int) event.getX (), (int) event.getY () );
    // if the event is not inside then we can close the activity
    if ( !intersects ) {
      // close the activity
      this.finish ();
      // notify that we consumed this event
      return true;
    }
  }
  // let the system handle the event
  return super.onTouchEvent ( event );
}

Źródło: http://blog.twimager.com/2010/08/closing-activity-by-touching-outside.html

Lukas Novak
źródło
9

Lub, jeśli dostosowujesz okno dialogowe za pomocą motywu zdefiniowanego w twoim stylu xml, umieść ten wiersz w swoim motywie:

<item name="android:windowCloseOnTouchOutside">true</item>
Chris.Zou
źródło
U mnie to nie działa na Samsung Galaxy Tab 2 WiFi. dialog.setCanceledOnTouchOutside(true);działa cudownie.
doplumi
7
dialog.setCanceledOnTouchOutside(true); 

aby zamknąć okno dialogowe dotyku na zewnątrz.

A jeśli nie chcesz zamykać na zewnątrz dotykiem, użyj poniższego kodu:

dialog.setCanceledOnTouchOutside(false);
Naveen
źródło
6

Ta metoda powinna całkowicie unikać działań poniżej szarego obszaru pobierających zdarzenia kliknięcia.

Usuń tę linię, jeśli ją masz:

window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);

Umieść to na utworzonej aktywności

getWindow().setFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);

następnie zastąp zdarzenie dotyku tym

@Override
public boolean onTouchEvent(MotionEvent ev)
{
    if(MotionEvent.ACTION_DOWN == ev.getAction())
    {
        Rect dialogBounds = new Rect();
        getWindow().getDecorView().getHitRect(dialogBounds);
        if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
            // You have clicked the grey area
            displayYourDialog();
            return false; // stop activity closing
        }
    }

    // Touch events inside are fine.
    return super.onTouchEvent(ev);
}
Nieznany dziwak
źródło
4

Możesz spróbować tego: -

AlterDialog alterdialog;
alertDialog.setCanceledOnTouchOutside(true);

lub

alertDialog.setCancelable(true);

A jeśli masz AlterDialog.Builder, możesz spróbować tego: -

alertDialogBuilder.setCancelable(true);
Julfikar
źródło
4

Ten kod jest używany do kliknięcia okna dialogowego, które powoduje ukrycie wejścia i gdy użytkownik kliknie zewnętrzną stronę okna dialogowego, gdy zarówno programowe wejście, jak i okno dialogowe są zamknięte.

dialog = new Dialog(act) {
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Tap anywhere to close dialog.
        Rect dialogBounds = new Rect();
        getWindow().getDecorView().getHitRect(dialogBounds);
        if (!dialogBounds.contains((int) event.getX(),
                (int) event.getY())) {
            // You have clicked the grey area
            InputMethodManager inputMethodManager = (InputMethodManager) act
                    .getSystemService(act.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(dialog
                    .getCurrentFocus().getWindowToken(), 0);
            dialog.dismiss();
            // stop activity closing
        } else {
            InputMethodManager inputMethodManager = (InputMethodManager) act
                    .getSystemService(act.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(dialog
                    .getCurrentFocus().getWindowToken(), 0);
        }

        return true;
    }
};
Maulik Santoki
źródło
2

Inne rozwiązanie, ten kod został wzięty z kodu źródłowego Androida Window Powinieneś po prostu dodać te dwie metody do kodu źródłowego okna dialogowego.

@Override
public boolean onTouchEvent(MotionEvent event) {        
    if (isShowing() && (event.getAction() == MotionEvent.ACTION_DOWN
            && isOutOfBounds(getContext(), event) && getWindow().peekDecorView() != null)) {
        hide();
    }
    return false;
}

private boolean isOutOfBounds(Context context, MotionEvent event) {
    final int x = (int) event.getX();
    final int y = (int) event.getY();
    final int slop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
    final View decorView = getWindow().getDecorView();
    return (x < -slop) || (y < -slop)
            || (x > (decorView.getWidth()+slop))
            || (y > (decorView.getHeight()+slop));
}

To rozwiązanie nie ma tego problemu:

Działa to świetnie, z wyjątkiem tego, że aktywność pod spodem również reaguje na zdarzenie dotykowe. Czy jest jakiś sposób, aby temu zapobiec? - howettl

Roman Nazarevych
źródło
Nie możesz po prostu zmienić trybu na dotykowe, jeśli nie chcesz, aby inne okna otrzymywały zdarzenia?
1

Zadzwoń dialog.setCancelable(false);z Twojej aktywności / fragmentu.

EKN
źródło
1

Pomogło mi:

myDialog.setCanceledOnTouchOutside(true);
Akanshi Srivastava
źródło
0

Możesz zająć backgroundcały rozmiar ekranu transparenti słuchać tego onClickwydarzenia dismiss.

oriolpons
źródło
10
Bardzo zła odpowiedź! Oczywiście można to zrobić, ale zrób to we właściwy sposób!
jc
-1

Oto kod

    dialog.getWindow().getDecorView().setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent ev) {

            if(MotionEvent.ACTION_DOWN == ev.getAction())
            {
                Rect dialogBounds = new Rect();
                dialog. getWindow().getDecorView().getHitRect(dialogBounds);
                if (!dialogBounds.contains((int) ev.getX(), (int) ev.getY())) {
                    // You have clicked the grey area
                    UiUtils.hideKeyboard2(getActivity());
                    return false; // stop activity closing
                }
            }
            getActivity().dispatchTouchEvent(ev);
            return false;
        }
    });

Spróbuj tego . możesz ukryć klawiaturę, gdy dotkniesz na zewnątrz

Saljith Kj
źródło