Android Layout make all children's not clickable-ThrowExceptions

Exception or error:

I am using Relative Layout and many buttons in it with TextViews etc.I want to make all of them not clickable unless some event happens.

I tried setting RelativeLayout.setClickable(false); but still all the elements inside the layout are clickable.

I know one way of doing it that setting each child element not clickable but it is not an appropriate way because i have lot of child elements like buttons text views etc inside a layout i cannot make each child not clickable.

Here my question is How to set all to setClickable(false); in layout ??

How to solve:

When you say click do you actually mean touch? Touch events are done element by element. They’re first sent to the top level, which says if it handled it and if it didn’t it goes onto each children of the view.

When a touch event is created, the onTouch method of view in the chain is called, if any of these return true (true meaning “I handled this!”) it stops going down to the children.

To make your relativelayout block touches and clicks for all of its children you simply need to set the onTouchListener, like this:

YOUR_RELATIVE_LAYOUT.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // ignore all touch events
        return true;
    }
});

This will ignore all touch events happening on the relative layout (and all of its children) which includes simple touch down then release events (called clicks).

###

I found an alternative way to achieve this. You may create a blocking LinearLayout on top all its sibling views in the RelativeLayout like below:

<RelativeLayout
    android:id="@+id/rl_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- the children views begins -->
    ...
    <!-- the children views ends -->

    <LinearLayout
        android:id="@+id/ll_mask"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:clickable="true"
        android:orientation="horizontal"
        android:visibility="visible"/>

</RelativeLayout>

Later you may toggle the LinearLayout’s visibility to GONE to allow the children views to be clickable again:

mMask.setVisibility(View.VISIBLE); // blocks children
mMask.setVisibility(View.GONE); // children clickable

###

You can use following function to find all the child view and cancel click.

  public void setClickable(View view) {
    if (view != null) {
        view.setClickable(false);
        if (view instanceof ViewGroup) {
            ViewGroup vg = ((ViewGroup) view);
            for (int i = 0; i < vg.getChildCount(); i++) {
                setClickable(vg.getChildAt(i));
            }
        }
    }
}

###

A very simple and full-proof way to do it is to create a sub class and override onInterceptTouchEvent:

public class MyRelativeLayout extends RelativeLayout {
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // true if you do not want the children to be clickable.
        return mShouldInterceptAllTouch;
    }
}

No need to call any of the children’s methods.

You can still call setOnClickListener on your myRelativeLayout object. Also, you can use the class in XMLs as if you were using a RelativeLayout

###

Since you are going to perform the click for some items in the layout when that particular function is executed,

  • You can keep a static flag like a boolean
  • Create a static boolean globally and declare it as false, once the particular function is performed change it to true.
  • so in all the onclick functions that u r performing check this flag if it is true perform the necessory function.

###

If Butterknife library is used, the views can be grouped and the functionality can be done on the group.
Refer http://jakewharton.github.io/butterknife/,

@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;

The apply method allows you to act on all the views in a list at once.

ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

Action and Setter interfaces allow specifying simple behavior.

static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
  @Override public void apply(View view, int index) {
    view.setEnabled(false);
  }
};

static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() {
  @Override public void set(View view, Boolean value, int index) {
    view.setEnabled(value);
  }
};

For eg., if the textviews are grouped, you could do

static final ButterKnife.Setter<TextView, Boolean> ENABLED = new ButterKnife.Setter<TextView, Boolean>() {
        @Override public void set(TextView view, Boolean value, int index) {
            view.setClickable(value);
            view.setLongClickable(value);
            if(value){
                view.setTextColor(color);
            } else {
                view.setTextColor(color);
            }
        }
    };

###

If you want the children of a ViewGroup to be unresponsive to touch events, but you want the ViewGroup itself to respond to clicks, for example, you can create your own ViewGroup subclass, override onInterceptTouchEvent(), and always return true. This will intercept all touch events before children see them, while allowing your custom ViewGroup to remain responsive to touch events.

So, instead of RelativeLayout, you could use your own subclass:

public class ControlFreakRelativeLayout extends RelativeLayout {
    private boolean mWithholdTouchEventsFromChildren;

    // Constructors omitted for sake of brevity

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mWithholdTouchEventsFromChildren || super.onInterceptTouchEvent(ev);
    }

    public void setWithholdTouchEventsFromChildren(boolean withholdTouchEventsFromChildren) {
        mWithholdTouchEventsFromChildren = withholdTouchEventsFromChildren;
    }
}

###

As an alternative way, you can make clickable ViewGroup with children views via FrameLayout instead of RelativeLayout. Just position your child views in FrameLayout using paddings and gravity, make FrameLayout clickable, and all children views non-clickable:

<FrameLayout
    android:layout_width="51dp"
    android:layout_height="59dp"
    android:layout_gravity="center"
    android:clickable="true"
    android:focusable="true"
    android:onClick="@{() -> activity.onClick()}"
    android:padding="5dp">

    <android.support.v7.widget.AppCompatImageButton
        android:layout_width="wrap_content"
        android:layout_height="29dp"
        android:layout_gravity="center"
        android:clickable="false"
        app:srcCompat="@drawable/ic_back" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|left"
        android:layout_marginBottom="5dp"
        android:layout_marginLeft="5dp"
        android:clickable="false"
        android:text="@string/back" />
</FrameLayout>

###

You can make a common method in which you can write the code for disabling all your views inside your relative layout and call this method in onCreate() and then you can enable your particular view inside the the layout on your event.

###

loop on your buttons and use setClickable function and pass false value for it.
then when your even happen loop again on ot and setClickable to true

###

Problem : same as original question, but different use case. i want to progressBar widget which in container with transparent background colors. i want to disable all clicks on other items which comes under my transparent background color .

solution : just set your container clickable in my case Relative layout, it can any other layout too, keep your layout clickable true, until you want according to condition in my case till api call completed. after it simply set your parent clickable false.

android xml

<RelativeLayout
android:id="@+id/rl_parent"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- the children views begins -->
...
<!-- the children views ends -->


</RelativeLayout>

Java:

rl_parent.setClickable(true); // when to disable all clicks of children view

rl_parent.setClickable(false); // when to enable all clicks of children view

Leave a Reply

Your email address will not be published. Required fields are marked *