Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 331 Vote(s) - 3.63 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Use group in ConstraintLayout to listen for click events on multiple views

#1
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?
Reply

#2
While I like the general approach in [Vitthalk's answer][1] 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][2] 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.

[1]:

[To see links please register here]

[2]:

[To see links please register here]

Reply

#3
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)
}
Reply

#4
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.



Reply

#5
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!
Reply

#6
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. <s>(I have not found official documentation on this feature, but I believe that it is just lagging the code release.)</s> See documentation on `getReferencedIds` [here](

[To see links please register 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.
Reply

#7
in Constraintlayout 2.0.0,you can use Layer to resolve multiple views click event,and also support scale animation
Reply

#8
fun ConstraintLayout.setAllOnClickListener(listener: (View) -> Unit) {
children.forEach { view ->
rootView.findViewById<View>(view.id).setOnClickListener { listener.invoke(this) }
}
}

And then

.setAllOnClickListener {
do something
}
Reply

#9
For the Java people out there like me:

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

This is not propagating click states to all other children however.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through