Разрешить внешнее касание для DialogFragment

У меня есть Fragment в моем приложении, которое показывает DialogFragment.
Во фрагменте есть кнопка, которая закрывает диалог. Но когда я показываю dialogFragment, касания снаружи диалога ничего не делают, и я не могу нажимать кнопки снаружи фрагмента диалога.

Как я могу разрешить внешние касания для DialogFragment?


person Yaniv    schedule 13.03.2013    source источник


Ответы (8)


Для этого должен быть включен флаг Window, разрешающий внешнее касание, и для хорошего внешнего вида должен быть снят флаг затемнения фона.
Поскольку это необходимо сделать после создания диалога, я реализовал это через Handler.

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    // This is done in a post() since the dialog must be drawn before locating.
    getView().post(new Runnable() {

        @Override
        public void run() {

            Window dialogWindow = getDialog().getWindow();

            // Make the dialog possible to be outside touch
            dialogWindow.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
            dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

            getView().invalidate();
        }
    });
}

В этот момент возможен внешний контакт.

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

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Hide title of the dialog
    setStyle(STYLE_NO_FRAME, 0);
}
person Yaniv    schedule 14.03.2013
comment
У вас есть идеи, как добиться того же для BottomSheetDialogFragment? - person elementstyle; 11.04.2019
comment
@Yanix Это не работает, когда DialogFragment установлен в полноэкранный режим window.setLayout(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT) - person Arun; 30.10.2019
comment
Это работает в случае Android 6, 7, 7.1, 8, но не работает в случае Android 9. На Android 9 не происходит никаких кликов за пределами DialogFragment. - person Leszek; 13.04.2020

Это более общий флаг, разрешающий любое действие, а не только сенсорное.

window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
person Ayman Mahgoub    schedule 20.12.2014

Я нашел альтернативный способ справиться с отклонением при касании снаружи. Пожалуйста, проверьте пример кода ниже, это определенно сработает,

@Override
public void onStart() {
    // mDialogView is member variable
    mDialogView = getView();
    mDialogView.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            float eventX = event.getRawX();
            float eventY = event.getRawY();

            int location[] = new int[2];
            mDialogView.getLocationOnScreen(location);

            if (eventX < location[0] || eventX > (location[0] + mDialogView.getWidth()) || eventY < location[1]
                    || eventY > location[1] + mDialogView.getHeight()) {
                dismiss();
                return true;
            }
            return false;
        }
    });
}

В моем случае я получал getDialog() как ненулевое значение, но getDialog().dismiss() не работал. Кроме того, решение, отмеченное справа выше, также не сработало в моем случае. Итак, я выбрал этот подход.

person Swaroop    schedule 20.01.2015

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

  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    FragmentActivity act = getActivity();
    AlertDialog.Builder builder = new AlertDialog.Builder(act);

    // ... do your stuff here

    Dialog dialog = builder.create();

    dialog.setCanceledOnTouchOutside(false);

    Window window = dialog.getWindow();

    if( window!=null )
      {
      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
      }

    return dialog;
    }

то есть переместите настройку флагов в конец onCreateDialog() и добавьте setCancelOnTouchOutside(false).

Принятый ответ не работает на телефонах Samsung под управлением Android 9 и 10 (и, скорее всего, 11). Похоже, что в этих системах Samsung что-то изменил, поэтому установка этих флагов работает только в том случае, если они установлены очень рано.

Вышеупомянутое работает везде, включая эмуляторы Google, LG Nexus 5X, HTC Desire 12, Google Pixel 6, несколько телефонов Huawei и все (около 20) телефоны Samsung, на которых я тестировал это.

person Leszek    schedule 14.04.2020

Я проверил принятый ответ (Янива) на

