需求涉及到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高度的计算规则就知道了