阅读 698
解决折叠状态栏+viewPager2嵌套带有下拉刷新的RecyclerView滑动冲突

解决折叠状态栏+viewPager2嵌套带有下拉刷新的RecyclerView滑动冲突

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金

CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+viewPager2+fragment+下拉刷新的RecyclerView

下拉刷新框架PullRefreshLayout

下滑图片示例

QQ图片20210926195816.jpg

上滑图片示例

QQ图片20210926195756.jpg

特点是搜索框折叠起来了

当手指下滑的时候,因为AppBarLayout还没有折叠,滑动事件会被PtrFrameLayout消费,但上滑是如果AppBarLayout还没有折叠,滑动事件也会被PtrFrameLayout消费但和AppBarLayout.Behavior的滑动产生冲突,会发生滑动抖动,网上说重写AppBarLayout.Behavior可以解决,或者重写PtrFrameLayout,对上下滑动事件进行拦截,也可以。 这个涉及安卓事件分发机制,不会的朋友可以搜一下。 当下滑时让PtrFrameLayout自行消费,上滑时进行拦截。
以上是我个人理解,有错误希望大佬指出。

上代码 fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">
    
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".module.home.HomeFragment">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/appBarLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_scrollFlags="scroll|enterAlways|snap">
                <LinearLayout
                    android:id="@+id/ll_start_search"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <TextView
                        android:id="@+id/tv_title"
                        android:layout_width="36dp"
                        android:layout_height="36dp"
                        android:layout_gravity="left|center"
                        android:layout_marginLeft="16dp"
                        android:background="@drawable/ic_search_gray_24dp"
                        android:textColor="@color/textColorPrimary" />

                    <EditText
                        android:id="@+id/et_search"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="16dp"
                        android:layout_marginTop="6dp"
                        android:layout_marginRight="16dp"
                        android:layout_marginBottom="3dp"
                        android:maxLines="1"
                        android:background="@drawable/gray_rounded_shape"
                        android:drawableLeft="@drawable/ic_search_gray_24dp"
                        android:drawablePadding="8dp"
                        android:hint="do you need?"
                        android:padding="9dp"
                        android:textColorHint="#9ea1b0" />
                </LinearLayout>
            </com.google.android.material.appbar.CollapsingToolbarLayout>



            <net.lucode.hackware.magicindicator.MagicIndicator
                android:id="@+id/magic_indicator"
                android:layout_width="match_parent"
                android:layout_height="36dp"           
                android:paddingLeft="16dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:tabMode="scrollable" />

        </com.google.android.material.appbar.AppBarLayout>


            <androidx.viewpager2.widget.ViewPager2
                android:id="@+id/viewpager2_classify"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

</layout>
复制代码

因为要对PtrFrameLayout进行触摸拦截,所以重写PtrFrameLayout

class PullRefreshLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : PtrFrameLayout(context, attrs, defStyleAttr), PtrUIHandler {

    private val viewBinding = ViewRecyclerHeaderBinding.inflate(LayoutInflater.from(context), this, false)
    private var onPullRefreshListener: OnPullRefreshListener? = null

    companion object {
        private const val TAG = "PullRefreshLayout"
    }

    init {
        disableWhenHorizontalMove(true)
        isKeepHeaderWhenRefresh = true
        setRatioOfHeaderHeightToRefresh(1F)
        headerView = viewBinding.root
        addPtrUIHandler(this)
        setPtrHandler(
            object : PtrDefaultHandler() {
                override fun onRefreshBegin(layout: PtrFrameLayout) {
                    onPullRefreshListener?.onRefreshBegin()
                }
            })
    }

    override fun onUIReset(layout: PtrFrameLayout) {

    }

    override fun onUIRefreshPrepare(layout: PtrFrameLayout) {
        Log.d(TAG, "onUIRefreshPrepare")
        viewBinding.tvRefreshState.setText(R.string.refresh_pull_down_to_refresh)
    }

    override fun onUIRefreshBegin(layout: PtrFrameLayout) {
        Log.d(TAG, "onUIRefreshBegin")
        viewBinding.tvRefreshState.setText(R.string.refresh_refreshing)
    }

    override fun onUIRefreshComplete(layout: PtrFrameLayout) {
        Log.d(TAG, "onUIRefreshComplete")
        viewBinding.tvRefreshState.setText(R.string.refresh_refresh_complete)
    }

    override fun onUIPositionChange(layout: PtrFrameLayout, isUnderTouch: Boolean, status: Byte, ptrIndicator: PtrIndicator) {
        val offsetToRefresh: Int = layout.offsetToRefresh
        val currentPos = ptrIndicator.currentPosY
        val lastPos = ptrIndicator.lastPosY

        if (currentPos < offsetToRefresh && lastPos >= offsetToRefresh) {
            if (isUnderTouch && status == PTR_STATUS_PREPARE) {
                viewBinding.tvRefreshState.setText(R.string.refresh_pull_down_to_refresh)
            }
        } else if (currentPos > offsetToRefresh && lastPos <= offsetToRefresh) {
            if (isUnderTouch && status == PTR_STATUS_PREPARE) {
                viewBinding.tvRefreshState.setText(R.string.refresh_release_to_refresh)
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
            }
        }
    }

    fun setOnPullRefreshListener(listener: OnPullRefreshListener) {
        this.onPullRefreshListener = listener
    }

    interface OnPullRefreshListener {
        fun onRefreshBegin()
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {

        val x: Float = ev.getX()
        val y: Float = ev.getY()
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                //将按下时的坐标存储
                downX = x
                downY = y
            }
            MotionEvent.ACTION_MOVE -> {
                //获取到距离差
                val dx: Float = x - downX
                val dy: Float = y - downY
                //通过距离差判断方向
                val orientation: Int = getOrientation(dx, dy)
                when (orientation) {
                    UP ->{
                        return super.dispatchTouchEventSupper(ev)
                    }
                    DOWN->{
                        //下拉刷新,让父布局禁用拦截事件功能,从而忽略该事件之后的一切Action,目的是防止下拉刷新时,左右滑动传递到ViewPager
                        parent.requestDisallowInterceptTouchEvent(true)
                    }
                }
            }

        }
        return super.dispatchTouchEvent(ev)
    }

    private var downX: Float = 0f//按下时 的X坐标
    private var downY: Float = 0f//按下时 的Y坐标
    private val LEFT: Int = 0
    private val RIGHT: Int = 1
    private val UP: Int = 2
    private val DOWN: Int = 3
    private var isDown :Boolean =false
    private fun getOrientation(dx: Float, dy: Float): Int {
        return if (Math.abs(dx) > Math.abs(dy)) {
            //X轴移动
            if (dx > 0) RIGHT else LEFT //右,左
        } else {
            //Y轴移动
            if (dy > 0) DOWN else UP //下//上
        }
    }

}

复制代码

view_recycler_header.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

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

        <TextView
            android:id="@+id/tv_refresh_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="sans-serif-medium"
            android:text="下拉刷新"
            android:textSize="15sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

复制代码

搞定

文章分类
Android
文章标签