3

I have a SwipeRefreshLayout with a nested RecyclerView. Each item in the recycler view essentially is a CardView with an onClickHandler attached. I have an issue where my clickHandler is not called if the recycler view is scrolled to the very top. If I scroll one px down I can click on things.

After adding printout statements on touch events and on click events it seems as if the swipe refresh layout will also intercept the touch event and setTargetOffsetTopAndBottom. This calls requestLayout which eventually seems to lead to an ACTION_CANCEL event. If I scroll down the recycler view 1px canChildScrollUp() will return true and stop the requestLayout call by the SwipRefreshLayout.

// SwipeRefreshLayout.java
public boolean onInterceptTouchEvent(MotionEvent ev) {
    ...
    if (!isEnabled() || mReturningToStart || canChildScrollUp()
            || mRefreshing || mNestedScrollInProgress) {
        // Fail fast if we're not in a state where a swipe is possible
        return false;
    }
    ...
    switch (action) {
        case MotionEvent.ACTION_DOWN:
            setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop());
            mActivePointerId = ev.getPointerId(0);
            mIsBeingDragged = false;

            pointerIndex = ev.findPointerIndex(mActivePointerId);
            if (pointerIndex < 0) {
                return false;
            }
            mInitialDownY = ev.getY(pointerIndex);
            break;
    ...

Layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ticket_wallet_refresh"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/ticket_wallet_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:itemCount="3"
        tools:listheader="@layout/ticket_header_item"
        tools:listitem="@layout/active_ticket_item" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

ViewHolder

sealed class FooViewHolder(view: View) : RecyclerView.ViewHolder(view) {

    abstract fun bind(item: WalletItem)

    class ActiveFooViewHolder(
        view: View,
        private val remainingTimeFormatter: RemainingTimeFormatter,
        private val callback: (Int) -> Unit
    ) : FooViewHolder(view) {

        init {
            view.setOnClickListener {
                callback(adapterPosition)
            }
   ...

My compile target is Android API lvl 29.

Viktor Lund
  • 225
  • 2
  • 8

2 Answers2

4

I have been experiencing the same issue. I did have a ConstraintLayout wrapping the SwipeRefreshLayout. When I removed the ConstraintLayout (replaced it with LinearLayout) the issue fixed. Hope that helps. I have no idea why it is working.

Katie B
  • 193
  • 1
  • 6
  • Setting parent layout with LinearLayout fixes the issue. – elbert rivas May 12 '20 at 14:55
  • For me this works! I can change ConstraintLayout with LinearLayout. I tried nesting layouts: - ConstraintLayout>LinearLayout>SwipeRefreshLayout does not work. I tried putting a margin/padding on the elements: - Putting a padding on the SwipeRefreshLayout works! Margin does not work. It must be top or bottom; start/end/left/right does not work. It can be 1px, 0.1px or even smaller, until a specific degree. This is very weird. Looks like a bug. – Kolonka Jun 12 '20 at 13:59
0

I experienced a similar issue. In my case, the onClick was blocked for a number of seconds and then magically returned. Scrolling might have solved it as well but I didn't have enough items to scroll them (in my app I was showing 3-5 Bluetooth devices). OnTouch was activated successfully; onClick was not.

I tried the answer proposed here and it didn't help. Neither did this or this.

What unexpectedly solved the issue was using notifyItemInserted(int position) in my addDevice(BluetoothDevice device) function instead of notifyDataSetChanged().

I have no idea why this worked. My RecyclerView held maybe 5 devices, I find it difficult to believe that notifying that the dataset changed takes up to 20 seconds.

In more detail:

My code is based on the Android example. In the example, in the DeviceScanActivity, there is the following code:

@Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mLeDeviceListAdapter.addDevice(device);
                    mLeDeviceListAdapter.notifyDataSetChanged();
                }
            });
        }

removing the line notifyDataSetChanged() and adding notifyItemInserted(<position of new device>) inside the addData function within the adapter solved the issue.

Danielle
  • 38
  • 9