Verwenden Sie die Gruppe in ConstraintLayout, um auf Klickereignisse in mehreren Ansichten zu lauschen

Lesezeit: 7 Minuten

Grundsätzlich möchte ich einen einzelnen OnClickListener an mehrere Ansichten innerhalb eines ConstraintLayout anhängen.

Vor der Migration zum ConstraintLayout befanden sich die Ansichten in einem Layout, dem ich einen Listener hinzufügen konnte. Jetzt befinden sie sich auf derselben Ebene mit anderen Ansichten direkt unter dem ConstraintLayout.

Ich habe versucht, die Ansichten zu a hinzuzufügen android.support.constraint.Group und programmgesteuert einen OnClickListener hinzugefügt.

group.setOnClickListener {
    Log.d("OnClick", "groupClickListener triggered")
}

Dies scheint jedoch ab der ConstraintLayout-Version nicht zu funktionieren 1.1.0-beta2

Habe ich etwas falsch gemacht, gibt es eine Möglichkeit, dieses Verhalten zu erreichen oder muss ich den Zuhörer an jede der einzelnen Ansichten binden?

Benutzer-Avatar
Cheticamp

Das Group in ConstraintLayout ist nur eine lose Vereinigung von Ansichten AFAIK. Es ist nicht ein ViewGroupsodass Sie keinen Einzelklick-Listener verwenden können, wie Sie es bei den Ansichten in a getan haben ViewGroup.

Alternativ können Sie eine Liste mit IDs abrufen, die Mitglieder Ihrer sind Group in Ihrem Code und legen Sie den Klick-Listener explizit fest. (Ich habe keine offizielle Dokumentation zu dieser Funktion gefunden, aber ich glaube, dass sie der Code-Veröffentlichung nur hinterherhinkt.) Siehe Dokumentation auf getReferencedIds hier.

Java:

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

In Kotlin kann man dafür eine Erweiterungsfunktion bauen.

Kotlin:

    fun Group.setAllOnClickListener(listener: View.OnClickListener?) {
        referencedIds.forEach { id ->
            rootView.findViewById<View>(id).setOnClickListener(listener)
        }
    }

Rufen Sie dann die Funktion für die Gruppe auf:

    group.setAllOnClickListener(View.OnClickListener {
        // code to perform on click event
    })

Aktualisieren

Die referenzierten IDs sind nicht sofort verfügbar in 2.0.0-beta2 obwohl sie drin sind 2.0.0-beta1 und davor. “Posten” Sie den obigen Code, um die Referenz-IDs nach dem Layout zu erhalten. So etwas wird funktionieren.

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

Dies sollte für Versionen vor funktionieren 2.0.0-beta2sodass Sie dies einfach tun können und keine Versionsprüfungen durchführen müssen.

  • Ich liebe Erweiterungsfunktionen!

    – Ivan Woll

    15. März 2018 um 10:51 Uhr

  • Die Kotlin-Erweiterung hat bei mir nicht funktioniert: Versuch, die virtuelle Methode „void android.view.View.setOnClickListener(android.view.View$OnClickListener)“ für eine Nullobjektreferenz aufzurufen

    – Guilherme Lima Pereira

    25. Juni 2018 um 13:04 Uhr

  • Seien Sie dabei vorsichtig – die ConstraintLayout-Gruppe wurde entwickelt, um zu verwalten Sichtweite. Ich wurde gerade davon gebissen – wir haben versucht, eine Gruppe zu verwenden, um einen gemeinsamen Klick-Listener wie diese Antwort festzulegen, wollten aber auch die Sichtbarkeit einer der Ansichten außerhalb der Gruppe festlegen, und das tat es nicht Arbeit.

    – Jeff Barger

    30. Juli 2018 um 21:24 Uhr

  • Nur um kotliniger auszusehen, habe ich eine kleine Änderung vorgenommen fun Group.setAllOnClickListener(listener: (View) -> Unit) { referencedIds.forEach { id -> rootView.findViewById<View>(id).setOnClickListener(listener) } }

    – lucas_sales

    5. Oktober 2018 um 7:16 Uhr


  • Ein Problem bei der Verwendung dieses Ansatzes besteht darin, dass wir am Ende nicht anklickbare Leerzeichen zwischen Elementen der Gruppe haben, was zu einer schlechten Benutzererfahrung führen kann

    – AntekM

    8. August 2019 um 13:02 Uhr

Benutzer-Avatar
Vitthalk

Die bessere Möglichkeit, Klickereignisse aus mehreren Ansichten abzuhören, besteht darin, eine transparente Ansicht als Container über allen erforderlichen Ansichten hinzuzufügen. Diese Ansicht muss am Ende (dh ganz oben) aller Ansichten stehen, auf die Sie klicken müssen.

Beispiel-Containeransicht:

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

