PopupWindow - закрываться при нажатии снаружи

94

У меня есть PopupWindow для моей активности, дело в том, что мое PopupWindow все еще отображается, даже когда я взаимодействую со своей деятельностью (например, прокручиваю свой список). Я могу пролистать свой список, а PopupWindow все еще там.

Я хочу добиться, чтобы когда я касался / прокручиваю / щелкаю / и т. Д. На экране, который не является PopupWindow, я хочу отклонить PopupWindow. Так же, как работает меню. Если вы щелкнули вне меню, меню будет закрыто.

Я пробовал, setOutsideTouchable(true)но окно не закрывается. Спасибо.

крестьянин
источник

Ответы:

130

Пожалуйста , попробуйте установить setBackgroundDrawableна PopupWindowтом , что следует закрыть окно , если вы касаетесь за его пределами.

Марцин С.
источник
5
Я пропустил это. Вы используете setBackgroundDrawable в своем popupWindow? Я знаю, что установка фона для рисования на null убивает OnTouchListener
Marcin S.
31
вот и все! спасибо человек! в этом случае можно правильно обрабатывать даже события касания. popupWindow.setOutsideTouchable (true); popupWindow.setTouchable (true); popupWindow.setBackgroundDrawable (новый BitmapDrawable ()); popupWindow.setTouchInterceptor (new OnTouchListener () {@Override public boolean onTouch (View v, событие MotionEvent) {if (AppContext.isDebugMode ()) Log.d ("POPUP_WINDOW", "v:" + v.getTag () + " | событие: "+ event.getAction ()); popupWindow.dismiss (); return true;}});
beerstorm 05
3
Установка фона для рисования на null для меня не работает. Если у кого-то еще есть проблемы, см. Мой ответ.
mpellegr
2
@WareNinja, ваш комментарий сработал! Может, тебе лучше оставить на этот вопрос полный ответ, он будет полезен другим
Антон Кизема
3
@WareNinja BitmapDrawable()не работает. ColorDrawable()Вместо этого используйте .
Srujan Barai
126

Я обнаружил, что ни один из предоставленных ответов не работал для меня, за исключением комментария WareNinja к принятому ответу, и, вероятно, также сработает Марцин С. Вот что мне подходит:

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

Альтернативно:

myPopupWindow.setFocusable(true);

Не уверен, в чем разница, но исходный код ListPopupWindow фактически использует последний, когда для его модальности установлено значение true с помощью setModal, поэтому, по крайней мере, разработчики Android считают этот подход жизнеспособным, и это всего лишь одна строка.

mpellegr
источник
6
Огромное спасибо. Ни один из других ответов не помог мне и не объяснил это достаточно хорошо. Но второй вариант мне не подходит.
JDN
2
Также отмечу, что BitmapDrawable устарел. Было бы неплохо иметь реальное решение проблемы, так как это выглядит как временный обходной путь, который не гарантированно поддерживается в новых версиях API.
HAL9000
чтобы не использовать устаревший конструктор BitmapDrawable, см. здесь: stackoverflow.com/a/21680637/2048266 . popupWindow.setBackgroundDrawable (новый BitmapDrawable (getResources (), ""));
nommer
При использовании альтернативного метода setFocusableнам нужно дважды щелкнуть кнопку (где кнопка находится за пределами всплывающего окна), где, как и в первом методе, он работает нормально :)
Джой Рекс
BitmapDrawable()деприминирован. ColorDrawable()Вместо этого используйте .
Srujan Barai
61

Я столкнулся с теми же проблемами и исправил их, как показано ниже. Он отлично работает для меня.

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

Кстати, не используйте устаревший конструктор BitmapDrawable, используйте этот новый ColorDrawable (android.R.color.transparent) для замены фона по умолчанию.

Радоваться, веселиться@.@

Луна Конг
источник
3
Не забудьте добавить этот код, прежде чем показывать свое popoupWindow
snersesyan
Мне действительно нужно установить для focusable значение true, если всплывающее окно не нуждается в фокусе?
Levor
Я удивлен, что это работает, но это требуется для API 21. Это также исправило некорректную анимацию всплывающего окна.
EpicPandaForce
24

Я знаю, что уже поздно, но замечаю, что у людей все еще проблема со всплывающим окном. Я решил написать полностью рабочий пример, в котором вы можете закрыть всплывающее окно, коснувшись или щелкнув за его пределами, или просто коснувшись самого окна. Для этого создайте новый класс PopupWindow и скопируйте этот код:

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

Теперь создайте макет для всплывающего окна: 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>      

В своей основной деятельности создайте экземпляр класса PopupWindow:

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

где YOUR_MAIN_LAYOUT - это макет текущего действия, в котором всплывает popupWindow

