Фрагмент поверх другого фрагмента

271

Когда я показываю один фрагмент (который является полноэкранным с #77000000фоном) поверх другого фрагмента (давайте назовем его основным), мой основной фрагмент по-прежнему реагирует на щелчки (мы можем нажать кнопку, даже если мы ее не видим).

Вопрос : как предотвратить нажатия на первый (основной) фрагмент?

РЕДАКТИРОВАТЬ

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

Дмитрий Зайцев
источник
Исходя из того, что вы дали нам работать, вы должны пробуйте установить Visibilityиз ваших , main Fragmentчтобы , GONEкогда вы его не используете.
adneal
1
Не видя, как вы реализуете свой метод onClicked, я предполагаю, что вы возвращаете «false» при нажатии.
DeeV
@DeeV, onClickметод ничего не возвращает. Но вы даете идею, спасибо (я скоро отправлю ответ).
Дмитрий Зайцев
1
D'о. Ты прав. OnTouch возвращает его. Хотелось бы мне понять, почему событие касания провалилось во фрагмент. Этого не должно быть, если вы не публикуете сенсорные события.
DeeV
@DeeV, похоже, что если ваше представление (например, поверх других) не перехватывает событие onTouch, то система продолжает поиск других представлений с такими же координатами.
Дмитрий Зайцев

Ответы:

578

Установите для clickableсвойства второго фрагмента значение true. Представление будет перехватывать событие, чтобы оно не передавалось основному фрагменту. Так что, если представление второго фрагмента является макетом, это будет код:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true" />
Юре Визяк
источник
4
Это сработало для меня. Кажется, проще, чем решение @Дмитрий Зайцев дал выше. Есть ли причина, почему это было бы плохой идеей? Я не могу думать ни о чем, но я просто хочу быть уверен.
13
1
Это не сработало для меня. У меня есть RelativeLayoutфрагмент внутри, и я установил весь вид с помощью clickeableсвойства. Решение @Dmitry решит мою проблему.
4gus71n
3
Это сработало для меня. «clickable» в Android, по-видимому, чем-то похож на iOS «userInteractionEnabled»
mvds
17
Почему Android подвергает нас жестким условиям кодирования ?!
Ло-Тан
1
У меня такая же проблема с ViewPager. Когда я прокручиваю первую страницу, она также переходит на вторую страницу, и это решение не сработало для меня. Любые идеи?
Гохан Арик
72

Решение довольно простое. Во втором фрагменте (который перекрывает наш основной фрагмент) нам просто нужно перехватить onTouchсобытие:

@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstance){
    View root = somehowCreateView();

    /*here is an implementation*/

    root.setOnTouchListener(new View.OnTouchListener() {
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
    return root;
}
Дмитрий Зайцев
источник
ваше решение сработало +1 для этого, но вы можете сказать мне, почему мы должны сделать это явно?
Охота
@ Охота, ты не обязан. Это просто еще один способ сделать это (см. Принятый ответ)
Дмитрий Зайцев
android: clickable = "true" в основном макете вашего второго фрагмента xml.
Ришабх Шривастава
Есть проблема с этим решением, когда вы включаете режим обратной связи доступности, он не будет читать отдельные элементы, вместо этого фокусируется на корневом представлении.
Амит Гарг
Версия Kotlin: root.setOnTouchListener {_, _ -> true}
Гал Ром
21

Просто добавьте clickable="true"и focusable="true"родительский макет

 <android.support.constraint.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:focusable="true">

      <!--Your views-->

 </android.support.constraint.ConstraintLayout>

Если вы используете AndroidX, попробуйте это

 <androidx.constraintlayout.widget.ConstraintLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:clickable="true"
      android:focusable="true">

          <!--Your views-->

 </androidx.constraintlayout.widget.ConstraintLayout>
Miravzal
источник
Это чем-то отличается от принятого ответа? focuseableне действительно необходимо.
Дмитрий Зайцев
2
Я думаю, что focusable="true"здесь только для того, чтобы избежать предупреждения в Android Studio.
Артем М
9

Вы должны скрывать первый фрагмент, когда вы показываете второй фрагмент, если два фрагмента размещены в одном и том же виде контейнера.

Если вы хотите узнать больше вопросов о том, как решить проблемы с фрагментом, вы можете посмотреть мою библиотеку: https://github.com/JustKiddingBaby/FragmentRigger

FirstFragment firstfragment;
SecondFragment secondFragment;
FragmentManager fm;
FragmentTransaction ft=fm.beginTransaction();
ft.hide(firstfragment);
ft.show(secondFragment);
ft.commit();
JingYeoh
источник
1
Это должен быть правильный ответ! Спасибо, мужик. Я решил этот вопрос на другом проекте. Но я забыл, как я решил это, и, наконец, я получил ваш ответ. Спасибо.
Ликат Юлий
1
Я не думаю, что это правильное решение. Фрагменты / Действия работают в стеке представления. Вам нужно будет снова вызвать .show после того, как верхний фрагмент будет удален из стека, что означает, что нижний фрагмент должен быть проинформирован о том, что верхний фрагмент пропал. Просто добавлена ​​дополнительная логика для поддержки.
XY
4

Вы должны добавить android:focusable="true"сandroid:clickable="true"

Clickable означает, что на него можно нажать указательным устройством или коснуться сенсорного устройства.

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

Хоссам Хасан
источник
2
and focusable = "true" требуется, чтобы избежать предупреждения в новой Android Studio
Хоссам Хасан
2

Существует несколько решений, которые некоторые из нас внесли в этот поток, но я также хотел бы упомянуть еще одно решение. Если вам не нравится помещать кликабельный и фокусируемый, равный true для каждой корневой макета макета в XML, как я. Вы также можете положить его на свою базу, если у вас есть так же, как показано ниже;

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ) : View? {
        super.onCreateView(inflater, container, savedInstanceState)

        val rootView = inflater.inflate(layout, container, false).apply {
            isClickable = true
            isFocusable = true
        }

        return rootView
    }

Вы также можете использовать встроенную переменную, но я не предпочел ее по моим причинам.

Я надеюсь, что это поможет тем, кто ненавидит макет XML.

Йекта Сарыоглу
источник
1

Приемлемый ответ будет «работать», но также приведет к снижению производительности (перерисовка, повторное измерение при изменении ориентации), так как фрагмент внизу все еще рисуется. Может быть, вам нужно просто найти фрагмент по тегу или идентификатору и установить видимость GONE или VISIBLE, когда вам нужно снова показать.

В Котлине:

fragmentManager.findFragmentByTag(BottomFragment.TAG).view.visibility = GONE

Такое решение предпочтительнее альтернативы hide()и show()методов FragmentTransactionиспользования анимации. Вы просто вызовите его из onTransitionStart()и onTransitionEnd()в Transition.TransitionListener.

Аллан Велозо
источник
1

Метод 1:

Вы можете добавить ко всем фрагментам макет

android:clickable="true"
android:focusable="true"
android:background="@color/windowBackground"

Метод 2: (программно)

Расширить весь фрагмент FragmentBaseи т. Д. Затем добавьте этот код вFragmentBase

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    getView().setBackgroundColor(getResources().getColor(R.color.windowBackground));
    getView().setClickable(true);
    getView().setFocusable(true);
}
MarsPeople
источник
0

Что вы можете сделать, так это то, что вы можете сделать пустой щелчок для макета предыдущего фрагмента, используя свойство onClick для родительского макета этого основного фрагмента, и в действии вы можете создать функцию doNothing(View view)и ничего не писать в ней. Это сделает это за вас.

Сатиш Сильвери
источник
0

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

Хуан Мендес
источник
0

Добавление android:clickable="true"не работает для меня. Это решение не работает на CoordinatorLayout, когда это родительский макет. Вот почему я сделал RelativeLayout в качестве родительского макета, добавил android:clickable="true"к нему и поместил CoordinatorLayout в этот RelativeLayout.

Жебжик Бабич
источник
0

У меня было несколько фрагментов с тем же XML.
Потратив часы я снял setPageTransformerи все заработало

   //  viewpager.setPageTransformer(false, new BackgPageTransformer())

У меня была отвратительная логика.

public class BackgPageTransformer extends BaseTransformer {

    private static final float MIN_SCALE = 0.75f;

    @Override
    protected void onTransform(View view, float position) {
        //view.setScaleX Y
    }

    @Override
    protected boolean isPagingEnabled() {
        return true;
    }
}
AskQ
источник