Как сгруппировать несколько просмотров и вместе изменить их видимость

У меня есть действие, которое содержит 2 группы представлений, которые НЕ МОЖЕТ находиться в той же группе LAYOUT, но принадлежать к той же группе LOGIC, что означает, что они должны быть показаны или скрыты и привязаны к событию щелчка одновременно. Дело в том, что мне ужасно неприятно писать что-то вроде этого:

fun hide() {
    view1.visibility = View.GONE
    view2.visibility = View.GONE
    view3.visibility = View.GONE
    // ...
    view9.visibility = View.GONE
}


fun show() {
    view1.visibility = View.VISIBLE
    view2.visibility = View.VISIBLE
    view3.visibility = View.VISIBLE
    // ...
    view9.visibility = View.VISIBLE

    view1.setOnClickListener{ run() }
    view2.setOnClickListener{ run() }
    view3.setOnClickListener{ run() }
    // ...
    view9.setOnClickListener{ run() }
}

Я прочитал сообщение, в котором описывается котлин умение, чтобы упростить этот беспорядок, каким-то образом группируя эти представления, а затем просто обрабатывая группы, но, к сожалению, я больше не могу найти этот пост ..

Помощь будет признательна!

========= Обновление 2019-07-31 =========

Я нашел решение, но забыл обновить этот вопрос, `` группировка '', которую я искал, на самом деле не является специфической функцией Kotlin, а просто использует варарг, и мы можем использовать расширение Kotlin (которое УДИВИТЕЛЬНО), чтобы немного упростить:

// assume we have a base activity or fragment, then put below functions in there
fun View.show() {
    visibility = View.VISIBLE
}

fun show(vararg views: View) {
    views.forEach { it.show() }
}

fun View.hide() {
    visibility = View.GONE
}

fun hide(vararg views: View) {
    views.forEach { it.hide() }
}

// then in any activity or fragment
show(v1, v2, v3, v4)
v9.hide()

============= обновлено 07.03.2020 ================

Именно для этого предназначен androidx.constraintlayout.widget.Group, который может логически группировать кучу представлений из любого места и управлять их видимостью, только изменяя видимость группы.

добавьте их в один макет, например линейный макет ...

Amit Jangid 16.10.2018 14:45

@AmitJangid Я бы хотел, чтобы это было так просто, но эти представления, принадлежащие одной логической группе, не могут быть просто добавлены в одну группу макетов .. они все смешаны вместе ..

ZhouX 16.10.2018 14:49
4
2
2 521
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

В зависимости от того, как структурирован ваш макет, вы можете сгруппировать эти представления в ViewGroup, например LinearLayout, RelativeLayout, FrameLayout или ConstraintLayout.

Затем вы можете изменить видимость только на ViewGroup, и все его дочерние элементы тоже это изменят.

Редактировать:

Без ViewGroup единственное решение для устранения этого шаблона - включить привязку данных в вашем проекте и установить ее следующим образом:

В вашей деятельности / фрагменте:

val groupVisible = ObservableBoolean()

fun changeVisibility(show: Boolean) {
    groupVisible.set(show)
}

В вашем xml:

<layout>
    <data>
        <variable name = "groupVisible" type = "Boolean"/>
    </data>
    <View
        android:visibility = "@{groupVisible ? View.VISIBLE : View.GONE}"/>
</layout>

извините, приятель, я не ясно выразился, теперь вопрос обновлен, эти представления принадлежат одной логической группе, не могут быть расположены в той же группе макета.

ZhouX 16.10.2018 14:59

Если ваши представления не входят в группу представлений вы можете использовать функция расширения. Вы можете создать один для переключения видимости представлений:

fun View.toggleVisibility() {
    if (visibility == View.VISIBLE) {
        visibility = View.GONE
    } else {
        visibility = View.VISIBLE
    }
}

И вы можете использовать это так:

view.toggleVisibility()

спасибо, что эти представления действительно находятся не в той же группе макетов, но с вашим предложением мне все равно нужно написать десяток строк xxxx.toggleVisibility (), чего я бы хотел избежать.

ZhouX 16.10.2018 14:58

Почему бы вам не создать массив:

val views = arrayOf(view1, view2, view3, view4, view5, view6, view7, view8, view9)

затем:

