github地址:
(https://github.com/liyuzero/NestedRecyclerView)
AppBarLayout 实现
优:使用简单
劣:1、动画无法衔接,会有停顿 2、上半部分视图会一次加载完毕,会造成两个影响,(1)无法走recyclerView的缓存机制,(2)会使该部分视图的曝光逻辑复杂化
RecyclerView嵌套ViewPager嵌套RecyclerView实现
1. 重写外层RecyclerView的dispatchTouch事件分发,以及子RecyclerView的touch事件分发,处理滑动冲突
2. 依照nestedScroll机制,重写内外层RecyclerView的触摸事件
优:可以无停滞滑动,内外层recyclerView可以进行正常的回收、绘制
劣:需要重写内外层RecyclerView,使用不太方便
3. 【本框架采用】只重写外层RecyclerView的事件分发,屏蔽RecyclerView本身的滑动逻辑,自行编写Scroller实现所有滑动逻辑
优:可以无停滞滑动,内外层recyclerView可以进行正常的回收、绘制,只有最外层RecyclerView需要使用特定类
劣:因为是外层RecyclerView实现所有触摸事件分发,所以横滑效果不能完全媲美原版,但是有办法弥补
具体原理可分为三部分,1、将触摸事件组(按下到抬起)进行分类处理,并屏蔽RecyclerView自身的滑动, 2、在用户手指抬起时触发fling滑动,3、编写Scroller整合内外RecyclerView滑动,使其有连续性
SCROLL_HOR
,二是竖直滑动 SCROLL_VER
,屏蔽RecyclerView自身滑动部分简略概括为:onInterceptTouchEvent
,依据事件组类型进行拦截,onTouchEvent
直接返回true if (mCurScrollState == SCROLL_VER) {
//竖直滑动时,对Recycler内容进行滑动,该方法处理了内外RecyclerView滑动切换逻辑
scrollVer(ev, offsetY);
return true;
} else if (mCurScrollState == SCROLL_HOR) {
// 水平滑动时,外层RecyclerView 不拦截触摸事件,该事件将会传给child
return false;
} else if (mCurScrollState == SCROLL_NONE) {
//触摸事件组分类
float dx = Math.abs(curX - mDownX);
float dy = Math.abs(curY - mDownY);
if (dx <= 0.01f && dy < 0.01f) {
return false;
} else {
if (Math.abs(dx) >= mViewConfiguration.getScaledTouchSlop()) {
mCurScrollState = SCROLL_HOR;
}
if (Math.abs(dy) > mViewConfiguration.getScaledTouchSlop()) {
mCurScrollState = SCROLL_VER;
scrollVer(ev, offsetY);
return true;
}
if (mCurScrollState == SCROLL_HOR) {
return false;
}
}
}
return false;
fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)
方法,此处传入用户手指抬起时的操作速度,则Scroller会自动给出动画偏移量,具体如下 void handleFlingEvent() {
if (mMinYV == 0) {
mMinYV = mViewConfiguration.getScaledMinimumFlingVelocity();
}
if (mTracker == null) {
mTracker = VelocityTracker.obtain();
}
mTracker.computeCurrentVelocity(1000);
int initialVelocity = (int) mTracker.getYVelocity();
if (Math.abs(initialVelocity) > mMinYV) {
// 由于坐标轴正方向问题,要加负号。
doFling((int) (-initialVelocity * 0.75f));
}
}
void doFling(int speed) {
// scroller 执行fling动画
mPreScrollY = 0;
mScroller.fling(0, mPreScrollY, 0, speed, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
post(this);
}
//
@Override
public void run() {
//计算当前动画偏移量,执行滑动操作
boolean finished = !mScroller.computeScrollOffset() || mScroller.isFinished();
if (!finished) {
int offsetY = mScroller.getCurrY() - mPreScrollY;
mPreScrollY = mScroller.getCurrY();
scrollVer(null, -offsetY);
post(this);
} else {
removeCallbacks(this);
}
}
private void scrollVer(MotionEvent ev, float offsetY) {
if(mIsScrollUp) {
return;
}
if (!(offsetY > 0 && !canScrollVertically(-1))) {
if (ev != null) {
//避免特殊情况下子View触摸生效或不连续
MotionEvent event = MotionEvent.obtain(ev);
event.setAction(MotionEvent.ACTION_CANCEL);
try {
super.dispatchTouchEvent(event);
} catch (Exception e) {
//
}
}
//滑动内容
scrollContent(offsetY);
}
}
private void scrollContent(float offsetY) {
int scrollY = (int) offsetY;
if (!canScrollVertically(1)) {
if (mChildRecyclerViewHelper != null) {
RecyclerView recyclerView = mChildRecyclerViewHelper.getCurRecyclerView();
if (recyclerView != null) {
//关闭嵌套滚动机制,避免与下拉刷新等Nested嵌套框架出现冲突
recyclerView.setNestedScrollingEnabled(false);
}
if (recyclerView != null && recyclerView.getTag(R.id.nested_recycler_view_inner_recycler_listener) == null) {
recyclerView.addOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (mOnScrollListener != null) {
mOnScrollListener.onScrollStateChanged(recyclerView, newState);
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (mOnScrollListener != null) {
mOnScrollListener.onScrolled(recyclerView, dx, dy);
}
}
});
recyclerView.setTag(R.id.nested_recycler_view_inner_recycler_listener, new Object());
}
if (recyclerView != null && recyclerView.getMeasuredHeight() != 0) {
//tab内的RecyclerView在顶部
try {
if (!recyclerView.canScrollVertically(-1)) {
if (offsetY > 0) {
scrollContentView(scrollY);
} else {
recyclerView.scrollBy(0, -scrollY);
}
} else if (!recyclerView.canScrollVertically(1)) {
mScrollerManager.abortAnimation();
recyclerView.scrollBy(0, -scrollY);
} else {
//滑动到底部,此时需要滑动tab内的recyclerView
recyclerView.scrollBy(0, -scrollY);
}
} catch (Exception e) {
//规避以下错误【底部信息流的view在被detached之后引起,偶先,理论上去掉信息流部分的回收机制也行】:java.lang.NullPointerException: Attempt to read from field 'java.util.ArrayList
// android.support.v7.widget.StaggeredGridLayoutManager$Span.mViews' on a null object reference
}
} else {
scrollContentView(scrollY);
}
} else {
scrollContentView(scrollY);
}
} else {
scrollContentView(scrollY);
}
}
链接:折叠RecyclerView 库地址,该框架支持无限层次,并可以复用:
(https://github.com/liyuzero/NestedRecyclerView)