Использовать группу в ConstraintLayout для прослушивания событий кликов на нескольких представлениях
В принципе, я хотел бы привязать один элемент OnClickListener к нескольким представлениям внутри ConstraintLayout.
Перед переносом на ConstraintLayout просмотры, в которых внутри одного макета, на который я мог бы добавить слушателя. Теперь они находятся на одном уровне с другими видами прямо под ConstraintLayout.
Я попытался добавить представления к android.support.constraint.Group
и программно добавил к нему OnClickListener.
group.setOnClickListener {
Log.d("OnClick", "groupClickListener triggered")
}
Однако это не похоже на версию ConstraintLayout 1.1.0-beta2
Я сделал что-то неправильно, есть ли способ достичь такого поведения или мне нужно приложить слушателя к каждому из отдельных представлений?
Ответы
Ответ 1
Group
в ConstraintLayout
- это просто слабая ассоциация взглядов AFAIK. Это не ViewGroup
, поэтому вы не сможете использовать прослушиватель с одним щелчком, как вы делали, когда представления были в ViewGroup
.
В качестве альтернативы, вы можете получить список идентификаторов, которые являются членами вашей Group
в вашем коде и явно установить прослушиватель кликов. (Я не нашел официальной документации по этой функции, но я считаю, что она просто отстает от выпуска кода.) Смотрите документацию по getReferencedIds
здесь.
Джава:
Group group = findViewById(R.id.group);
int refIds[] = group.getReferencedIds();
for (int id : refIds) {
findViewById(id).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// your code here.
}
});
}
В Kotlin вы можете создать для этого функцию расширения.
Котлин:
fun Group.setAllOnClickListener(listener: View.OnClickListener?) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
Затем вызовите функцию для группы:
group.setAllOnClickListener(View.OnClickListener {
// code to perform on click event
})
Обновить
Указанные идентификаторы не сразу доступны в 2.0.0-бета2, хотя они есть в 2.0.0-бета1 и ранее. "Разместите" приведенный выше код, чтобы получить ссылочные идентификаторы после размещения. Примерно так будет работать.
class MainActivity : AppCompatActivity() {
fun Group.setAllOnClickListener(listener: View.OnClickListener?) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Referenced ids are not available here but become available post-layout.
layout.post {
group.setAllOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
val text = (v as Button).text
Toast.makeText([email protected], text, Toast.LENGTH_SHORT).show()
}
})
}
}
}
Это должно работать для выпусков до 2.0.0-бета2, так что вы можете просто сделать это и не делать никаких проверок версий.
Ответ 2
Чтобы дополнить принятый ответ для пользователей Kotlin, создайте функцию расширения и примите лямбду, чтобы больше походить на API group.addOnClickListener { }
.
Создайте функцию расширения:
fun Group.addOnClickListener(listener: (view: View) -> Unit) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
использование:
group.addOnClickListener { v ->
Log.d("GroupExt", v)
}
Ответ 3
Лучший способ прослушать события клика из нескольких представлений - добавить прозрачный вид в качестве контейнера поверх всех необходимых представлений. Это представление должно находиться в конце (т.е. Сверху) всех представлений, по которым нужно выполнить щелчок.
Пример контейнера:
<View
android:id="@+id/view_container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@+id/view_bottom"
app:layout_constraintEnd_toEndOf="@+id/end_view_guideline"
app:layout_constraintStart_toStartOf="@+id/start_view_guideline"
app:layout_constraintTop_toTopOf="parent"/>
Вышеприведенный пример содержит все четыре границы ограничения, которые мы можем добавить к представлениям для совместного прослушивания, и, поскольку это представление, мы можем делать все, что захотим, например, эффект ряби.
Ответ 4
Метод расширения хорош, но вы можете сделать его еще лучше, изменив его на
fun Group.setAllOnClickListener(listener: (View) -> Unit) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
Так что призвание будет таким
group.setAllOnClickListener {
// code to perform on click event
}
Теперь необходимость явного определения View.OnClickListener исчезла.
Вы также можете определить свой собственный интерфейс для GroupOnClickLitener, как это
interface GroupOnClickListener {
fun onClick(group: Group)
}
а затем определить метод расширения, как это
fun Group.setAllOnClickListener(listener: GroupOnClickListener) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener { listener.onClick(this)}
}
}
и использовать это так
groupOne.setAllOnClickListener(this)
groupTwo.setAllOnClickListener(this)
groupThree.setAllOnClickListener(this)
override fun onClick(group: Group) {
when(group.id){
R.id.group1 -> //code for group1
R.id.group2 -> //code for group2
R.id.group3 -> //code for group3
else -> throw IllegalArgumentException("wrong group id")
}
}
Второй подход имеет лучшую производительность, если количество представлений велико, поскольку вы используете только один объект в качестве слушателя для всех представлений!
Ответ 5
В то время как мне нравится общий подход в Ответ Vitthalk, я думаю, что у него есть один главный недостаток и два незначительных.
-
В нем не учитываются динамические изменения позиции отдельных представлений
-
Он может регистрировать клики для просмотров, которые не входят в группу
- Это не общее решение этой довольно распространенной проблемы.
В то время как я не уверен в решении второго пункта, очевидно, что они очень легкие для первого и третьего.
1. Изменение позиции позиции в группе
Это на самом деле довольно просто. Для настройки краев прозрачного представления можно использовать набор инструментов макета ограничения.
Мы просто используем Barriers, чтобы получить самые левые, самые правые и т.д. Позиции любого вида в группе.
Затем мы можем настроить прозрачный вид на барьеры вместо конкретных видов.
3. Общее решение
Используя Kotlin, мы можем расширить Group-Class, чтобы включить метод, который добавляет ClickListener в представление, как описано выше.
Этот метод просто добавляет Барьеры к макету, обращая внимание на каждого дочернего элемента группы, прозрачное представление, которое выровнено с барьерами и регистрирует ClickListener для последнего.
Таким образом, нам просто нужно вызвать метод в группе и не нужно добавлять представления в макет вручную каждый раз, когда нам нужно это поведение.