PopupWindow - Zamknij po kliknięciu na zewnątrz


94

Mam PopupWindow w moim działaniu, chodzi o to, że moje PopupWindow nadal wyświetla się, nawet gdy wchodzę w interakcję z moją aktywnością (powiedzmy przewijanie mojej listy). Mogę przewijać moją listę, a PopupWindow nadal tam jest.

To, co chcę osiągnąć, to kiedy dotykam / przewijam / klikam / etc na ekranie, który nie jest PopupWindow, chcę odrzucić PopupWindow. Tak jak działa menu. Jeśli kliknąłeś poza menu, menu zostanie odrzucone.

Próbowałem, setOutsideTouchable(true)ale to nie zamknie okna. Dzięki.

Odpowiedzi:


130

Spróbuj zestawu setBackgroundDrawablena PopupWindowktóry powinien zamknąć okno, jeśli dotknąć poza nim.


5
Przegapiłem to. Czy używasz setBackgroundDrawable na swoim popupWindow? Wiem, że ustawienie tła do rysowania na null zabija OnTouchListener
Marcin S.

31
Otóż ​​to! dzięki człowieku! w takim przypadku nawet zdarzenia dotykowe mogą być obsługiwane prawidłowo. popupWindow.setOutsideTouchable (true); popupWindow.setTouchable (true); popupWindow.setBackgroundDrawable (nowy BitmapDrawable ()); popupWindow.setTouchInterceptor (new OnTouchListener () {@Override public boolean onTouch (View v, MotionEvent event) {if (AppContext.isDebugMode ()) Log.d ("POPUP_WINDOW", "v:" + v.getTag () + " | event: "+ event.getAction ()); popupWindow.dismiss (); return true;}});
burza piwna

3
Ustawienie tła do rysowania na null nie działa dla mnie. Jeśli ktoś ma problemy, zobacz moją odpowiedź.
mpellegr

2
@WareNinja, Twój komentarz zadziałał! Może lepiej zostawić całą odpowiedź na ten quesiton, przydałaby się innym
Anton Kizema

3
@WareNinja BitmapDrawable()jest pozbawiony praw. Użyj ColorDrawable()zamiast tego.
Srujan Barai

126

Okazało się, że żadna z udzielonych odpowiedzi nie zadziałała dla mnie, z wyjątkiem komentarza WareNinja dotyczącego zaakceptowanej odpowiedzi, a Marcin S. prawdopodobnie również zadziała. Oto część, która działa dla mnie:

myPopupWindow.setBackgroundDrawable(new BitmapDrawable());
myPopupWindow.setOutsideTouchable(true);

Alternatywnie:

myPopupWindow.setFocusable(true);

Nie jestem pewien, jakie są różnice, ale kod źródłowy ListPopupWindow faktycznie używa tego drugiego, gdy jego modalność jest ustawiona na true za pomocą setModal, więc przynajmniej programiści Androida uważają to za wykonalne podejście i jest to tylko jedna linia.


6
Dziękuję bardzo. Żadna z pozostałych odpowiedzi nie działała dla mnie ani nie wyjaśniała tego wystarczająco dobrze. Jednak druga opcja nie działa dla mnie.
JDN

2
Zwracam również uwagę, że BitmapDrawable jest przestarzały. Byłoby miło mieć prawdziwe rozwiązanie problemu, ponieważ wygląda to na tymczasowe obejście, które nie jest gwarantowane w nowszych wersjach interfejsu API.
HAL9000

aby nie używać przestarzałego konstruktora BitmapDrawable, zobacz tutaj: stackoverflow.com/a/21680637/2048266 . popupWindow.setBackgroundDrawable (nowy BitmapDrawable (getResources (), ""));
nommer

Korzystając z alternatywnej metody setFocusable, musimy dwukrotnie kliknąć przycisk (w miejscu, w którym przycisk znajduje się poza wyskakującym okienkiem), gdzie tak jak w pierwszej metodzie działa dobrze :)
Joy Rex

BitmapDrawable()jest pozbawiony praw. Użyj ColorDrawable()zamiast tego.
Srujan Barai

61

Napotkałem te same problemy i naprawiłem to, jak poniżej kody. U mnie działa dobrze.

    // Closes the popup window when touch outside.
    mPopupWindow.setOutsideTouchable(true);
    mPopupWindow.setFocusable(true);
    // Removes default background.
    mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

BTW, nie używaj przestarzałego konstruktora BitmapDrawable, użyj tego nowego ColorDrawable (android.R.color.transparent) aby zastąpić domyślne tło.

Baw się dobrze@.@


3
Pamiętaj, aby dodać ten kod przed wyświetleniem swojego popoupWindow
snersesyan

Czy naprawdę muszę ustawić fokus na true, jeśli wyskakujące okienko nie wymaga fokusu?
Levor

