我們在使用RecyclerView嵌套R(shí)ecyclerView時(shí)绝淡,會(huì)覺得卡頓,操作不是很順暢
RecyclerView嵌套R(shí)ecyclerView滑動(dòng).gif
在這個(gè)現(xiàn)象中,我們想要水平滑動(dòng)時(shí),卻豎直滑動(dòng)了
我們觀察RecyclerView的源碼续扔,在onInterceptTouchEvent方法中對(duì)事件做了處理
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (mLayoutFrozen) {
// When layout is frozen, RV does not intercept the motion event.
// A child view e.g. a button may still get the click.
return false;
}
if (dispatchOnItemTouchIntercept(e)) {
cancelTouch();
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 = MotionEventCompat.getActionMasked(e);
final int actionIndex = MotionEventCompat.getActionIndex(e);
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);
}
// Clear the nested offsets
mNestedOffsets[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);
break;
case MotionEventCompat.ACTION_POINTER_DOWN:
mScrollPointerId = e.getPointerId(actionIndex);
mInitialTouchX = mLastTouchX = (int) (e.getX(actionIndex) + 0.5f);
mInitialTouchY = mLastTouchY = (int) (e.getY(actionIndex) + 0.5f);
break;
case MotionEvent.ACTION_MOVE: {
final int index = e.findPointerIndex(mScrollPointerId);
if (index < 0) {
Log.e(TAG, "Error processing scroll; pointer index for id " +
mScrollPointerId + " not found. Did any MotionEvents get skipped?");
return false;
}
final int x = (int) (e.getX(index) + 0.5f);
final int y = (int) (e.getY(index) + 0.5f);
if (mScrollState != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
boolean startScroll = false;
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
startScroll = true;
}
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
startScroll = true;
}
if (startScroll) {
setScrollState(SCROLL_STATE_DRAGGING);
}
}
} break;
case MotionEventCompat.ACTION_POINTER_UP: {
onPointerUp(e);
} break;
case MotionEvent.ACTION_UP: {
mVelocityTracker.clear();
stopNestedScroll();
} break;
case MotionEvent.ACTION_CANCEL: {
cancelTouch();
}
}
return mScrollState == SCROLL_STATE_DRAGGING;
}
主要看MOVE事件攻臀,RecyclerView中只做了距離判斷,只要滑動(dòng)的距離大于系統(tǒng)的默認(rèn)滑動(dòng)距離閾值纱昧,則本身進(jìn)行滑動(dòng)刨啸,這就導(dǎo)致了為什么我們水平滑動(dòng)的時(shí)候,子RecyclerView接收不到Touch事件的原因识脆,我們針對(duì)這點(diǎn)進(jìn)行優(yōu)化
/**
* 解決嵌套R(shí)ecyclerView滑動(dòng)問題
*/
public class BetterRecyclerView extends RecyclerView {
private int mTouchSlop;
private int mScrollPointerId = 0;
private int mInitialTouchX;
private int mInitialTouchY;
public BetterRecyclerView(@NonNull Context context) {
this(context, null);
}
public BetterRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BetterRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final ViewConfiguration vc = ViewConfiguration.get(context);
mTouchSlop = vc.getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
//仿造源碼
final int action = MotionEventCompat.getActionMasked(e);
final int actionIndex = MotionEventCompat.getActionIndex(e);
switch (action) {
case MotionEvent.ACTION_DOWN://記錄按下的坐標(biāo)
mScrollPointerId = e.getPointerId(0);
mInitialTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = (int) (e.getY() + 0.5f);
break;
case MotionEvent.ACTION_POINTER_DOWN://這邊處理多手指觸摸呜投,優(yōu)先使用后面觸摸的手指
mScrollPointerId = e.getPointerId(actionIndex);
mInitialTouchX = (int) (e.getX() + 0.5f);
mInitialTouchY = (int) (e.getY() + 0.5f);
break;
case MotionEvent.ACTION_MOVE://仿造源碼
final int index = e.findPointerIndex(mScrollPointerId);
final int x = (int) (e.getX(index) + 0.5f);
final int y = (int) (e.getY(index) + 0.5f);
boolean startScroll = false;
if (getScrollState() != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
//這邊增加攔截的難度,當(dāng)滑動(dòng)的水平距離大于滑動(dòng)的垂直距離時(shí)存璃,才攔截,當(dāng)然了雕拼,如果自身又能豎直滑動(dòng)的情況纵东,就不判斷兩個(gè)滑動(dòng)距離大小
if ((getLayoutManager().canScrollHorizontally() && Math.abs(dx) > mTouchSlop)
&& (getLayoutManager().canScrollVertically() || Math.abs(dx) > Math.abs(dy))) {
startScroll = true;
}
//這里是當(dāng)滑動(dòng)的水平距離小于滑動(dòng)的垂直距離時(shí),才攔截啥寇,如果自身又能水平滑動(dòng)的情況偎球,就不判斷兩個(gè)滑動(dòng)距離大小
if ((getLayoutManager().canScrollVertically() && Math.abs(dy) > mTouchSlop)
&& (getLayoutManager().canScrollHorizontally() || Math.abs(dy) > Math.abs(dx))) {
startScroll = true;
}
}
return startScroll && super.onInterceptTouchEvent(e);
}
return super.onInterceptTouchEvent(e);
}
}
我們修改了攔截方法,只有當(dāng)水平滑動(dòng)距離大于豎直滑動(dòng)距離時(shí)辑甜,才讓RecyclerView攔截事件
RecyclerView嵌套R(shí)ecyclerView滑動(dòng)2.gif
這時(shí)衰絮,發(fā)現(xiàn)了另一個(gè)問題,當(dāng)子RecyclerView快速滑動(dòng)磷醋,還沒有停止?jié)L動(dòng)時(shí)猫牡,我們?nèi)ヘQ直滑動(dòng),發(fā)現(xiàn)父RecyclerView沒法接受到Touch事件邓线,我們找到requestDisallowInterceptTouchEvent方法并改寫了它淌友,解決了這個(gè)問題
/**
* 這個(gè)接口是不允許父類打斷這個(gè)onTouch事件,一般被子類調(diào)用getParent.requestDisallowInterceptTouchEvent(true)
* 比如快速水平滑動(dòng)子view的時(shí)候骇陈,這時(shí)我們想去豎直滑動(dòng)父類震庭,因?yàn)榭焖倩瑒?dòng)有一個(gè)速度,子view還沒停止?jié)L動(dòng)你雌,所以子類禁止父類攔截
* 想要解決這個(gè)辦法器联,就把這個(gè)方法改為空方法就行了
*
* @param disallowIntercept
*/
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
RecyclerView嵌套R(shí)ecyclerView滑動(dòng)3.gif