fun show() {
    views.forEach {
        it.visibility = View.VISIBLE
        it.setOnClickListener{  }
    }
}

fun hide() {
    views.forEach { it.visibility = View.INVISIBLE }
}

Или без массива, если имена представлений наверняка похожи на view1, view2, ...

for (i in 1..9) {
    val id = resources.getIdentifier("view$i", "id", context.getPackageName())
    val view = findViewById<View>(id)
    view.visibility = View.VISIBLE
    view.setOnClickListener{  }
}

Это точно может, спасибо! однако это не то, что я видел в том посте, а это что-то ... эээ. скажем, котлин-волшебство ..

ZhouX 16.10.2018 15:02

зачем вам создавать ненужные циклы, если это можно сделать через группы

Satyaraj Moily 24.05.2021 17:32

Вы можете определить функцию с тремя параметрами и использовать vararg, как в следующем коде:

fun changeVisiblityAndAddClickListeners(visibility: Int,
                                        listener: View.OnClickListener,
                                        vararg views: View) {
    for (view: View in views) {
        view.visibility = visibility
        if (visibility == View.VISIBLE) {
            view.setOnClickListener(listener)
        }
    }
}

Конечно, если у вас слишком много просмотров, это не эффективное решение. Я просто добавил этот фрагмент кода для альтернативного способа, особенно для проблем с набором динамических представлений.

Сначала в макете xml сгруппируйте представления по атрибуту android:tag = "group_1".

Затем внутри вашей деятельности используйте цикл for для реализации любой необходимой вам логики:

val root: ViewGroup = TODO("find your root layout")
for (i in 0 until root.childCount) {
    val v = root.getChildAt(i)
    when (v.tag) {
        "group_1" -> {
            TODO()
        }
        "group_2" -> {
            TODO()
        }
        else -> {
            TODO()
        }
    }
}

Создайте список представлений и зацикливайтесь на нем

val views = listOf<View>(view1, view2, ...)
views.forEach {
    it.visibility = View.GONE
}

Вы также можете создать функция расширенияIterable<View>, чтобы упростить любые действия в перечисленных представлениях.

fun Iterable<View>.visibility(visibility: Int) = this.forEach {
    it.visibility = visibility
}
//usage
views.visibility(View.GONE)

Возможно, вы хотите найти все представления из тегов в XML. Взгляните на этот ответ

Начиная с ConstraintLayout 1.1, вы можете использовать Group вместо LayoutGroup. Вы можете просто добавить этот код в свой XML-макет

<android.support.constraint.Group
    android:id = "@+id/profile"
    app:constraint_referenced_ids = "profile_name,profile_image" />

А затем вы можете вызвать его из кода, чтобы добиться нужного вам поведения.

profile.visibility = GONE
profile.visibility = VISIBLE

Подробнее читайте в статье https://medium.com/androiddevelopers/introduction-constraint-layout-1-1-d07fc02406bc

Если вы используете androidx, используйте androidx.constraintlayout.widget.Group вместо

Bojan Radivojevic Bomber 13.02.2020 19:17
Ответ принят как подходящий

Вам необходимо создать функции расширения.

Например:

fun View.showGroupViews(vararg view: View) {
    view.forEach {
        it.show()
    }
}

fun View.hideGroupViews(vararg view: View) {
    view.forEach {
        it.hide()
    }
}

Спасибо! Это тот, который наиболее близок к тому, что я искал и использую в настоящее время. Я обновил решение, которое использую в своем вопросе. Ваше здоровье.

ZhouX 31.07.2019 05:00

Вы можете создать LinearLayout или любой другой ViewGroup, содержащий вашего дочернего Views, и дать ему идентификатор в XML-файле, а затем в вашем классе Activity или Fragment определить следующие функции:

    fun disableViewGroup(viewGroup: ViewGroup) {
        viewGroup.children.iterator().forEach {
            it.isEnabled = false
        }
    }

    fun enableViewGroup(viewGroup: ViewGroup) {
        viewGroup.children.iterator().forEach {
            it.isEnabled = true
        }
    }

А затем в onCreate() или onStart() назовите его следующим образом:

disableViewGroup(idOfViewGroup)

Метод children возвращает Sequence дочерних View в ViewGroup, который вы можете перебрать с помощью forEach и применить любую операцию, применимую к View.

Надеюсь, это поможет!

Другие вопросы по теме