Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

android - Touch Event Interception when using ViewPager + NestedScrollView + RecyclerView

I'm using a ViewPager for swiping through pages, and each of these pages contains a NestedScrollView and a RecyclerView. The NestedScrollView is used to scroll the chart when using in split-screen mode.

fragment_accounts:

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ProgressBar
        android:id="@+id/mAccountsProgressbar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:visibility="visible" />

    <androidx.core.widget.NestedScrollView
        android:id="@+id/mAccountsScrollView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/mAccountsProgressbar">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <at.guger.moneybook.core.ui.widget.StrokePieChart
                android:id="@+id/mAccountsPieChart"
                android:layout_width="match_parent"
                android:layout_height="@dimen/default_chart_height"
                app:accounts="@{viewModel.accounts}"
                app:bold="true"
                app:fontResId="@font/eczar_regular"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/mAccountsProgressbar"
                app:textSize="42sp"
                tools:text="$ 1234,56" />

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/mAccountsRecyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginTop="8dp"
                tools:itemCount="4"
                tools:listitem="@layout/item_account" />

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Now, I'm using an OnTouchListener, to detect clicks in my RecyclerView. When swiping between pages in the ViewPager, the RecyclerView also detects long presses onto an item in the RecyclerView. How can I make sure, the RecyclerView does not use touch events when swiping to the next page?

To clearify: I want to receive long press events on items in the RecyclerView, but I don't want to receive them when the user is swiping between pages of the ViewPager!

OnItemTouchListener:

class OnItemTouchListener(context: Context, recyclerView: RecyclerView, private val onTouchCallback: ItemTouchListener) : RecyclerView.OnItemTouchListener {

    //region Variables

    private val gestureDetector: GestureDetectorCompat

    //endregion

    init {
        gestureDetector = GestureDetectorCompat(context, object : GestureDetector.SimpleOnGestureListener() {
            private val MIN_SWIPE_DISTANCE: Int = 50
            private lateinit var downMotionEvent: MotionEvent

            override fun onDown(e: MotionEvent?): Boolean {
                e?.let { downMotionEvent = it }

                return super.onDown(e)
            }

            override fun onSingleTapUp(e: MotionEvent?): Boolean {
                return true
            }

            override fun onLongPress(e: MotionEvent?) {
                e?.let {
                    val child: View? = recyclerView.findChildViewUnder(it.x, it.y)

                    if (child != null && !isGestureSwipe(it)) {
                        onTouchCallback.onItemLongClick(child, recyclerView.getChildLayoutPosition(child), it)
                    }
                }

                super.onLongPress(e)
            }

            fun isGestureSwipe(e: MotionEvent): Boolean {
                return downMotionEvent.x - e.x >= MIN_SWIPE_DISTANCE
            }
        })
    }

    //region TouchHandler

    override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
        val childView = rv.findChildViewUnder(e.x, e.y)

        if (childView != null && gestureDetector.onTouchEvent(e)) {
            onTouchCallback.onItemClick(childView, rv.getChildLayoutPosition(childView), e)
        }

        return false
    }

    override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {

    }

    override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {

    }

    //endregion

    interface ItemTouchListener {
        fun onItemClick(view: View, pos: Int, e: MotionEvent)
        fun onItemLongClick(view: View, pos: Int, e: MotionEvent)
    }

    companion object {

        fun isViewClicked(container: View, @IdRes viewId: Int, e: MotionEvent): Boolean {
            val view = container.findViewById<View>(viewId)

            return isViewClicked(view, e)
        }

        fun isViewClicked(view: View, e: MotionEvent): Boolean {
            val rect = Rect()
            view.getGlobalVisibleRect(rect)

            return view.isVisible && rect.contains(e.rawX.toInt(), e.rawY.toInt())
        }
    }
}

Parent Fragment, where fragment_accounts is embedded:

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/mHomeConstraintLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/mHomeViewPager"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:orientation="horizontal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/mHomeTabs" />

        <at.guger.moneybook.core.ui.widget.LabelVisibilityTabLayout
            android:id="@+id/mHomeTabs"
            style="@style/Widget.MaterialComponents.TabLayout"
            android:layout_width="0dp"
            android:layout_height="?attr/actionBarSize"
            android:backgroundTint="@android:color/transparent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:tabIconTint="@color/tab_highlight_selector"
            app:tabIndicator="@null"
            app:tabInlineLabel="true"
            app:tabMode="scrollable"
            app:tabRippleColor="@color/colorRippleMaterialDark"
            app:tabTextColor="@color/tab_highlight_selector" />
    </androidx.constraintlayout.widget.ConstraintLayout>


    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fabHomeAddTransaction"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:backgroundTint="?colorPrimary"
        android:clickable="true"
        android:contentDescription="@string/AddTransaction"
        android:focusable="true"
        android:src="@drawable/ic_add"
        app:layout_anchor="@+id/mHomeConstraintLayout"
        app:layout_anchorGravity="bottom|end"
        app:tint="?colorOnPrimary" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

All the other code can be found on Github (https://github.com/guger/MoneyBook).

question from:https://stackoverflow.com/questions/65935433/touch-event-interception-when-using-viewpager-nestedscrollview-recyclerview

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Probably playing with focus may help, I mean, if you're going to swipe, then clear focus from RecyclerView and if it is not focused you are now sure that the event onTouch() won't be calles, else you'd be calling it for the RecyclerView.

Otherwise I have not any idea about how to make it work, because or you swipe or you onLongPress


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...