ViewPager2 通過(guò)封裝 RecyclerView來(lái)實(shí)現(xiàn),主要優(yōu)勢(shì)有:
1. 支持垂直分頁(yè)??梢酝ㄟ^(guò)設(shè)置 ViewPager2 元素的 android:orientation 屬性為其啟用垂直分頁(yè)。
2. 支持從右到左 (RTL) 分頁(yè)。作用是適配阿拉伯語(yǔ)言國(guó)家從右到左的使用習(xí)慣。
3. 支持notifyDatasetChanged() 來(lái)更新界面。
使用
最近去面試時(shí),面試官問(wèn)我使用過(guò)ViewPager2嗎 ?我很尷尬的答不上來(lái)。回來(lái)就是一頓惡補(bǔ)ViewPager2知識(shí),把項(xiàng)目里的ViewPager都做了升級(jí)。
跟ViewPager相比,ViewPager2 API方面有幾個(gè)變動(dòng):
1.適配器:FragmentStateAdapter替換了原來(lái)的 FragmentStatePagerAdapter,與Recycleview的生命周期綁定。
2.頁(yè)面改變監(jiān)聽(tīng):registerOnPageChangeCallback替換了原來(lái)的 addPageChangeListener 。
踩坑
終于都升級(jí)到了ViewPager2,我滿意的滑動(dòng)著列表,卻感到了前所未有的卡頓。一番分析之后,確定是ViewPager2 + 刷新控件SmartRefreshLayout的鍋。前面說(shuō)過(guò),不同于ViewPager,ViewPager2 是封裝 RecyclerView實(shí)現(xiàn)的,在里面嵌套R(shí)efreshLayout+RecyclerView的時(shí)候會(huì)有滑動(dòng)沖突。遂到SmartRefreshLayout框架的Github上查看有木有解決方案。在Isseus里找到不少跟我一樣,因使用ViewPager2 導(dǎo)致的這個(gè)問(wèn)題,逛了一圈沒(méi)找到解決方案。
填坑
解決方案:自定義View,繼承FrameLayout,攔截事件分發(fā),判斷滑動(dòng)方向?yàn)樽笥一蛏舷?。作為包裹RefreshLayout + RecyclerView的容器。
/**
* @CreateDate : 2021/8/27 17:51
* @Author : 青檸
* @Description : 解決ViewPager2+RefreshLayout導(dǎo)致的滑動(dòng)沖突
*/
class RefreshSlideLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private var downX: Float = 0f
private var downY: Float = 0f
private var isDragged = false
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
private val HORIZONTAL = LinearLayout.HORIZONTAL
private val VERTICAL = LinearLayout.VERTICAL
private var orientation = VERTICAL
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
downX = ev.x
downY = ev.y
isDragged = false
}
MotionEvent.ACTION_MOVE -> {
if (!isDragged) {
val dx = abs(ev.x - downX)
val dy = abs(ev.y - downY)
if (orientation == HORIZONTAL) {
isDragged = dx > touchSlop && dx > dy
} else if (orientation == VERTICAL) {
isDragged = dy > touchSlop && dy > dx
}
}
parent.requestDisallowInterceptTouchEvent(isDragged)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
isDragged = false
//阻止父層的view獲取touch事件
parent.requestDisallowInterceptTouchEvent(false)
}
}
return super.onInterceptTouchEvent(ev)
}
}
在布局中使用:
<RefreshSlideLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.scwang.smart.refresh.layout.SmartRefreshLayout
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"
app:layout_constraintTop_toTopOf="parent"
app:srlEnableLoadMore="true">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
</RefreshSlideLayout>