需求涉及到CoordinatorLayout\AppBarLayout這套組件,基本邏輯寫完後,發現滑動卡頓。多方查找後得知是所使用的support版本中控件本身問題,高版本中已解決,但實際情況是不可能升級support.design版本的,因此只能嘗試其他方案,最後在github上找到了庫smooth-app-bar-layout,雖然使用比原生複雜,但好歹能實現需求。不過在使用過程中遇到些問題,在此記錄。
1、SmoothAppBarLayout區域無法滑動
這個問題的解決辦法找了好久,最後在issues中找到了https://github.com/henrytao-me/smooth-app-bar-layout/issues/105,就是修改SmoothAppBarLayout的dispatchTouchEvent方法,代碼如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getEdgeFlags() == CUSTOM_EDGE_FLAG) {
return false;
}
boolean dispatched = super.dispatchTouchEvent(ev);
if(ev.getAction() == MotionEvent.ACTION_DOWN) {
downXValue = ev.getX();
downYValue = ev.getY();
}else{
float currentX = ev.getX();
float currentY = ev.getY();
if (dispatched && ev.getAction() == MotionEvent.ACTION_MOVE) {
if (Math.abs(downXValue - currentX) > Math.abs(downYValue - currentY)) {
Log.d("Motion Type :","Horizantal");
}else{
Log.d("Motion Type :","Vertical");
MotionEvent motionEvent = MotionEvent.obtain(ev);
motionEvent.offsetLocation(getLeft(), getTop());
motionEvent.setAction(MotionEvent.ACTION_DOWN);
motionEvent.setEdgeFlags(CUSTOM_EDGE_FLAG);
// getParent() cannot return null, since well - who would have called this method
((ViewGroup) getParent()).dispatchTouchEvent(motionEvent);
return false;
}
}
}
return dispatched;
}
但是在ZUK手機上測試時,奇怪的問題發生了,SmoothAppBarLayout內控件點擊事件無效,調試後發現,實際的ACTION_UP變成了ACTION_MOVE,因此又做了些變動,最終代碼如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// We need to check wether we have arrived from our own triggered motion dispatch,
// There are no really appropriate fields on MotionEvents to store custom data, so we abuse edgeflags
if (ev.getEdgeFlags() == CUSTOM_EDGE_FLAG) {
return false;
}
boolean dispatched = super.dispatchTouchEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
downXValue = ev.getX();
downYValue = ev.getY();
} else {
float currentX = ev.getX();
float currentY = ev.getY();
if (dispatched
&& ev.getAction() == MotionEvent.ACTION_MOVE
&& (Math.abs(downXValue - currentX) > mTouchSlop || Math.abs(downYValue - currentY) > mTouchSlop)) {
if (Math.abs(downXValue - currentX) > Math.abs(downYValue - currentY)) {
Log.d("Motion Type :", "Horizantal");
} else {
Log.d("Motion Type :", "Vertical");
MotionEvent motionEvent = MotionEvent.obtain(ev);
motionEvent.offsetLocation(getLeft(), getTop());
motionEvent.setAction(MotionEvent.ACTION_DOWN);
motionEvent.setEdgeFlags(CUSTOM_EDGE_FLAG);
// getParent() cannot return null, since well - who would have called this method
((ViewGroup) getParent()).dispatchTouchEvent(motionEvent);
return false;
}
}
}
return dispatched;
}
2、ViewPager中有多個列表時,向上滑動其中一個列表,切換到另一個列表時,SmoothAppBarLayout會完全展示。
這個問題沒有什麼好的解決辦法,我只能嘗試讓其他列表跟着滑動,這樣切換過去後不會出現頁面跳動。大致代碼如下:
mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
RecyclerView recyclerView = mOtherFragment.getScrollTarget();
if (recyclerView != null) {
recyclerView.scrollBy(0, verticalOffset - recyclerView.computeVerticalScrollOffset());
}
}
});
3、RecyclerView中footer的高度
這個問題是最複雜也是耗時最長的。如果計算不對,有可能出現推不上去或推上去太多的問題。這個問題的解決視情況而定,這裏簡要介紹下我碰到的需求的實現思路。如圖:
爲了把AppBarLayout推上去,RecyclerView的高度要比實際高度多出AppBarLayout的高度,所以結果就是:
RVHeight + AppBarHeight = FooterHeight + lastVisibleItemBottom
考慮到lastVisibleItemBottom與RecyclerView的滑動距離有關,因此修正後爲
RVHeight + AppBarHeight = FooterHeight + lastVisibleItemBottom + scrollY
這樣Footer高度的計算規則就知道了