104

For example I have:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
        android:layout_height="fill_parent">
     <Button 
        android:id="@+id/backbutton"
        android:text="Back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:id="@+id/my_layout"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/my_text_view"
            android:text="First Name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <EditText
            android:id="@+id/my_edit_view"
            android:width="100px"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" /> 
        <View .../>
        <View .../>
        ...
        <View .../>
    </LinearLayout>

</LinearLayout>

Is there a way to disable (setEnable(false)) all elements inside LinearLayout my_layout ?

Scit
  • 1,722
  • 4
  • 15
  • 18

24 Answers24

141

this one is recursive for ViewGroups

private void disableEnableControls(boolean enable, ViewGroup vg){
    for (int i = 0; i < vg.getChildCount(); i++){
       View child = vg.getChildAt(i);
       child.setEnabled(enable);
       if (child instanceof ViewGroup){ 
          disableEnableControls(enable, (ViewGroup)child);
       }
    }
}
tütü
  • 1,481
  • 1
  • 10
  • 6
  • Good snippet, but scrollviews will still be scrollable, some more code is needed for additional functionality – htafoya Jul 12 '12 at 02:25
  • Spinners not getting disabled not sure why :( – Vivek Singh Jun 16 '16 at 15:59
  • ViewPagers also don't get disabled, including views inside it. – Raphael C Jun 27 '16 at 12:24
  • 2
    It doesn't work because the implementation of setEnabled() lies on the subclass of the view, in other words it can be implemented differently for different views. So there is no way such approach will work for every view in existence – RexSplode Mar 29 '17 at 07:11
103

Another way is to call setEnabled() on each child (for example if you want to do some extra check on child before disabling)

LinearLayout layout = (LinearLayout) findViewById(R.id.my_layout);
for (int i = 0; i < layout.getChildCount(); i++) {
    View child = layout.getChildAt(i);
    child.setEnabled(false);
}
4e6
  • 10,696
  • 4
  • 52
  • 62
  • 22
    However this only works for the first level children, if you have nested views it won't work. – htafoya Jul 12 '12 at 02:22
  • One solution is making a recursive function, passing a ViewGroup as parameter. Check the class for its every children, disabled it if it's a EditText. – StoneLam Nov 21 '19 at 03:08
97

tutu's answer is on the right track, but his recursion is a little awkward. I think this is cleaner:

private static void setViewAndChildrenEnabled(View view, boolean enabled) {
    view.setEnabled(enabled);
    if (view instanceof ViewGroup) {
        ViewGroup viewGroup = (ViewGroup) view;
        for (int i = 0; i < viewGroup.getChildCount(); i++) {
            View child = viewGroup.getChildAt(i);
            setViewAndChildrenEnabled(child, enabled);
        }
    }
}
Eric Cochran
  • 8,414
  • 5
  • 50
  • 91
27

Actully what work for me is:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

and to undo it:

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
Idan Magled
  • 2,186
  • 1
  • 23
  • 33
  • 3
    This solution would disable all views in the activity. Doesn't really address the question. What if the goal is to disable only certain ViewGroups in the activity? – AlanKley Oct 04 '18 at 17:09
  • 2
    that's exactly I was looking for, thanks!! But then I can detect if user want to click somewhere? – Skizo-ozᴉʞS ツ Mar 19 '19 at 20:59
  • 1
    best solution if u want to disable/enable only one or set of views use the setEnabled(bool) or if the views are all children of one view loop through all children of this view and disable them all but this solution I think is the best(I mean this answer) thanks Idan – Mohammad Elsayed Feb 22 '20 at 18:48
  • Interesting but is not correct in this case. The question is about all views in some layout, NOT whole activity – Vitaly Jul 26 '23 at 05:51
27

If you're interested in disabling views in a specific ViewGroup then you can use the interesting, perhaps slightly obscure duplicateParentState. A view state is a set of boolean attributes such as pressed, enabled, activated, and others. Just use this on each child you want to sync to parent ViewGroup:

android:duplicateParentState="true"

Note that it duplicates the entire state and not just the enabled state. This may be what you want! Of course, this approach is best if you're loading layout XML.

androidguy
  • 3,005
  • 2
  • 27
  • 38
  • So much easier and more specific than a recursive function, thanks for sharing! I added it to a `@style` of a certain button type and magic happened . – TWiStErRob May 23 '23 at 09:55
19

I personally use something like this (vertical tree traversal using recursion)

fun ViewGroup.deepForEach(function: View.() -> Unit) {
    this.forEach { child ->
        child.function()
        if (child is ViewGroup) {
            child.deepForEach(function)
        }
    }
}

usage :

   viewGroup.deepForEach { isEnabled = false }
Achraf Amil
  • 1,275
  • 16
  • 20
13

Let's change tütü's code

private void disableEnableControls(boolean enable, ViewGroup vg){
for (int i = 0; i < vg.getChildCount(); i++){
   View child = vg.getChildAt(i);
   if (child instanceof ViewGroup){ 
      disableEnableControls(enable, (ViewGroup)child);
   } else {
     child.setEnabled(enable);
   }
 }
}

I think, there is no point in just making viewgroup disable. If you want to do it, there is another way I have used for exactly the same purpose. Create view as a sibling of your groupview :

<View
    android:visibility="gone"
    android:id="@+id/reservation_second_screen"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="bottom"
    android:background="#66ffffff"
    android:clickable="false" />

and at run-time, make it visible. Note: your groupview's parent layout should be either relative or frame layout. Hope this will help.

boburShox
  • 2,630
  • 5
  • 23
  • 35
13

If some desperate developer scrolls down here, I have another option to do it. Which also disables scrolling as far as I experimented with it. The idea is to use View element like this one in a RelativeLayout, under all your UI elements.

<View
                android:id="@+id/shade"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/primaryShadow"
                android:visibility="gone"/>

So it is set to be "gone" before some condition. And then you set it's visibility to VISIBLE when you want to disable your UI. Also you have to implement OnClickListener for this View. This onClickListener will catch click event and won't pass it to the underlying elements.

halfer
  • 19,824
  • 17
  • 99
  • 186
RexSplode
  • 1,475
  • 1
  • 16
  • 24
  • 1
    That's what I was looking for, yes I was desperate and found your solution scrolling like crazy :D – Skizo-ozᴉʞS ツ Mar 19 '19 at 21:04
  • 1
    Pretty nice solution, actually I had it in my mind and I was looking for someone else having the same idea so I'm not a crazy loner ;D – Ricard May 21 '19 at 10:50
9

Although not quite the same as disabling views within a layout, it is worth mentioning that you can prevent all children from receiving touches (without having to recurse the layout hierarchy) by overriding the ViewGroup#onInterceptTouchEvent(MotionEvent) method:

public class InterceptTouchEventFrameLayout extends FrameLayout {

    private boolean interceptTouchEvents;

    // ...

    public void setInterceptTouchEvents(boolean interceptTouchEvents) {
        this.interceptTouchEvents = interceptTouchEvents;
    }

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

}

Then you can prevent children from receiving touch events:

InterceptTouchEventFrameLayout layout = (InterceptTouchEventFrameLayout) findViewById(R.id.layout);
layout.setInterceptTouchEvents(true);

If you have a click listener set on layout, it will still be triggered.

Travis
  • 1,926
  • 1
  • 19
  • 26
  • Love this answer. Pretty light weight and efficient compared to constantly traversing ViewGroup to disable all children! To solve the issue with the layout still responding to clicks, you can just disable/enable the layout in setInterceptTouchEvents() – AlanKley Oct 04 '18 at 17:20
  • how about passing context to cinstructor? You cant create view without context – Waldmann Jan 11 '21 at 20:38
  • this is the most efficient workaround for a production app. – Ian Elvister Mar 07 '21 at 08:21
9

Details

  • Android studio 3.1.4
  • Kotlin 1.2.70
  • checked in minSdkVersion 19

Solution

fun View.forEachChildView(closure: (View) -> Unit) {
    closure(this)
    val groupView = this as? ViewGroup ?: return
    val size = groupView.childCount - 1
    for (i in 0..size) {
        groupView.getChildAt(i).forEachChildView(closure)
    }
}

Usage

val layout = LinearLayout(context!!)
layout.forEachChildView {  it.isEnabled = false  }

val view = View(context!!)
view.forEachChildView {  it.isEnabled = false  }

val fragment = Fragment.instantiate(context, "fragment_id")
fragment.view?.forEachChildView {  it.isEnabled = false  }
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
6
  private void disableLL(ViewGroup layout){
    for (int i = 0; i < layout.getChildCount(); i++) {
        View child = layout.getChildAt(i);
        child.setEnabled(false);
        child.setClickable(false);
        if (child instanceof ViewGroup)
            disableLL((ViewGroup) child);
    }
}

and call method like this :

RelativeLayout rl_root = (RelativeLayout) findViewById(R.id.rl_root);
disableLL(rl_root);
AKTanara
  • 230
  • 1
  • 11
abbasalim
  • 3,118
  • 2
  • 23
  • 45
2

In Kotlin, you can use isDuplicateParentStateEnabled = true before the View is added to the ViewGroup.

As documented in the setDuplicateParentStateEnabled method, if the child view has additional states (like checked state for a checkbox), these won't be affected by the parent.

The xml analogue is android:duplicateParentState="true".

Louis CAD
  • 10,965
  • 2
  • 39
  • 58
1

I improved the tütü response to properly disable EditText and RadioButton componentes. Besides, I'm sharing a way that I found to change the view visibility and add transparency in the disabled views.

private static void disableEnableControls(ViewGroup view, boolean enable){
    for (int i = 0; i < view.getChildCount(); i++) {
        View child = view.getChildAt(i);
        child.setEnabled(enable);
        if (child instanceof ViewGroup){
            disableEnableControls((ViewGroup)child, enable);
        }
        else if (child instanceof EditText) {
            EditText editText = (EditText) child;
            editText.setEnabled(enable);
            editText.setFocusable(enable);
            editText.setFocusableInTouchMode(enable);
        }
        else if (child instanceof RadioButton) {
            RadioButton radioButton = (RadioButton) child;
            radioButton.setEnabled(enable);
            radioButton.setFocusable(enable);
            radioButton.setFocusableInTouchMode(enable);
        }
    }
}

public static void setLayoutEnabled(ViewGroup view, boolean enable) {
    disableEnableControls(view, enable);
    view.setEnabled(enable);
    view.setAlpha(enable? 1f: 0.3f);
}

public static void setLayoutEnabled(ViewGroup view, boolean enable, boolean visibility) {
    disableEnableControls(view, enable);
    view.setEnabled(enable);
    view.setAlpha(enable? 1f: 0.3f);
    view.setVisibility(visibility? View.VISIBLE: View.GONE);
}
Ângelo Polotto
  • 8,463
  • 2
  • 36
  • 37
1

This is a pretty delayed answer.But it might help someone. Many answers mentioned above seem to be good. But if your layout.xml has nested viewgroups. Then above answers may not provide full result. Hence i have posted my opinion as a snippet. With the code below one can disable all views (Including Nested ViewGroups).

NOTE: Try avoiding nested ViewGroups as they are not recommended.

 private void setEnableView(boolean b) {
    LinearLayout layout = (LinearLayout)findViewById(R.id.parent_container);
    ArrayList<ViewGroup> arrVg = new ArrayList<>();
    for (int i = 0; i < layout.getChildCount(); i++) {
        View child = layout.getChildAt(i);
        if (child instanceof ViewGroup) {
            ViewGroup vg = (ViewGroup) child;
            arrVg.add(vg);
        }
        child.setEnabled(b);
    }

    for (int j=0;j< arrVg.size();j++){
        ViewGroup vg = arrVg.get(j);
        for (int k = 0; k < vg.getChildCount(); k++) {
         vg.getChildAt(k).setEnabled(b);
        }
    }
}
mohammed nathar
  • 190
  • 1
  • 12
1
You can have whichever children that you want to have the same state as the parents include android:duplicateParentState="true".  Then if you disable the parent, whatever children you have that set on will follow suit.  

This will allow you to dynamically control all states at once from the parent view.

<LinearLayout
    android:id="@+id/some_parent_something"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
     <Button 
        android:id="@+id/backbutton"
        android:text="Back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:duplicateParentState="true"/>
    <LinearLayout
        android:id="@+id/my_layout"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:duplicateParentState="true">
        <TextView
            android:id="@+id/my_text_view"
            android:text="First Name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:duplicateParentState="true" />
        <EditText
            android:id="@+id/my_edit_view"
            android:width="100px"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:duplicateParentState="true" /> 
        <View .../>
        <View .../>
        ...
           <View .../>
    </LinearLayout>

</LinearLayout>
a54studio
  • 965
  • 11
  • 11
0

Use below recursive function to make your child views visible or gone. First argument is your parent view and second argument decides if you want childs of parent view visible or gone. true = visible false = gone

private void layoutElemanlarininGorunumunuDegistir(View view, boolean gorunur_mu_olsun) {
    ViewGroup view_group;
    try {
        view_group = (ViewGroup) view;
        Sabitler.konsolaYazdir(TAG, "View ViewGroup imiş!" + view.getId());
    } catch (ClassCastException e) {
        Sabitler.konsolaYazdir(TAG, "View ViewGroup değilmiş!" + view.getId());
        return;
    }

    int view_eleman_sayisi = view_group.getChildCount();
    for (int i = 0; i < view_eleman_sayisi; i++) {
        View view_group_eleman = view_group.getChildAt(i);
        if (gorunur_mu_olsun) {
            view_group_eleman.setVisibility(View.VISIBLE);
        } else {
            view_group_eleman.setVisibility(View.GONE);
        }
        layoutElemanlarininGorunumunuDegistir(view_group_eleman, gorunur_mu_olsun);
    }
}
resw67
  • 139
  • 3
  • 6
0

If you want to disable a set of, or say a particular kind of view.Let's say you want to disable a fixed number of buttons with some particular text of or no text then you can use array of that type and loop through the array elements while disabling the buttons using setEnabled(false) property You can do it on a function call like this:

public void disable(){
        for(int i=0;i<9;i++){
                if(bt[i].getText().equals("")){//Button Text condition
                    bt[i].setEnabled(false);
            }
        }
}
Mohit Pardasani
  • 166
  • 1
  • 3
0

My two cents function without recursion

    package io.chord.ui.utils

import android.view.View
import android.view.ViewGroup
import androidx.core.view.forEach

class ViewUtils
{
    companion object
    {
        fun setViewState(view: View, state: Boolean)
        {
            var depth = 0
            val views: MutableMap<Int, MutableList<View>> = mutableMapOf()
            views[depth] = mutableListOf(view)

            while(true)
            {
                val currentViews = views[depth]
                val nextViews = mutableListOf<View>()

                currentViews!!.forEach { view ->
                    if(view is ViewGroup)
                    {
                        view.forEach { children ->
                            nextViews.add(children)
                        }
                    }
                }

                if(nextViews.size == 0)
                {
                    break
                }

                depth++

                views[depth] = nextViews
            }

            views.flatMap {
                it.value
            }.forEach {
                it.isEnabled = state
            }
        }
    }
}
midoriiro
  • 81
  • 1
  • 5
0

I like to have a control over the root view so I added the includeSelf flag to deepForEach

fun ViewGroup.deepForEach(includeSelf: Boolean = true, function: View.() -> Unit) {
    if (includeSelf) function()
    forEach {
        it.apply {
            function()
            (this as? ViewGroup)?.apply { deepForEach(includeSelf, function) }
        }
    }
}

usage :

   viewGroup.deepForEach (includeSelf: true) { isEnabled = false }
t3chb0t
  • 16,340
  • 13
  • 78
  • 118
-1

Set

android:descendantFocusability="blocksDescendants"

for yor ViewGroup view. All descendants will not take focus.

Maratu13
  • 25
  • 3
-1

For me RelativeLayout or any other layout at the end of the xml file with width and height set to match_parent with attribute focusable and clickable set to true.

 <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"
            android:focusable="true">

        <ProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true" />

    </RelativeLayout>
N. Hsn
  • 41
  • 1
  • 4
-1

Many answers are good here. But if child views may be change during initialization (e.g. in the case of RecyclerView) you can use the Handler:

fun ViewGroup.setEnabledDeep(enabled: Boolean) {
    Handler(Looper.getMainLooper()).post {
        isEnabled = enabled
        for (i: Int in 0..childCount) {
            val child: View = getChildAt(i) ?: continue
            child.isEnabled = enabled
            if (child is ViewGroup) {
                child.setEnabledDeep(enabled)
            }
        }
    }
}

Then:

recyclerView.setEnabledDeep(false)
antaki93
  • 704
  • 7
  • 10
-2

to disable a view, you must call the method setEnabled with false for argument. ex:

Button btn = ...
btn.setEnabled(false);
-2

The easiest way is creating a <View in your xml, with match_parent for height and width, make sure the view is above every other views, then when you want to prevent clicks, make it visible add an onClickListener to that view with null as parameter.

Example:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
        android:layout_height="fill_parent">
     <Button 
        android:id="@+id/backbutton"
        android:text="Back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <LinearLayout
        android:id="@+id/my_layout"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/my_text_view"
            android:text="First Name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <EditText
            android:id="@+id/my_edit_view"
            android:width="100px"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" /> 
        
        <View
            android:id="@+id/disable_layout_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"/>
    </LinearLayout>

</LinearLayout>

Then in your code:

val disableLayoutView = rootView.find<View>(R.id.disable_layout_view)
disableLayoutView.visibility = View.VISIBLE
disableLayoutView.setOnClickListener(null)