CoordinatorLayout+AppBarLayout+viewPager2+fragment+下拉刷新的RecyclerView
下滑
上滑
当手指下滑的时候,因为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
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)
}
}
}
}
return super.dispatchTouchEvent(ev)
}
override fun dispatchTouchEventSupper(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)
}
}
}
}
return super.dispatchTouchEventSupper(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 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="@string/refresh_pull_down_to_refresh"
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>