場景
某個(gè)界面的頭部HeaderBehavior是動態(tài)生成的,高度不定迈窟,某次在頭部超過整屏的時(shí)候缝其,上滑顯示列表挎塌,再下拉,當(dāng)我再次想要上滑的時(shí)候内边,發(fā)現(xiàn)無法滑動了
原因
- 協(xié)調(diào)者布局會根據(jù)手指觸摸的落點(diǎn)是否在頭部布局的范圍內(nèi)來判斷由HeaderBehavior來處理滑動還是RecycleView或者NestedScrollerView來處理滑動
case MotionEvent.ACTION_DOWN: {
mIsBeingDragged = false;
final int x = (int) ev.getX();
final int y = (int) ev.getY();
if (canDragView(child) && parent.isPointInChildBounds(child, x, y)) {
mLastMotionY = y;
mActivePointerId = ev.getPointerId(0);
ensureVelocityTracker();
}
break;
}
public boolean isPointInChildBounds(View child, int x, int y) {
final Rect r = acquireTempRect();
getDescendantRect(child, r);
try {
return r.contains(x, y);
} finally {
releaseTempRect(r);
}
}
- AppBarLayout對滑動的子View有弱引用(也就是RecycleView或者NestedScrollerView)
- 當(dāng)這個(gè)弱引用存在的時(shí)候判斷子View是否能繼續(xù)滑動榴都,如果能繼續(xù)滑動,則交由子View處理(也就是這里的問題導(dǎo)致我Head過大時(shí)漠其,落點(diǎn)在Head缭贡,導(dǎo)致無法滑動),如果子View不能滑動辉懒,在onTouchEvent事件中把MotionEvent.ACTION_DOWN交給HeaderBehavior自己來處理
@Override
boolean canDragView(AppBarLayout view) {
if (mOnDragCallback != null) {
// If there is a drag callback set, it's in control
return mOnDragCallback.canDrag(view);
}
// Else we'll use the default behaviour of seeing if it can scroll down
if (mLastNestedScrollingChildRef != null) {
// If we have a reference to a scrolling view, check it
final View scrollingView = mLastNestedScrollingChildRef.get();
return scrollingView != null && scrollingView.isShown()
&& !ViewCompat.canScrollVertically(scrollingView, -1);
} else {
// Otherwise we assume that the scrolling view hasn't been scrolled and can drag.
return true;
}
}
解決方案
那么從上述分析中我們可以得出兩點(diǎn)阳惹,如果要滑動,以我鄙見眶俩,有兩個(gè)方案莹汤,想辦法讓Header來處理,或者同時(shí)讓RecycleView來接管不屬于他的事件
- 先看怎么讓Header來處理
從上述部分代碼我們可以看到canDragView在有弱引用的時(shí)候返回為true的條件有scrollingView(此時(shí)為recycleview)不為空颠印,且是顯示狀態(tài)纲岭,且canScrollVertically返回為false
- 那么問題來了,recycleview的canScrollVertically這個(gè)方法什么時(shí)候返回為false
/**
* Query if vertical scrolling is currently supported. The default implementation
* returns false.
*
* @return True if this LayoutManager can scroll the current contents vertically
*/
public boolean canScrollVertically() {
return false;
}
- 默認(rèn)返回false线罕,但是當(dāng)有LayoutManager為豎直方向是返回為true止潮。OK,那么我們只要在自己的recycleview中重新canScrollVertically方法就好了(經(jīng)我測試钞楼,默認(rèn)返回為false可以解決無法滑動的問題喇闸,但是為了更嚴(yán)謹(jǐn)一點(diǎn),盡量還是做一層判斷询件,某些第三方可能將下拉刷新的邏輯引入了canScrollVertically的判斷燃乍,默認(rèn)返回為false可能導(dǎo)致無法下拉刷新)
- 再看看怎么讓RecycleView來處理
- 簡單,在 AppBarLayout的touch監(jiān)聽中直接把事件交給recycleview
appbarlayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
recycleview.dispatchTouchEvent(event);
return false;
}
});
- 經(jīng)我測試宛琅,有效刻蟹,并且不會對header中本身的控件點(diǎn)擊事件行為產(chǎn)生影響
結(jié)語
雖然解決了問題,也提供了兩個(gè)方案嘿辟,但是從代碼來看舆瘪,我的解決方案都是很粗暴的片效,短時(shí)間來看沒有什么毛病,但是真的沒有毛病嗎英古?這是要打一個(gè)問號的