個人適合做什么網(wǎng)站域名??烤W(wǎng)頁推廣大全
背景
ViewPager2內(nèi)嵌套橫向滑動的RecyclerView,會有滑動沖突的情況,引入官方提供的NestedScrollableHost類可以解決沖突問題,但是有一些瑕疵,滑動橫向RecyclerView到頂部,按住它不放手繼續(xù)往左拖再往右拖,這時候會發(fā)現(xiàn)外層ViewPager2滑動了,而不是橫向RecyclerView滑動,于是參考NestedScrollableHost進(jìn)行邏輯完善
完整代碼
- 主要是增加判斷外層ViewPager2是否可滾動來設(shè)置是否允許父View攔截事件
open class NestRecyclerView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null
): RecyclerView(context, attrs) {private var initialX = 0fprivate var initialY = 0fprivate val parentViewPager: ViewPager2?get() {var v: View? = parent as? Viewwhile (v != null && v !is ViewPager2) {v = v.parent as? View}return v as? ViewPager2}private fun canViewScroll(target: View?, orientation: Int, delta: Float): Boolean {val direction = -delta.sign.toInt()return when (orientation) {0 -> target?.canScrollHorizontally(direction) ?: false1 -> target?.canScrollVertically(direction) ?: falseelse -> throw IllegalArgumentException()}}override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {val orientation = parentViewPager?.orientation ?: return super.onInterceptTouchEvent(event)if (!canViewScroll(this, orientation, -1f) && !canViewScroll(this, orientation, 1f)) {return super.onInterceptTouchEvent(event)}when (event?.action) {MotionEvent.ACTION_DOWN -> {initialX = event.xinitialY = event.yparent.requestDisallowInterceptTouchEvent(true)}MotionEvent.ACTION_MOVE -> {val dx = event.x - initialXval dy = event.y - initialYval isVpHorizontal = orientation == ViewPager2.ORIENTATION_HORIZONTALif (isVpHorizontal == dy.absoluteValue > dx.absoluteValue) {parent.requestDisallowInterceptTouchEvent(false)} else {if (canViewScroll(this, orientation, if (isVpHorizontal) dx else dy)) {parent.requestDisallowInterceptTouchEvent(true)} else {if (canViewScroll(parentViewPager, orientation, if (isVpHorizontal) dx else dy)) {parent.requestDisallowInterceptTouchEvent(false)} else {parent.requestDisallowInterceptTouchEvent(true)}}}}}return super.onInterceptTouchEvent(event)}
}
向上滑動AppBarLayout不聯(lián)動問題
如果布局CoordinatorLayout + AppBarLayout + ViewPager2內(nèi)嵌套橫向滑動的RecyclerView,這時拖拽橫向滑動的RecyclerView向上移,AppBarLayout不會跟著向上移
原因分析
-
拖拽橫向滑動的RecyclerView向上移時,CoordinatorLayout.onNestedPreScroll內(nèi)的lp.isNestedScrollAccepted(type)返回false,造成AppBarLayout沒有執(zhí)行scroll
-
lp.isNestedScrollAccepted(type)被賦值的地方,會根據(jù)AppBarLayout$Behavior.onStartNestedScroll返回的accepted進(jìn)行賦值
-
AppBarLayout$Behavior.onStartNestedScroll內(nèi),會判斷nestedScrollAxes的值不是2就返回false
-
RecyclerView也支持嵌套滑動。startNestedScroll是由NestedScrollingChildHelper實(shí)現(xiàn)的,它會將嵌套滑動上傳,也就是NestedScrollingChild都會將嵌套滑動先交給NestedScrollingParent處理。
class RecyclerView...public boolean onInterceptTouchEvent(MotionEvent e) {if (mLayoutSuppressed) {// When layout is suppressed, RV does not intercept the motion event.// A child view e.g. a button may still get the click.return false;}// Clear the active onInterceptTouchListener. None should be set at this time, and if one// is, it's because some other code didn't follow the standard contract.mInterceptingOnItemTouchListener = null;if (findInterceptingOnItemTouchListener(e)) {cancelScroll();return true;}if (mLayout == null) {return false;}final boolean canScrollHorizontally = mLayout.canScrollHorizontally();final boolean canScrollVertically = mLayout.canScrollVertically();if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(e);final int action = e.getActionMasked();final int actionIndex = e.getActionIndex();switch (action) {case MotionEvent.ACTION_DOWN:if (mIgnoreMotionEventTillDown) {mIgnoreMotionEventTillDown = false;}mScrollPointerId = e.getPointerId(0);mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);if (mScrollState == SCROLL_STATE_SETTLING) {getParent().requestDisallowInterceptTouchEvent(true);setScrollState(SCROLL_STATE_DRAGGING);stopNestedScroll(TYPE_NON_TOUCH);}// Clear the nested offsetsmNestedOffsets[0] = mNestedOffsets[1] = 0;int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;if (canScrollHorizontally) {nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;}if (canScrollVertically) {nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;}startNestedScroll(nestedScrollAxis, TYPE_TOUCH);break;...return mScrollState == SCROLL_STATE_DRAGGING;}
這里RecyclerView是橫向的,所以nestedScrollAxis會被賦值為1,RecyclerView內(nèi)調(diào)用startNestedScroll會向上層view傳遞,直到交給CoordinatorLayout處理,而CoordinatorLayout在調(diào)用onStartNestedScroll的時候,AppBarLayout$Behavior.onStartNestedScroll又返回false了,造成CoordinatorLayout回調(diào)onNestedPreScroll(由RecyclerView在ACTION_MOVE時調(diào)用dispatchNestedPreScroll觸發(fā))時無法調(diào)用AppBarLayout的滾動。
解決方法
在CoordinatorLayout調(diào)用onStartNestedScroll的時候不處理橫向的情況,就不會導(dǎo)致lp.isNestedScrollAccepted(type)被賦值
class NestedCoordinatorLayout @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null
): CoordinatorLayout(context, attrs) {override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {return if (axes and ViewCompat.SCROLL_AXIS_HORIZONTAL != 0) {false} else super.onStartNestedScroll(child, target, axes, type)}
}