Friday, 25 June 2021

Click through RecyclerView ViewHolder

I have a RecyclerView that displays an Button which extends outside its parent ViewHolder. To the button I added a clickListener to display a toast. If you click on the Button and the click is on the area of the Button parent ViewHolder, the toast shows, but if you click on the button but outside its parent ViewHolder the toast doesn't show anymore.

Toast shows if you click here

Toast doesn't show if you click here

Here's what I currently have

RecyclerView:

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:focusedByDefault="false"
        android:scrollbars="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/indicator"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:reverseLayout="true"
        app:stackFromEnd="true" />

Item view:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/layout"
    android:layout_width="15dp"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clickable="false"
    android:focusable="false"
    android:focusableInTouchMode="false"
    android:clipToPadding="false"
    android:elevation="0dp"
    android:scaleY="-1.0">

    <View
        android:id="@+id/vertical"
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:alpha="0.25"
        android:background="#ffffff"
        android:visibility="gone"
        android:layout_marginVertical="5dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/annotation"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:scaleY="-1.0"
        android:elevation="100dp"
        android:text="TEST ANNOTATION"
        android:clickable="true"
        android:focusable="true"
        android:focusableInTouchMode="true"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Adapter:

class ChartAdapter(
    private val dataSet: MutableList<Float>,
    private val context: Context
) : RecyclerView.Adapter<ChartAdapter.ViewHolder>() {
    private var scaleFactor: Float = 1.0f

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val layout: ConstraintLayout = view.findViewById(R.id.layout)
        val vertical: View = view.findViewById(R.id.vertical)
        val annotation: View = view.findViewById(R.id.annotation)
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder =
        ViewHolder(
            LayoutInflater.from(viewGroup.context)
                .inflate(R.layout.layout_quadrant, viewGroup, false)
        )

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
        viewHolder.layout.layoutParams = ConstraintLayout.LayoutParams(
            (39 * this.scaleFactor).roundToInt(),
            ConstraintLayout.LayoutParams.MATCH_PARENT
        )

        ConstraintSet().apply {
            clone(viewHolder.layout)
            applyTo(viewHolder.layout)
        }

        viewHolder.annotation.scaleX = this.scaleFactor
        viewHolder.annotation.scaleY = this.scaleFactor * -1

        viewHolder.vertical.visibility = when {
            position % 5 == 0 || position == dataSet.size - 1 -> View.VISIBLE
            else -> View.GONE
        }

        viewHolder.annotation.visibility = when (position) {
            15 -> View.VISIBLE
            else -> View.GONE
        }

        viewHolder.annotation.setOnClickListener {
            Toast.makeText(context, "annotation clicked!", Toast.LENGTH_SHORT).show()
        }
    }

    override fun getItemCount() = dataSet.size

    fun setScaleFactor(scaleFactor: Float) {
        this.scaleFactor = scaleFactor
        notifyDataSetChanged()
    }
}

As you can see I've tried to add android:clickable="false", android:focusable="false" and android:focusableInTouchMode="false" to the Item layout and the RecyclerView to prevent it from intercepting the click action but no luck the parent always keep intercepting the click thus preventing the button from being clicked.

Also I've tried this on the ViewHolder but no luck

inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val layout: ConstraintLayout = view.findViewById(R.id.layout)
    val vertical: View = view.findViewById(R.id.vertical)
    val annotation: View = view.findViewById(R.id.annotation)

    init {
        view.layoutParams = WindowManager.LayoutParams().apply {
            height = WindowManager.LayoutParams.MATCH_PARENT;
            width = WindowManager.LayoutParams.WRAP_CONTENT;
            flags =
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
        }
        view.isClickable = false
        view.isFocusable = false
        view.isFocusableInTouchMode = false
        val itemTouchListener = View.OnTouchListener { v, event -> false }
        view.setOnTouchListener(itemTouchListener)
    }
}


from Click through RecyclerView ViewHolder

No comments:

Post a Comment