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

Basically, I would like to bind one OnClickListener element to several views inside ConstraintLayout.

Before porting to ConstraintLayout, there are views in which there is one layout within which I could add a listener. Now they are on par with other species right below ConstraintLayout.

I tried to add views to android.support.constraint.Group and programmatically added OnClickListener to it.

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

However, this is not like ConstraintLayout version 1.1.0-beta2

I did something wrong, is there a way to achieve this behavior, or do I need to attach a listener to each of the individual views?

+20
source share
5 answers

Group at ConstraintLayout is just a weak association of AFAIK views. This is not a ViewGroup , so you cannot use the one-click listener, as you did when the views were in the ViewGroup .

Alternatively, you can get a list of identifiers that are members of your Group in your code and explicitly set a click listener. (I did not find the official documentation for this function, but I believe that it is simply behind the release of the code.) See the documentation for 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 create an extension function for this.

Kotlin:

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

Then call the function for the group:

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

Refresh

These identifiers are not immediately available in 2.0.0-beta2, although they are in 2.0.0-beta1 and earlier. "Post" the above code to get reference identifiers after posting. 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 it and not do any version checks.

+37
source

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

Create an extension function:

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

using:

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

The best way to listen for click events from multiple views is to add a transparent view as a container on top of all the necessary views. This view should be at the end (i.e., Top) of all views that you want to click on.

Container example:

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

The above example contains all four bounds of a constraint that we can add to presentations for listening together, and since this is an idea, we can do whatever we want, such as a ripple effect.

+4
source

The extension method is good, 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 vocation will be like that

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

Now the need to explicitly define View.OnClickListener has disappeared.

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 that

 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 better performance if the number of views is large, since you use only one object as a listener for all views!

+2
source

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

  • It does not take into account dynamic changes in the position of individual views

  • It can record clicks for views that are not in the group.

  • This is not a general solution to this rather common problem.

While I'm not sure about the solution to the second point, it is obvious that they are very easy for the first and third.


1. Change the position of a position in a group

It is actually quite simple. You can use the constraint layout toolbox to customize the edges of a transparent view. We just use Barriers to get the leftmost , rightmost , etc. Positions of any kind in the group. Then we can set up a transparent view of the barriers instead of specific views.

3. General decision

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

Thus, we just need to call the method in the group and do not need to add the views to the layout manually every time we need this behavior.

+1
source

Source: https://habr.com/ru/post/1272620/


All Articles