309

When I'm showing one fragment (which is full screen with #77000000 background) over another fragment (let's call it main), my main fragment still reacts to clicks (we can click a button even if we don't see it).

Question: how to prevent clicks on first (main) fragment?

EDIT

Unfortunately, I can't just hide main fragment, because I'm using transparent background on second fragment (so, user can see what located behind).

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
  • Based on what you gave us to work with, you should try setting the `Visibility` of your `main` `Fragment` to `GONE` when you're not using it. – adneal Apr 30 '12 at 19:58
  • 1
    Without seeing how you implement your onClicked method, I'm guessing you're returning "false" when clicked. – DeeV Apr 30 '12 at 19:59
  • @DeeV, `onClick` method doesn't returns anything. But you give an idea, thanks (I'll post answer soon). – Dmitry Zaytsev Apr 30 '12 at 20:04
  • 1
    D'oh. You're right. onTouch returns it. I just wish I understood why a touch event fell through a fragment. It shouldn't do that if you're not issuing touch events. – DeeV Apr 30 '12 at 20:30
  • @DeeV, looks like if your view (that, for example on top of other) doesn't catches onTouch event, then system continue searching for other views with same coordinates. – Dmitry Zaytsev Apr 30 '12 at 20:42
  • Why does this happen? Anytime you have multiple fragments stacked up this will occur? – portfoliobuilder Sep 30 '15 at 01:28

12 Answers12

647

Set clickable property on the second fragment's view to true. The view will catch the event so that it will not be passed to the main fragment. So if the second fragment's view is a layout, this would be the code:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true" />
Jure Vizjak
  • 6,748
  • 2
  • 16
  • 15
  • 4
    This worked for me. It seems easier than the solution @Dmitry Zaitsev gave above. Is there any reason why this would be a bad idea? I can't seem to think of any, but I just want to be sure. – ariets Apr 30 '13 at 15:02
  • 3
    This didn't work for me. I have a `RelativeLayout` inside the fragment, and I set the whole view with the `clickeable` property. The solution of @Dmitry solve my problem. – 4gus71n Nov 01 '13 at 13:51
  • My bad, I was inflating the fragment with `inflater.inflate(R.layout.fragment_lastsearch, null);` instead of `inflater.inflate(R.layout.fragment_lastsearch, container, false);`. Your solution is right. – 4gus71n Nov 01 '13 at 14:16
  • 3
    This worked for me. "clickable" in Android apparently is somewhat like iOS' "userInteractionEnabled" – mvds Jan 30 '14 at 18:05
  • 34
    Why does Android subject us to harsh coding conditions?! – Lo-Tan May 20 '15 at 06:06
  • 1
    I am having same issue with ViewPager. When I scroll on first page, it passes to second page also, and this solution didn't work for me. Any ideas? – Gokhan Arik Nov 10 '15 at 16:40
  • @GokhanArik : Did you get the solution of your problem ?? I m facing the same right now. – Jatin Jha Sep 05 '16 at 07:21
  • This didn't work for me. Use `Fragment.show()` and `Fragment.hide()` methods instead. – Hossein Nov 03 '16 at 06:51
  • Did not work for me. Minimising and maximising the app still gives the focus back to the edittext in the parent fragment. – alchemist Jul 20 '17 at 03:56
  • There is a problem with this solution, when you get in accessibility talkback mode ON, it won't read the individual elements instead gets the focus to root view. – Amit Garg Jun 28 '18 at 17:45
  • Do you want overdraw? Because that's how you get overdraw – Allan Veloso Aug 16 '18 at 21:14
  • 1
    `android:clickable="true"` `android:focusable="true"` – tim4dev Jul 23 '19 at 13:13
  • `'clickable' attribute found, please also add 'focusable'`. Android Studio recommends adding `focusable` too. – Roshana Pitigala Apr 24 '20 at 18:56
76

Solution is pretty simple. In our second fragment (that overlaps our main fragment) we just need to catch onTouch event:

@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;
}
Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
29

Just add clickable="true" and focusable="true" to parent layout

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

If you are using AndroidX, try this

 <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
  • 2,025
  • 1
  • 13
  • 11
13

You should hide the first fragment when you are showing the second Fragment if two fragments is placed in same container view.

If you want to know more questions about how to solve problems about Fragment, you can see my library: https://github.com/JustKiddingBaby/FragmentRigger

FirstFragment firstfragment;
SecondFragment secondFragment;
FragmentManager fm;
FragmentTransaction ft=fm.beginTransaction();
ft.hide(firstfragment);
ft.show(secondFragment);
ft.commit();
JingYeoh
  • 151
  • 1
  • 6
  • 1
    This should be the right answer! Thank you man. I have solved this question on another project. But I have forgotten How did I solve it and Finally, I got your answer. Thank you. – Chinese Cat Jun 11 '18 at 08:35
  • 4
    I don't think this is the right solution. Fragments/Activities work in a view stack. You'll have to call .show again after top fragment is popped off the stack, which means the bottom fragment has to be informed the top one is gone. It's just added extra logic to maintain. – X.Y. Nov 14 '19 at 03:20
7

You need to add android:focusable="true" with android:clickable="true"

Clickable means that it can be clicked by a pointer device or be tapped by a touch device.

Focusable means that it can gain the focus from an input device like a keyboard. Input devices like keyboards cannot decide which view to send its input events to based on the inputs itself, so they send them to the view that has focus.

Hossam Hassan
  • 795
  • 2
  • 13
  • 39
4

Metod 1:

You can add to all fragments layout

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

Metod 2: (Programmatically)

Extend all fragment from FragmentBase etc. Then add this code to 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
  • 1,772
  • 18
  • 30
3

There is more than one solution that some of us contributed to this thread but also I would like to mention one other solution. If you don't fancy putting clickable and focusable equals true to every layout's root ViewGroup in XML like me. You can also put it to your base if you have one just like below;

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
    }

You can also use inline variable but I did not prefer it for personal reasons.

I hope it helps for the ones who hate layout XMLs.

Yekta Sarıoğlu
  • 1,435
  • 2
  • 12
  • 20
1

The acceptable answer will "work", but will also cause performance cost (overdraw, re-measuring on orientation change) as the fragment on the bottom is still being drawn. Maybe you should simply find the fragment by tag or ID and set visibility to GONE or VISIBLE when you need to show again.

In Kotlin:

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

This solution is preferable to the alternative hide() and show() methods of FragmentTransaction when you use animations. You just call it from the onTransitionStart() and onTransitionEnd() of Transition.TransitionListener.

Allan Veloso
  • 5,823
  • 1
  • 38
  • 36
0

What u can do is u can give a Blank click to the previous fragment's layout by using onClick property to parent layout of that main fragment and in activity you can create a function doNothing(View view) and do not write anything in it. This will do it for you.

Maximilian Ast
  • 3,369
  • 12
  • 36
  • 47
Satish Silveri
  • 393
  • 4
  • 9
0

This sounds like a case for DialogFragment. Otherwise with Fragment Manager commit one to hide and the other one to show. That has worked for me.

Juan Mendez
  • 2,658
  • 1
  • 27
  • 23
0

The adding of android:clickable="true" didn't work for me. This solution not works on CoordinatorLayout when it's a parent layout. That's why I've made RelativeLayout as parent layout, added android:clickable="true" to it and placed CoordinatorLayout on this RelativeLayout.

Eugene Babich
  • 1,271
  • 15
  • 25
0

I had multiple fragments with same xml.
After spending hours, I removed setPageTransformer and it started working

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

I had scalling logic.

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
  • 4,215
  • 7
  • 34
  • 61