Jestem zdumiony, że to działa, ale jest to wymagane w API 21. To również naprawiło nieprawidłową animację mojego okna podręcznego.
EpicPandaForce

24

Wiem, że jest późno, ale zauważam, że ludzie nadal mają problem z wyskakującym okienkiem. Postanowiłem napisać w pełni działający przykład, w którym możesz zamknąć wyskakujące okienko, dotykając lub klikając poza nim lub po prostu dotykając samego okna. Aby to zrobić, utwórz nową klasę PopupWindow i skopiuj ten kod:

PopupWindow.class

public class PopupWindow extends android.widget.PopupWindow
{
Context ctx;
Button btnDismiss;
TextView lblText;
View popupView;

public PopupWindow(Context context)
{
    super(context);

    ctx = context;
    popupView = LayoutInflater.from(context).inflate(R.layout.popup, null);
    setContentView(popupView);

    btnDismiss = (Button)popupView.findViewById(R.id.btn_dismiss);
    lblText = (TextView)popupView.findViewById(R.id.text);

    setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
    setWidth(WindowManager.LayoutParams.WRAP_CONTENT);

    // Closes the popup window when touch outside of it - when looses focus
    setOutsideTouchable(true);
    setFocusable(true);

    // Removes default black background
    setBackgroundDrawable(new BitmapDrawable());

    btnDismiss.setOnClickListener(new Button.OnClickListener(){

        @Override
        public void onClick(View v) {


         dismiss();
        }});

    // Closes the popup window when touch it
/*     this.setTouchInterceptor(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction() == MotionEvent.ACTION_MOVE) {
                dismiss();
            }
            return true;
        }
    }); */   
   } // End constructor

   // Attaches the view to its parent anchor-view at position x and y
   public void show(View anchor, int x, int y)
   {
      showAtLocation(anchor, Gravity.CENTER, x, y);
   }
}

Teraz utwórz układ dla wyskakującego okienka: popup.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout     
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="1dp"
    android:orientation="vertical"
    android:padding="10dp" >

<TextView 
    android:id="@+id/text" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"  
    android:gravity="center" 
    android:padding="5dp" 
    android:text="PopupWindow Example"
    android:textColor="#000000" 
    android:textSize="17sp" 
    android:textStyle="italic" />

<FrameLayout
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:layout_gravity="center_vertical">

    <Button
        android:id="@+id/btn_dismiss" 
        style="?android:attr/buttonStyleSmall" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:text="Dismiss" 
        android:visibility="gone" />

    <TextView
        android:id="@+id/lbl_dismiss"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Touch outside of this box to dismiss"
        android:textColor="#ffffff"
        android:textStyle="bold" />

</FrameLayout>      

W swoim głównym działaniu utwórz instancję klasy PopupWindow:

final PopupWindow popupWindow = new PopupWindow(this);
popupWindow.show(findViewById(R.id.YOUR_MAIN_LAYOUT), 0, -250);

gdzie YOUR_MAIN_LAYOUT jest układem bieżącego działania, w którym pojawi się popupWindow


1
Dzięki - to zadziałało dla mnie. Wystarczy jedna mała uwaga, aby raczej użyć innej nazwy niż PopupWindow dla swojej niestandardowej klasy, może nazwać ją MyPopupWindow zamiast Popupwindow, aby system Android nie pomylił się między standardową klasą Androida a klasą niestandardową.
Simon

@Marcin S. findViewById (R.id.YOUR_MAIN_LAYOUT) ?? Czy to będzie R.layout.My_Layout
Ankesh kumar Jaisansaria

@Simon findViewById (R.id.YOUR_MAIN_LAYOUT) ?? Czy będzie to R.layout.My_Layout?
Ankesh kumar Jaisansaria

15

Dzięki za odpowiedź @ LunaKong i potwierdzenie @ HourGlass. Nie chcę powtarzać komentarza, chcę tylko, aby był jasny i zwięzły.

// Closes the popup window when touch outside. This method was written informatively in Google's docs.
mPopupWindow.setOutsideTouchable(true);

// Set focus true to prevent a touch event to go to a below view (main layout), which works like a dialog with 'cancel' property => Try it! And you will know what I mean.
mPopupWindow.setFocusable(true);

// Removes default background.
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

Mttdat.


Chciałem móc zamknąć wyskakujące okienko, klikając poza nim, ale kiedy to zrobiłem, klikano widoki pod nim (nie część wyskakującego okienka, ale część działania). setFocusabl (true) było tym, czego szukałem. Dzięki!
hellaandrew

@hellaandrew, cieszę się, że ci pomaga, :)
Nguyen Tan Dat

8

Aby ListPopupWindowustawić okno jako modalne, gdy jest wyświetlane.

mListPopupWindow.setModal(true);

W ten sposób kliknięcie poza obszarem ListPopupWindowwoli go odrzuci.


