Use group in ConstraintLayout to listen for click events on multiple views

constraintlayout group visibility not working
constraint_referenced_ids
group visibility android
when to use constraint layout
overlap views in constraint layout
how to use bias in constraint layout
android hide multiple views
nested constraint layout

Basically I'd like to attach a single OnClickListener to multiple views inside a ConstraintLayout.

Before migrating to the ConstraintLayout the views where inside one layout onto which I could add a listener. Now they are on the same layer with other views right under the ConstraintLayout.

I tried adding the views to a android.support.constraint.Group and added a OnClickListener to it programmatically.

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

However this does not seem to work as of the ConstraintLayout version 1.1.0-beta2

Have I done something wrong, is there a way to achieve this behaviour or do I need to attach the listener to each of the single views?

The Group in ConstraintLayout is just a loose association of views AFAIK. It is not a ViewGroup, so you will not be able to use a single click listener like you did when the views were in a ViewGroup.

As an alternative, you can get a list of ids that are members of your Group in your code and explicitly set the click listener. (I have not found official documentation on this feature, but I believe that it is just lagging the code release.) See documentation on getReferencedIds here.

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 you can build an extension function for that.

Kotlin:

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

Then call the function on the group:

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

Update

The referenced ids are not immediately available in 2.0.0-beta2 although they are in 2.0.0-beta1 and before. "Post" the code above to grab the reference ids after layout. Something like this will work.

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(this@MainActivity, text, Toast.LENGTH_SHORT).show()
                }
            })
        }
    }
}

This should work for releases prior to 2.0.0-beta2, so you can just do this and not have to do any version checks.

The ultimate view group: ConstraintLayout - Alan Camargo, We will use ConstraintLayout as the root layout, with both width and height as match_parent Now click on the Button and drag the vertical bias SeekBar to set the value to 0. Groups are invisible wrappers to multiple views. Build a Responsive UI with ConstraintLayout Part of Android Jetpack. ConstraintLayout allows you to create large and complex layouts with a flat view hierarchy (no nested view groups). It's similar to RelativeLayout in that all views are laid out according to relationships between sibling views and the parent layout, but it's more flexible than RelativeLayout and easier to use with Android Studio's Layout Editor.

To complement the accepted answer for Kotlin users create an extension function and accept a lambda to feel more like the API group.addOnClickListener { }.

Create the extension function:
fun Group.addOnClickListener(listener: (view: View) -> Unit) {
    referencedIds.forEach { id ->
        rootView.findViewById<View>(id).setOnClickListener(listener)
    }
}
usage:
group.addOnClickListener { v ->
    Log.d("GroupExt", v)
}

Group, Before migrating to the ConstraintLayout the views where inside one layout onto which I could add a listener. Now they are on the same layer with  Android ConstraintLayout is used to define a layout by assigning constraints for every child view/widget relative to other views present. A ConstraintLayout is similar to a RelativeLayout, but with more power. The aim of ConstraintLayout is to improve the performance of the applications by removing the nested views with a flat and flexible design.

The better way to listen to click events from multiple views is to add a transparent view as a container on top of all required views. This view has to be at the end (i.e on top) of all the views you need to perform a click on.

Sample container view :

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

Above sample contains all four constraint boundaries within that, we can add views that to listen together and as it is a view, we can do whatever we want, such as ripple effect.

Manage touch events in a ViewGroup, Use group in ConstraintLayout to listen for click The better way to listen to click events from multiple views is to add a transparent view as a  As you can see already, ConstraintLayout can do the same things as RelativeLayout, and more. Another nice addition from ConstraintLayout is the Guideline. This can be used in your ConstraintLayout to help set the position and size of your views.

The extension method is great but you can make it even better by changing it to

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

So the calling would be like this

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

Now the need for explicitly defining View.OnClickListener is now gone.

You can also define your own interface for GroupOnClickLitener like this

interface GroupOnClickListener {
    fun onClick(group: Group)
}

and then define an extension method like this

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

and use it like 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")
    }
}

The second approach has a better performance if the number of views is large since you only use one object as a listener for all the views!