Das obige Beispiel enthält alle vier Einschränkungsgrenzen, wir können Ansichten hinzufügen, die zusammengehört werden sollen, und da es sich um eine Ansicht handelt, können wir tun, was wir wollen, z. B. einen Welleneffekt.

  • oder Sie können es auf die Unterseite setzen, wenn Sie den Hintergrund ändern möchten

    – Michael Vescovo

    12. Februar 2018 um 0:31 Uhr

  • Das funktioniert ziemlich gut. Sie müssen Ihre Ansichten nicht gruppieren. Sie können sogar einen Selektor zum Hintergrund der Ansicht hinzufügen, um anzuzeigen, dass der Benutzer ihn gedrückt hat.

    – Hoffnung

    26. Juni 2019 um 0:54 Uhr

  • Haben Sie einen Tipp, wie Sie diese Ansicht etwas größer als durch Einschränkungen angegeben machen können? Wie 4dp auf jeder Seite der Ansicht hinzufügen?

    – pkuszewski

    16. März 2020 um 15:17 Uhr

  • Ich finde es die sauberste Lösung. Es ist nicht schick und nicht perfekt, aber sauberer als eines, das den Rest überstimmt. Menschen wählen beliebte, ausgefallene Dinge und Entwickler sind (leider) nicht anders.

    – Przemo

    11. Mai 2020 um 10:09 Uhr


Um die akzeptierte Antwort für Kotlin-Benutzer zu ergänzen, erstellen Sie eine Erweiterungsfunktion und akzeptieren Sie ein Lambda, um sich eher wie die API zu fühlen group.addOnClickListener { }.

Erstellen Sie die Erweiterungsfunktion:

fun Group.addOnClickListener(listener: (view: View) -> Unit) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener(listener)
    }
}

Verwendungszweck:

group.addOnClickListener { v ->
    Log.d("GroupExt", v)
}

  • Gibt es eine Möglichkeit, die findViewByID nicht zu verwenden?

    – Cyd

    16. November 2020 um 18:18 Uhr

  • @Cyd – Ich glaube nicht; Sie müssen die Ansichten finden. Denken Sie daran, dass dies nur einmal pro Ansicht während der Einrichtung geschieht, nicht beim Klicken.

    – Charles Allen

    4. Dezember 2020 um 4:18 Uhr

Benutzer-Avatar
Alireza A. Ahmadi

Die Erweiterungsmethode ist großartig, aber Sie können sie noch besser machen, indem Sie sie in ändern

fun Group.setAllOnClickListener(listener: (View) -> Unit) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener(listener)
    }
}

Die Berufung würde also so aussehen

group.setAllOnClickListener {
    // code to perform on click event
}

Jetzt muss View.OnClickListener nicht mehr explizit definiert werden.

Sie können auf diese Weise auch Ihre eigene Schnittstelle für GroupOnClickLitener definieren

interface GroupOnClickListener {
    fun onClick(group: Group)
}

und definieren Sie dann eine Erweiterungsmethode wie diese

fun Group.setAllOnClickListener(listener: GroupOnClickListener) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener { listener.onClick(this)}
    }
}

und benutze es so

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

Der zweite Ansatz hat eine bessere Leistung, wenn die Anzahl der Ansichten groß ist, da Sie nur ein Objekt als Listener für alle Ansichten verwenden!

Obwohl ich den allgemeinen Ansatz in Vitthalks Antwort mag, denke ich, dass er einen großen und zwei kleinere Nachteile hat.

  1. Dynamische Positionsänderungen der einzelnen Ansichten werden nicht berücksichtigt

  2. Es kann Klicks für Ansichten registrieren, die nicht Teil der Gruppe sind

  3. Es ist keine generische Lösung für dieses ziemlich häufige Problem

Während ich mir bezüglich einer Lösung für den zweiten Punkt nicht sicher bin, gibt es für den ersten und dritten eindeutig recht einfache.


1. Buchungspositionsänderungen eines Elements in der Gruppe

Das ist eigentlich ziemlich einfach. Man kann das Toolset des Constraint-Layouts verwenden, um die Ränder der transparenten Ansicht anzupassen. Wir verwenden einfach Barrieren um die Positionen ganz links, ganz rechts usw. einer beliebigen Ansicht in der Gruppe zu erhalten. Dann können wir anstelle konkreter Ansichten die transparente Ansicht an die Barrieren anpassen.

3. Generische Lösung

Mit Kotlin können wir die Group-Klasse um eine Methode erweitern, die einen ClickListener wie oben beschrieben zu einer View hinzufügt. Diese Methode fügt einfach die Barriers zum Layout hinzu, wobei auf jedes untergeordnete Element der Gruppe geachtet wird, die transparente Ansicht, die an den Barrieren ausgerichtet ist, und registriert den ClickListener für letztere.

Auf diese Weise müssen wir einfach die Methode für die Gruppe aufrufen und müssen die Ansichten nicht jedes Mal manuell zum Layout hinzufügen, wenn wir dieses Verhalten benötigen.

Für die Java-Leute da draußen wie mich:

public class MyConstraintLayoutGroup extends Group {
    public MyConstraintLayoutGroup(Context context) {
        super(context);
    }

    public MyConstraintLayoutGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyConstraintLayoutGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setOnClickListener(OnClickListener listener) {
        for (int id : getReferencedIds()) {
            getRootView().findViewById(id).setOnClickListener(listener);
        }
    }
}

Ich mag es nicht, dass dies keine Klickzustände an alle anderen untergeordneten Elemente weitergibt.

1019780cookie-checkVerwenden Sie die Gruppe in ConstraintLayout, um auf Klickereignisse in mehreren Ansichten zu lauschen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy