Reputation: 1399
I'm currently working on a UI that looks like this
The blue part is a ConstraintLayout while the purple part is a RecyclerView inside it (it's a RecyclerView because it's content are dynamic based on service response).
I'm setting onClick
handler on the ConstraintLayout that would take the user to another page. The problem is the RecyclerView is consuming the clicks and not forwarding it to its parent. Thus onClick
handler works for the blue area, but not for the purple area.
I tried setting android:clickable="false"
and android:focusable="false"
in the RecyclerView but it still won't propagate the clicks to its parent.
One solution I came across is to extend from ConstraintLayout and override onInterceptTouchEvent()
to return true. However I have a strict requirement in my project to not create custom widgets, so I cannot use this solution.
Is there a way to tell RecyclerView to stop consuming touch events?
Activity layout:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_margin="16dp"
android:background="#42d7f4"
android:onClick="navigate"
android:padding="16dp">
<TextView
android:id="@+id/headerText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="FRUITS"
android:textSize="36sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/itemsList"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#9f41f2"
android:clickable="false"
android:focusable="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
Item layout:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/itemText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:clickable="false"
android:focusable="false" />
Activity.kt:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rcView = findViewById<RecyclerView>(R.id.itemsList)
rcView.layoutManager = LinearLayoutManager(this)
val items = listOf("Apple", "Banana", "Oranges", "Avocado")
rcView.adapter = ItemAdapter(items)
}
fun navigate(view: View) {
Toast.makeText(this, "Navigating to details page", Toast.LENGTH_SHORT)
.show()
}
}
class ItemAdapter(private val data: List<String>) : RecyclerView.Adapter<ItemViewHolder>() {
override fun getItemCount(): Int = data.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
return ItemViewHolder(view)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(data[position])
}
}
class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val itemTv: TextView = view.findViewById(R.id.itemText)
fun bind(item: String) {
itemTv.text = item
}
}
Upvotes: 5
Views: 1848
Reputation: 2821
If you freeze the layout after you've set the adapter it no longer swallows clicks:
recyclerView.isLayoutFrozen = true // kotlin
recyclerView.setLayoutFrozen(true); // java
Just keep in mind if you need to change the data in the adapter you have to unfreeze the layout before you call notifyDataSetChanged and then re-freeze the layout. I don't love this solution but it's the only one that worked for me.
Upvotes: 2
Reputation: 62841
One way to approach this is to set the RecyclerView
row with a click listener and to disable clicks and long clicks on the children of the RecyclerView
row as follows:
ConstraintLayout: `android:onClick="navigate"`
For the item layout: `android:onClick="navigate"`
TextView: android:clickable="false"
android:longClickable="false"
(etc. for all children of the row)
I think what is missing is to make the TextView
not long clickable.
Upvotes: 0
Reputation: 1968
Probably the easiest way to completely block interaction with anything inside a single view is to put a transparent view over it that intercepts all touch events. You can set click on that view manage all the functionality through that view.
For example like this
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_margin="16dp"
android:background="#42d7f4"
android:padding="16dp">
<TextView
android:id="@+id/headerText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="FRUITS"
android:textSize="36sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/itemsList"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#9f41f2"
android:clickable="false"
android:focusable="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/clickView"
android:layout_width="0dp"
android:layout_height="0dp"
android:onClick="navigate"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:clickable="true"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
Now you can functionality perform through that view instead through ConstraintLayout
.
Moreover, you look at this answer
Upvotes: -1
Reputation: 10538
You can set your RecyclerView
focus to false.
recyclerView.setFocusable(false);
And/Or set its rows view.setFocusable(false);
EDIT
If you want to give the ConstraintLayout
the focus
layout.setFocusable(true);
layout.setClickable(true);
//
// add click listener and event ....
//
For more information please refer to the official documentation provided by Google
Hope this helps, cheers
Upvotes: -1