如何禁用布局内的所有视图?


83

例如,我有:

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

有没有办法禁用(setEnable(false))LinearLayout内部的所有元素my_layout

Answers:


98

另一种方法是在每个子对象上调用setEnabled()(例如,如果要在禁用子对象之前进行一些额外的检查)

LinearLayout layout = (LinearLayout) findViewById(R.id.my_layout);
for (int i = 0; i < layout.getChildCount(); i++) {
    View child = layout.getChildAt(i);
    child.setEnabled(false);
}

20
但是,这仅适用于第一级子级,如果您具有嵌套视图,则它将不起作用。
htafoya 2012年

一种解决方案是制作一个递归函数,将ViewGroup作为参数传递。检查班级的每个子级,如果它是EditText,则将其禁用。
StoneLam

125

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

好的代码段,但是scrollviews仍然可以滚动,需要更多代码才能实现其他功能
htafoya 2012年

纺纱厂没有变得残障人士不知道为什么:(
Vivek Singh

ViewPagers也不会被禁用,包括其中的视图。
拉斐尔C

1
它不起作用,因为setEnabled()的实现位于视图的子类上,换句话说,可以针对不同的视图以不同的方式实现。因此,这种方法不可能适用于所有存在的视图
RexSplode

81

tutu的答案是正确的,但他的递归有点尴尬。我认为这更干净:

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

优秀的解决方案。谢谢
Blodhgard

这是正确的答案。它以递归方式进行。当前接受的答案只有一个层次。
Stealth Rabbi

24

实际上,对我有用的是:

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

并撤消它:

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

3
不错的简单解决方案!
Schm1

2
此解决方案将禁用活动中的所有视图。并没有真正解决这个问题。如果目标是仅禁用活动中的某些ViewGroup,该怎么办?
阿兰克利

2
那正是我想要的,谢谢!但是,然后我可以检测用户是否要单击某处?
Skizo-ozᴉʞS

1
伟大的简单解决方案
WHOATEMYNOODLES

1
最好的解决方案,如果您只想使用setEnabled(bool)禁用/启用一个视图或一组视图,或者如果视图是一个视图的所有子级,则循环遍历此视图的所有子级并将其全部禁用,但是我认为这种解决方案是最好的(我的意思是这个答案)非常感谢Idan
Mohammad Elsayed

21

如果您有兴趣禁用特定ViewGroup中的视图,则可以使用有趣的,也许有些晦涩的duplicateParentState。视图状态是一组布尔属性,例如按下,启用,激活等。只需在要同步到父级ViewGroup的每个孩子上使用它:

android:duplicateParentState="true"

请注意,它复制了整个状态,而不仅是启用状态。这可能就是您想要的!当然,如果要加载布局XML,则这种方法是最好的。


13

让我们更改tütü的代码

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

我认为,仅禁用视图组是没有意义的。如果您想这样做,我还有另一种完全相同的用途。创建视图作为您的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" />

并在运行时使其可见。注意:您的groupview的父布局应该是相对布局或框架布局。希望这会有所帮助。


1
您的代码不会禁用,例如Spinner,因为Spinner扩展了ViewGroup
qbait 2014年

12

如果一些绝望的开发人员在此处向下滚动,则我还有另一种选择。根据我的尝试,这也将禁用滚动。想法是在所有UI元素下的RelativeLayout中使用像这样的View元素。

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

因此,它被设置为在某些情况之前“消失”。然后,当您要禁用UI时,将其可见性设置为VISIBLE。另外,您必须OnClickListener为此视图实施。这onClickListener将捕获click事件,并且不会将其传递给基础元素。


1
这就是我一直在寻找的,是的,我很绝望,发现您的解决方案滚动像疯了似的:d
Skizo-ozᴉʞS

1
很好的解决方案,实际上我已经想到了这一点,并且我正在寻找其他具有相同想法的人,所以我不是一个疯子; D
Ricard,

12

我个人使用这样的东西(使用递归进行垂直树遍历)

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

用法:

   viewGroup.deepForEach { isEnabled = false }

1
优雅的解决方案。
拉斐尔C

缺少forEach方法我猜
Skizo-ozᴉʞS

@ Skizo-ozᴉʞS这是一个androidx.core.view方法:developer.android.com/reference/kotlin/androidx/core/view/...
Achraf阿米尔

4

细节

  • Android Studio 3.1.4
  • 科特林1.2.70
  • 在minSdkVersion 19中签入

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

用法

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  }

