Как отключить все виды внутри макета?

84

Например у меня есть:

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

Scit
источник

Ответы:

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);
}
4e6
источник
20
Однако это работает только для детей первого уровня, если у вас есть вложенные представления, это не сработает.
htafoya
Одним из решений является создание рекурсивной функции с передачей ViewGroup в качестве параметра. Проверьте класс для всех его дочерних элементов, отключите его, если это EditText.
StoneLam
126

это рекурсивно для 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ü
источник
Хороший фрагмент, но прокрутки по-прежнему можно будет прокручивать, требуется еще код для дополнительной функциональности
htafoya
Спиннеры не выходят из строя, не знаю почему :(
Вивек Сингх
ViewPager также не отключается, включая представления внутри него.
Raphael C
1
Это не работает, потому что реализация setEnabled () лежит в подклассе представления, другими словами, она может быть реализована по-разному для разных представлений. Таким образом, такой подход не может работать для всех существующих представлений
RexSplode
83

Туту ответ на верный путь, но его рекурсия немного неудобна. Думаю, это чище:

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
Это решение отключит все представления в действии. На самом деле не отвечает на вопрос. Что, если цель состоит в том, чтобы отключить только определенные ViewGroups в действии?
AlanKley
2
именно это я и искал, спасибо !! Но тогда я могу определить, хочет ли пользователь куда-нибудь щелкнуть?
Skizo-ozᴉʞS
1
Отличное простое решение
WHOATEMYNOODLES
1
лучшее решение, если вы хотите отключить / включить только одно или набор представлений, используйте setEnabled (bool) или если представления являются дочерними элементами одного цикла просмотра через всех дочерних элементов этого представления и отключите их все, но это решение, которое я считаю лучшим (Я имею в виду этот ответ) спасибо Идан
Мохаммад Эльсайед
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);
   }
 }
}

Думаю, нет смысла просто отключать viewgroup. Если вы хотите сделать это, я использовал другой способ для той же цели. Создайте представление как одно из двух представлений группы:

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

и во время выполнения сделать его видимым. Примечание: родительский макет вашего группового просмотра должен быть относительным или фреймовым. Надеюсь, это поможет.

boburShox
источник
1
Ваш код не отключит, например, Spinner, потому что Spinner расширяет ViewGroup
qbait
12

Если сюда прокрутится какой-нибудь отчаянный разработчик, у меня есть другой вариант. Что также отключает прокрутку, насколько я экспериментировал с этим. Идея состоит в том, чтобы использовать элемент View, подобный этому, в RelativeLayout под всеми вашими элементами пользовательского интерфейса.

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

Таким образом, он должен «исчезнуть» перед определенным условием. И затем вы устанавливаете его видимость на ВИДИМОСТЬ, когда хотите отключить свой пользовательский интерфейс. Также вы должны реализовать OnClickListenerэто представление. Это onClickListenerперехватит событие щелчка и не передаст его базовым элементам.

RexSplode
источник
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
элегантное решение.
Raphael C
forEachМне кажется, не хватает метода
Skizo-ozᴉʞS
@ Skizo-ozᴉʞS это androidx.core.viewметод: developer.android.com/reference/kotlin/androidx/core/view/ ...
Ахраф Амил,
5

Детали

  • Студия Android 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  }
Василий Боднарчук
источник
Работает
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 = trueдо Viewдобавления вViewGroup .

Как указано в setDuplicateParentStateEnabled методе, если дочернее представление имеет дополнительные состояния (например, отмеченное состояние для флажка), они не будут затронуты родителем.

Аналог xml есть android:duplicateParentState="true".

Луи CAD
источник
1

Используйте рекурсивную функцию ниже, чтобы сделать ваши дочерние представления видимыми или исчезнувшими . Первый аргумент - это ваше родительское представление, а второй аргумент решает, хотите ли вы, чтобы дочерние элементы родительского представления были видны или исчезли. истина = видимая ложь = ушла

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
источник
1

Это довольно запоздалый ответ, но он может кому-то помочь. Многие ответы, упомянутые выше, кажутся хорошими. Но если ваш layout.xml имеет вложенные группы просмотра. Тогда приведенные выше ответы могут не дать полного результата. Поэтому я опубликовал свое мнение в виде фрагмента. С помощью приведенного ниже кода можно отключить все представления (включая вложенные группы просмотра).

ПРИМЕЧАНИЕ. Старайтесь избегать вложенных групп просмотра, поскольку они не рекомендуются.

 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. Всех потомков в фокус не возьмут.

Марату13
источник
Он не спрашивает о фокусировке.
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
            }
        }
    }
}
Midoriiro
источник
0

Самый простой способ - создать <View в вашем xml, с 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)
Хесус Альмарал - Hackaprende
источник
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) }
        }
    }
}
t3chb0t
источник
0
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.  

Это позволит вам динамически управлять всеми состояниями сразу из родительского представления.

<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
источник
-1

чтобы отключить представление, вы должны вызвать метод setEnabled с false в качестве аргумента. пример:

Button btn = ...
btn.setEnabled(false);
Эрик Нингабие
источник
-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>
Н. Hsn
источник