Galaxy Tab A 9.7 Android 7.1.1: ok
Galaxy Tab A 8.0 Android 7    : ok
Galaxy Tab S4 Android 9       : no touch outside DialogFragment
Galaxy S5 Android 6.0.1       : ok
Galaxy Tab S2 Android 7       : ok
Galaxy Tab S3 Android 9       : no touch outside DialogFragment
Galaxy Tab S4 Android 9       : no touch outside DialogFragment
Galaxy Note Edge Android 6.0.1: ok
Galaxy Note4 Android 6.0.1    : ok

и действительно похоже, что это не работает на Android 9. Когда я говорю «не работает», я имею в виду, что когда я пытаюсь нажать кнопку, которая находится за пределами DialogFragment, ее метод onClick() вообще не вызывается.

EDIT: в свете моих выводов (в комментариях) я думаю, что ответ ниже должен быть принятым. Приведенное ниже работает везде, где я тестировал, включая эмуляторы, Google Pixel 6, LG Nexus 5X, HTC Desire 12, около 5 телефонов Huawei и около 20 телефонов Samsung под управлением всех версий Android 5–10.

public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    FragmentActivity act = getActivity();
    AlertDialog.Builder builder = new AlertDialog.Builder(act);

    // ... do your stuff here....

    Dialog dialog = builder.create();

    dialog.setCanceledOnTouchOutside(false);

    Window window = dialog.getWindow();

    if( window!=null )
      {
      window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
      window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
      }

    return dialog;
    }
person Leszek    schedule 13.04.2020
comment
Он не работает на Android 10 и предстоящем Android 11. Итак, резюмируя: работает только на Android 6, 7, 7.1, 8.0, 8.1 и не работает на Android 9, 10 и 11. - person Leszek; 13.04.2020
comment
Вышеуказанные результаты наблюдались на реальных телефонах Samsung. Любопытно, что при работе на эмуляторе с Android 9, 10 или 11 принятый ответ работает. - person Leszek; 13.04.2020
comment
Еще лучше — сейчас я проверил на Huawei Mate Pro под управлением Android 9, и там все работает. Похоже, это не работает только на телефонах Samsung под управлением Android 9, 10 или 11. - person Leszek; 14.04.2020
comment
Хорошо, я наконец нашел способ заставить принятый ответ работать и на телефонах Samsung: переместите раздел, который устанавливает флаг «FLAG_NOT_TOUCH_MODAL», в конец onCreateDialog () и не публикуйте () его - просто вызовите его оттуда. Похоже, на телефонах Samsung этот флаг нужно ставить как можно раньше, чтобы он заработал. - person Leszek; 15.04.2020

Вам нужно установить правильный стиль для ваших нужд. Подробнее о стилях для FragmentDialogs можно прочитать в официальной документации.

Я считаю, что вы могли бы использовать следующий стиль:

  • STYLE_NO_FRAME
person Warpzit    schedule 13.03.2013
comment
Я уже работаю со STYLE_NO_FRAME, и это единственное, что мне подходит (начиная с пользовательского интерфейса). - person Yaniv; 13.03.2013

Ну давайте конкретно. Если вы хотите работать с внешними представлениями, вам не следует использовать фрагмент диалога, вместо этого используйте фрагмент, как здесь.

 FragmentTransaction transaction = fragmentManager.beginTransaction();
    // For a little polish, specify a transition animation
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    // To make it fullscreen, use the 'content' root view as the container
    // for the fragment, which is always the root view for the activity
    transaction.add(android.R.id.content, newFragment)
               .addToBackStack(null).commit();

И сделать макет wrap_content, который внешне будет похож на Dialog и по умолчанию будет располагаться вверху экрана.

Подробнее см. здесь

person Akber    schedule 04.03.2016

@yaniv предоставил ответ. Но если хотите, вот фрагмент, где вы получите тот же результат, но без необходимости отправлять Runnable в корень View:

@NonNull
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
    final Dialog dialog = super.onCreateDialog(savedInstanceState);
    dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
    return dialog;
}
person Alexandre Bodi    schedule 18.07.2019