Марцин С.
источник
1
Спасибо, у меня это сработало. Одно небольшое замечание - лучше использовать другое имя, отличное от PopupWindow для вашего пользовательского класса, возможно, называть его MyPopupWindow вместо Popupwindow, чтобы Android не путался между вашим стандартным классом Android и вашим пользовательским классом.
Саймон
@Marcin S. findViewById (R.id.YOUR_MAIN_LAYOUT) ?? Будет ли это R.layout.My_Layout
Анкеш кумар Джайсансария
@Simon findViewById (R.id.YOUR_MAIN_LAYOUT) ?? Это будет R.layout.My_Layout?
Анкеш Кумар Джайсансария
15

Спасибо за ответ @ LunaKong и подтверждение @ HourGlass. Я не хочу дублировать комментарий, а хочу лишь сделать его ясным и кратким.

// 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.

Нгуен Тан Дат
источник
Я хотел иметь возможность закрыть всплывающее окно, щелкнув за его пределами, но когда я это сделал, виды под ним (не часть всплывающего окна, а часть действия) щелкались. setFocusabl (true) - это то, что я искал. Благодарность!
hellaandrew
@hellaandrew, рад, что это вам помогло :)
Нгуен Тан Дат
8

Для ListPopupWindowустановки окно будет модальным при отображении.

mListPopupWindow.setModal(true);

Таким образом, щелчок за пределами ListPopupWindowзакроет его.

toobsco42
источник
Спасибо, я просто ждал этого. Это не только устанавливает listpopupwindow как закрываемое после касания за пределами представления, но также не передает событие касания другим представлениям, которые находятся рядом со listpopwindow. Я отчаянно искал это, так как в моем случае касание внешнего listpopwindow передавало событие в recyclerview, которое было под ним, рядом с закрытием listpopupwindow, и выбирался элемент recyclerview, которого я не хотел.
shankar_vl 04
Вам также может потребоваться mListPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);, чтобы всплывающее окно не мешало работе экранной клавиатуры.
Mr-IDE
6

Обратите внимание, что для отмены с помощью popupWindow.setOutsideTouchable(true)вам необходимо указать ширину и высоту, wrap_contentкак показано в приведенном ниже коде:

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

Он закроет PopupWindow при нажатии / касании на экране. Убедитесь, что вы установили focusable true перед showAtLocation.

андроид
источник
1
Пожалуйста, добавьте пояснительный текст, чтобы уточнить, как это дает точный ответ на заданный вопрос. Спасибо.
филантроверт
Благодарность! Вам нужно вызвать сеттеры перед вызовом showAtLocation ().
droid256
5

Вы можете использовать isOutsideTouchable ИЛИ, isFocusable чтобы закрыть всплывающее окно при касании снаружи

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

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

Заметка

  • В настоящее время после теста я вижу, setBackgroundDrawable что не помогает нам закрыть всплывающее окно

  • Если вы посмотрите код для увольнения в PopupWindow( PopupWindow->PopupDecorView->dispatchKeyEventи PopupWindow->PopupDecorView->onTouchEvent). Вы увидите, что при нажатии кнопки возврата они закрываются, ACTION_UPа при касании снаружи они закрываются на ACTION_UPилиACTION_OUTSIDE

Фан Ван Линь
источник
4
mPopWindow.setFocusable(true);
Мухаммад Аамир Али
источник
1
Это единственное, что требуется. Я не понимаю, почему принятый ответ получил столько голосов.
sziraqui
4

Предложение @LunaKong работает как шарм.

Но настройка mPopupWindow.setFocusable (false). удаляет ненужные прикосновения, необходимые для исчезновения всплывающего окна.

Например: Предположим, на экране появляется всплывающее окно, и вы собираетесь нажать кнопку. Итак, в этом случае (если mpopwindow.setFocusable (true)) при первом нажатии кнопки всплывающее окно закрывается. Но вам нужно щелкнуть еще раз, чтобы кнопка заработала. if ** (mpopwindwo.setFocusable (false) ** однократное нажатие кнопки закрывает всплывающее окно, а также запускает нажатие кнопки. Надеюсь, это поможет.

Песочные часы
источник
1
Большое спасибо! Я точно искал то же самое
Ганеш
3

Установите прозрачный фон окна:

PopupWindow.getBackground().setAlpha(0);

После этого установите фон в макете. Работает отлично.

Амак
источник
1
getBackground () может иметь значение null.
Джаред Раммлер,
1

В некоторых случаях делать всплывающее окно фокусируемым нежелательно (например, вы можете не захотеть, чтобы оно отвлекало фокус от другого представления).

Альтернативный подход - использование перехватчика касаний:

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;
    }
});
dev.bmax
источник
1

Если это всплывающее окно является другим действием, и вы уменьшили его размер до исходного экрана и хотите включить или отключить внешнюю область, вы можете просто включить или отключить внешнюю область с помощью этого кода:

включить:

YourActivity.this.setFinishOnTouchOutside(true);

отключить:

YourActivity.this.setFinishOnTouchOutside(false);

Яссер Эльгрити
источник
0

Используйте View popupView, чтобы закрыть popupWindow

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

`Если вы используете это, вы также можете установитьOnClickListener на любую кнопку внутри popupWindow

Прабхат Кумар Сингх
источник