user3192881
user3192881

Reputation:

Android Layout make all children's not clickable

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

Upvotes: 30

Views: 48992

Answers (13)

Highbrow Director
Highbrow Director

Reputation: 87

I would recommend creating an extension function something like extensions.kt

import android.view.View

fun View.disable() {
    isEnabled = false
    isClickable = false
    alpha = 0.5F
}

fun View.enable() {
    isEnabled = true
    isClickable = true
    alpha = 1F
}

obviously, you can customize the value as you need and now you can use it on any View like

somethingView.enable()
imageView.disable()
myButton.enable()
textView.diable()

Upvotes: 0

Oz Shabat
Oz Shabat

Reputation: 1622

An easy Kotlin extension solution to disable/enable a view and all of it's children:

fun View.isUserInteractionEnabled(enabled: Boolean) {
    isEnabled = enabled
    if (this is ViewGroup && this.childCount > 0) {
        this.children.forEach {
            it.isUserInteractionEnabled(enabled)
        }
    }
}

and call it with:

view.isUserInteractionEnabled(false)

Upvotes: 8

Abhishek Garg
Abhishek Garg

Reputation: 3242

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

Upvotes: 2

Fragment
Fragment

Reputation: 1585

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>

Upvotes: 1

Mark McClelland
Mark McClelland

Reputation: 5288

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

Upvotes: 2

Vedant Agarwala
Vedant Agarwala

Reputation: 18819

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

Upvotes: 7

anoo_radha
anoo_radha

Reputation: 822

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

Upvotes: 1

chubao
chubao

Reputation: 6011

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

Upvotes: 31

Menna-Allah Sami
Menna-Allah Sami

Reputation: 580

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

Upvotes: 0

CodeWarrior
CodeWarrior

Reputation: 5176

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.

Upvotes: 1

Tamilselvan Kalimuthu
Tamilselvan Kalimuthu

Reputation: 1532

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.

Upvotes: 1

vipul mittal
vipul mittal

Reputation: 17401

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

Upvotes: 16

Kasra Rahjerdi
Kasra Rahjerdi

Reputation: 2503

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

Upvotes: 28

Related Questions