ConstraintLayout in the LIMELIGHT, Use ViewConfiguration constants; Extend a child view's touchable area to have children that are targets for different touch events than the ViewGroup itself. In the following snippet, the class MyViewGroup extends ViewGroup . MyViewGroup contains multiple child views. If you drag your finger across a child view horizontally, the child view should no longer get touch events, and MyViewGroup should handle touch events by scrolling its contents. However, if you press buttons in the child view, or

While I like the general approach in Vitthalk's answer I think it has one major drawback and two minor ones.

  1. It does not account for dynamic position changes of the single views

  2. It may register clicks for views that are not part of the group

  3. It is not a generic solution to this rather common problem

While I'm not sure about a solution to the second point, there clearly are quite easy ones to the first and third.


1. Accounting position changes of element in the group

This is actually rather simple. One can use the toolset of the constraint layout to adjust the edges of the transparent view. We simply use Barriers to receive the leftmost, rightmost etc. positions of any View in the group. Then we can adjust the transparent view to the barriers instead of concrete views.

3. Generic solution

Using Kotlin we can extend the Group-Class to include a method that adds a ClickListener onto a View as described above. This method simply adds the Barriers to the layout paying attention to every child of the group, the transparent view that is aligned to the barriers and registers the ClickListener to the latter one.

This way we simply need to call the method on the Group and do not need to add the views to the layout manually everytime we need this behaviour.

How can I treat several views as one in ConstraintLayout, The Android team did a great job channeling ConstraintLayout's power through the In fact, the different combinations can help us have different positioning behaviors: Black view aligning bottom to bottom with the parent view group. Finally, we implemented the start button's click listener to handle the  To use ConstraintLayout, the appropriate support library must be included in the build.gradle (Module: app) file in your project. The constraint-layout dependency is provided as a separate support library that works on all Android versions back to Android 2.3 (Gingerbread).

OnClickListener for Multiple Buttons, constraint layout and set a click listener for it. Check the accepted answer for this one. Use group in ConstraintLayout to listen for click events on multiple views​  This is the activity java class, there are two listeners in this file, one is used to listen radio button check or uncheck event ( OnCheckedChangeListener) , the other is used to listen button click event ( OnClickListener). Please see code comments for more detail explanation.

Constraint layout recyclerview item, We will then use a switch/case statement to check for the button IDs and handle the Duration: 3:32 Posted: Dec 24, 2017 If you run the app and click any item in the grid, a message appears at the bottom of the screen. Note that you can use the same code to listen for click events on items inside a ListView. 7. Extending the ArrayAdapter. An ArrayAdapter can handle only one TextView widget inside the layout of the View objects it generates. To broaden its capabilities you must extend it.

It is a constraint layout with two text views to display brand name and product count and check box. RecyclerView With Multiple View Types. Implement RecyclerView In Android Implement Item Click Listener on RecyclerView Android Implement Also I will show how to add click and long click events within adapter. ConstraintLayout#clone has multiple overloaded versions and it takes xml layout or inflated ConstraintLayout widget. Then changeConstraints method is triggered by click on a fab and all we need to call is setTwo.applyTo, first like is to make it animated. So is it useful, where can we use it?

Comments
  • I love extension functions!
  • The kotlin extension didn't work for me: Attempt to invoke virtual method 'void android.view.View.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
  • Be careful doing this - the ConstraintLayout Group is designed to manage visibility. I just got bit by this - we were trying to use a group to be able to set a common click listener just like this answer, but also wanted to set the visibility of one of the views outside of the group, and it didn't work.
  • Just to look more kotlinish I made a small change fun Group.setAllOnClickListener(listener: (View) -> Unit) { referencedIds.forEach { id -> rootView.findViewById<View>(id).setOnClickListener(listener) } }
  • One issue with using this approach is that we will end up with non-clickable white spaces between elements of the group, which may result in poor user experience
  • or you can put it on the bottom if you want to change the background
  • This works pretty well. You don't need to group your views together. You can even add a selector to the View's background to indicate the user pressed it.