像魅力一样工作;)
Skizo-ozᴉʞS

3

尽管与禁用布局中的视图并不完全相同,但值得一提的是,您可以通过重写ViewGroup#onInterceptTouchEvent(MotionEvent)方法来防止所有子项都收到触摸(而不必递归布局层次结构):

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

}

然后,您可以防止孩子接收触摸事件:

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

如果您将点击监听器设置为layout,则仍会触发它。


喜欢这个答案。与不间断遍历ViewGroup来禁用所有子级相比,它的重量轻且效率高!要解决布局仍然响应点击的问题,您可以在setInterceptTouchEvents()
AlanKley

2
  private void disableLL(ViewGroup layout){
    for (int i = 0; i < layout.getChildCount(); i++) {
        View child = layout.getChildAt(i);
        child.setClickable(false);
        if (child instanceof ViewGroup)
            disableLL((ViewGroup) child);
    }
}

并像这样调用方法:

RelativeLayout rl_root = (RelativeLayout) findViewById(R.id.rl_root);
disableLL(rl_root);

2

在Kotlin中,您可以先使用isDuplicateParentStateEnabled = trueView添加到ViewGroup

如文件中所述 setDuplicateParentStateEnabled方法,如果子视图具有其他状态(例如复选框的选中状态),则这些状态将不受父视图的影响。

xml类似物是android:duplicateParentState="true"


1

使用下面的递归函数可以使您的子视图可见消失。第一个参数是您的父视图,第二个参数决定您是希望父视图的子视图可见还是消失。true =可见false =消失

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

1

这是一个相当延迟的答案,但可能会对某人有所帮助。上面提到的许多答案似乎都不错。但是,如果您的layout.xml具有嵌套的视图组。那么以上答案可能无法提供完整的结果。因此,我发表了我的观点作为摘要。使用下面的代码,可以禁用所有视图(包括嵌套视图组)。

注意:尽量避免使用嵌套的ViewGroup,因为不建议使用它们。

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

0

android:descendantFocusability="blocksDescendants"

用于您的ViewGroup视图。所有后代将不会集中注意力。


他不是在问重点。
Stealth Rabbi

0

如果您要禁用一组视图或说一种特定的视图,也就是说您要禁用固定数量的按钮,其中包含一些特定的文本或不包含文本,则可以使用该类型的数组并遍历数组元素使用setEnabled(false)属性禁用按钮时,可以在函数调用中执行以下操作:

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

0

我改进了tütü响应,以正确禁用EditText和RadioButton组件。此外,我正在分享一种发现改变视图可见性并在禁用的视图中增加透明度的方法。

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

0

我的两分钱功能没有递归

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

0

最简单的方法是在xml中创建一个<view,并用match_parent作为高度和宽度,确保该视图位于其他所有视图之上,然后在要防止单击时使其可见,将一个onClickListener添加到该视图中,并将null作为参数。

例:

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

然后在您的代码中:

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

0

我喜欢对根视图进行控制,因此将includeSelf标志添加到deepForEach

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

-1

对我来说,RelativeLayout或xml文件末尾的任何其他布局,其宽度和高度设置为match_parent,而属性focusable和clickable设置为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>

-2

要禁用视图,必须为参数调用false的setEnabled方法。例如:

Button btn = ...
btn.setEnabled(false);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.