Dzięki, właśnie tego szukałem. To nie tylko ustawia listpopupwindow na odrzucenie po dotknięciu poza widokiem, ale także nie przekazuje zdarzenia touch do innych widoków, które są poza listpopwindow. Desperacko tego szukałem, ponieważ w moim przypadku dotknięcie zewnętrznego okna listy przesyła zdarzenie do widoku recyklingu, który znajdował się pod nim, obok odrzucenia okna listpopupwindow, a przedmiot z widoku recyklingu był wybierany, czego nie chciałem.
shankar_vl,

Może być również konieczne mListPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);, aby zapobiec kolidowaniu wyskakującego okienka z klawiaturą ekranową.
Mr-IDE

6

Zwróć uwagę, że aby anulować za pomocą popupWindow.setOutsideTouchable(true), musisz ustawić szerokość i wysokość wrap_contentjak w poniższym kodzie:

PopupWindow popupWindow = new PopupWindow(
            G.layoutInflater.inflate(R.layout.lay_dialog_support, null, false),
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT, true);

popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
popupWindow.showAtLocation(view, Gravity.RIGHT, 0, 0);

5
  popupWindow.setTouchable(true);
  popupWindow.setFocusable(true);
  popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);

Po kliknięciu / dotknięciu ekranu, okno PopupWindow zostanie odrzucone. Upewnij się, że ustawiłeś fokus na true przed showAtLocation.


1
Dodaj tekst wyjaśniający, aby wyjaśnić, w jaki sposób zapewnia to dokładną odpowiedź na zadane pytanie. Dzięki.
filantrovert

Dzięki! Musisz wywołać metody ustawiające przed wywołaniem metody showAtLocation ().
droid256

5

Możesz użyć isOutsideTouchable OR, isFocusable aby zamknąć wyskakujące okienko, gdy dotkniesz na zewnątrz

popupWindow.isOutsideTouchable = true // dismiss popupwindow when touch outside

popupWindow.isFocusable = true // dismiss popupwindow when touch outside AND when press back button

Uwaga

  • Obecnie po teście widzę, setBackgroundDrawable że nie pomaga nam w zamykaniu popupwindow

  • Jeśli spojrzysz na kod zwolnienia w PopupWindow( PopupWindow->PopupDecorView->dispatchKeyEventi PopupWindow->PopupDecorView->onTouchEvent). Zobaczysz, że po naciśnięciu przycisku Wstecz zwalniają, ACTION_UPa gdy dotykają na zewnątrz, zwalniają na ACTION_UPlubACTION_OUTSIDE



4

@LunaKong sugestia działa jak urok.

Ale konfigurowanie mPopupWindow.setFocusable (false). usuwa niepotrzebne dotknięcie wymagane do zniknięcia wyskakującego okienka.

Na przykład: Rozważmy, że na ekranie jest widoczne wyskakujące okienko i masz zamiar kliknąć przycisk. Więc w tym przypadku (jeśli mpopwindow.setFocusable (true)) przy pierwszym kliknięciu przycisku popupwindow zostanie odrzucone. Ale musisz kliknąć ponownie, aby przycisk działał. if ** (mpopwindwo.setFocusable (false) ** pojedyncze kliknięcie przycisku powoduje zamknięcie wyskakującego okienka i kliknięcie przycisku. Mam nadzieję, że to pomoże.


1
Dzięki wielkie! Szukałem dokładnie tego samego
Ganesh

3

Ustaw przezroczyste tło okna:

PopupWindow.getBackground().setAlpha(0);

Po ustawieniu tła w układzie. Działa w porządku.


1
getBackground () może mieć wartość null.
Jared Rummler

1

W niektórych przypadkach ustawianie fokusu na wyskakującym okienku nie jest pożądane (np. Możesz nie chcieć, aby kraść fokus z innego widoku).

Alternatywnym podejściem jest użycie przechwytywacza dotyku:

popupWindow.setOutsideTouchable(true);
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
            popupWindow.dismiss();
        }
        return false;
    }
});

1

Jeśli to wyskakujące okno jest inną czynnością i zmniejszyłeś jego rozmiar do pierwotnego ekranu i chcesz włączyć lub wyłączyć obszar zewnętrzny. Możesz po prostu włączyć lub wyłączyć obszar zewnętrzny za pomocą tego kodu:

włączyć:

YourActivity.this.setFinishOnTouchOutside(true);

wyłączyć:

YourActivity.this.setFinishOnTouchOutside(false);


0

Użyj Wyświetl popupView, aby odrzucić popupWindow

`popupView.setOnClickListener(new View.OnClickListener() {
                   @Override
                   public void onClick(View view) {
                       popupWindow.dismiss();
                   }
               }); 

`Jeśli tego używasz, możesz także ustawić funkcję OnClickListener na dowolny przycisk w oknie popupWindow

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.