/**
* RecyclerView -ViewPage-RecyclerView 内层嵌套的RV, 解决上下滑动冲突
*/
public class ParentRecyclerViewextends RecyclerView {
int totalDy =0;
/**
* 用于判断RecyclerView是否在fling
*/
boolean isStartFling =false;
private int mMaxDistance;
private FlingHelper mFlingHelper;
/**
* 记录上次Event事件的y坐标
*/
private float lastY;
public int getVelocityY() {
return velocityY;
}
/**
* 记录当前滑动的y轴加速度
*/
private int velocityY;
private AtomicBoolean canScrollVertically;
private ChildRecyclerView mChildRecyclerView;
private int mMeasuredHeight;
private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
private VirtualLayoutManager.LayoutParams mLayoutParams;
public ParentRecyclerView(Context context) {
super(context);
init();
}
public ParentRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ParentRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mFlingHelper =new FlingHelper(getContext());
mMaxDistance = mFlingHelper.getVelocityByDistance(UIUtils.getScreenHeight() *4.0);
canScrollVertically =new AtomicBoolean(true);
addOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView,int newState) {
super.onScrollStateChanged(recyclerView, newState);
//如果父RecyclerView fling过程中已经到底部,需要让子RecyclerView滑动神域的fling
if (parentRecyclerOnScrollListener !=null){
parentRecyclerOnScrollListener.onScrollStateChanged(recyclerView,newState);
}
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
dispatchChildFling();
}
}
@Override
public void onScrolled(RecyclerView recyclerView,int dx,int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isStartFling) {
totalDy =0;
isStartFling =false;
}
//在RecyclerView fling情况下,记录当前RecyclerView在y轴的偏移
totalDy += dy;
if (parentRecyclerOnScrollListener !=null){
parentRecyclerOnScrollListener.onScrolled(recyclerView,dx,dy);
}
}
});
onGlobalLayoutListener =new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int measuredHeight = getMeasuredHeight();
if (measuredHeight != mMeasuredHeight) {
mMeasuredHeight = measuredHeight;
if (mLayoutParams !=null) {
mLayoutParams.height = (int) (mMeasuredHeight - getResources().getDimension(R.dimen.x40));
}
}
}
};
getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
public interface ParentRecyclerOnScrollListener{
void onScrollStateChanged(RecyclerView recyclerView,int newState);
void onScrolled(RecyclerView recyclerView,int dx,int dy);
}
private ParentRecyclerOnScrollListener parentRecyclerOnScrollListener;
public void setParentRecyclerOnScrollListener(ParentRecyclerOnScrollListener parentRecyclerOnScrollListener) {
this.parentRecyclerOnScrollListener = parentRecyclerOnScrollListener;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
private void dispatchChildFling() {
if (isScrollEnd() && velocityY !=0) {
double splineFlingDistance = mFlingHelper.getSplineFlingDistance(velocityY);
if (splineFlingDistance > totalDy) {
childFling(mFlingHelper.getVelocityByDistance(splineFlingDistance - totalDy));
}
}
totalDy =0;
velocityY =0;
}
private void childFling(int velY) {
if (mChildRecyclerView !=null) {
mChildRecyclerView.fling(0,velY);
}
}
public void setChildRecyclerView(ChildRecyclerView childRecyclerView) {
this.mChildRecyclerView = childRecyclerView;
}
public VirtualLayoutManager initLayoutManager() {
return new VirtualLayoutManager(getContext()) {
@Override
public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
try {
return super.scrollVerticallyBy(dy, recycler, state);
}catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@Override
public void onLayoutChildren(Recycler recycler, State state) {
try {
super.onLayoutChildren(recycler, state);
}catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean canScrollVertically() {
return canScrollVertically.get() || mChildRecyclerView ==null || mChildRecyclerView.isScrollTop();
}
@Override
public void addDisappearingView(View child) {
try {
super.addDisappearingView(child);}catch ( Exception e) {
e.printStackTrace();
}
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
mLayoutParams =new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
(int) (mMeasuredHeight - getResources().getDimension(R.dimen.x40)));
return mLayoutParams;
}
@Override
public boolean supportsPredictiveItemAnimations() {
return false;
}
};
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev !=null && ev.getAction() == MotionEvent.ACTION_DOWN) {
//ACTION_DOWN的时候重置加速度
velocityY =0;
stopScroll();
LogUtil.i("ParentRecyclerView","isScrollTop:"+isScrollTop());
if (!isScrollTop()) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
if(!(ev ==null || ev.getAction() == MotionEvent.ACTION_MOVE)) {
//在ACTION_MOVE的情况下,将lastY置为0
lastY =0f;
canScrollVertically.set(!isScrollEnd());
//LogUtil.i("ParentRecyclerView","isScrollEnd:"+isScrollEnd());
}
return super.dispatchTouchEvent(ev);
}
/****
* 滑动距离及坐标 归还父控件焦点
****/
private float xLast, yLast;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
xLast = ev.getX();
yLast = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
final float curX = ev.getX();
final float curY = ev.getY();
final int xDistance = (int) (curX - xLast);
final int yDistance = (int) (curY - yLast);
int orientation = getOrientation(xDistance, yDistance);
if (orientation=='r'||orientation =='l'){
return false;
}
if (orientation =='u' && isScrollEnd()) {
return false;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
private int getOrientation(float dx,float dy) {
if (Math.abs(dx) > Math.abs(dy)) {
//X轴移动
return dx >0 ?'r' :'l';//右,左
}else {
//Y轴移动
return dy >0 ?'d' :'u';//下//上
}
}
@Override
public boolean fling(int velocityX,int velocityY) {
boolean fling =super.fling(velocityX, velocityY);
if (!fling || velocityY <=0) {
this.velocityY =0;
}else {
isStartFling =true;
this.velocityY = velocityY;
}
return fling;
}
private boolean isScrollEnd() {
//RecyclerView.canScrollVertically(1)的值表示是否能向上滚动,false表示已经滚动到底部
return !canScrollVertically(1);
}
public void scrollToTop() {
if (!canScrollVertically.get()) {
canScrollVertically.set(true);
}
if (mChildRecyclerView !=null && !mChildRecyclerView.isScrollTop()) {
mChildRecyclerView.scrollToPosition(0);
postDelayed(() -> {
super.scrollToPosition(0);
},20);
}else {
super.scrollToPosition(0);
}
}
public boolean isScrollTop() {
if (mChildRecyclerView !=null && mChildRecyclerView.isScrollTop() && canScrollVertically.get() && !canScrollVertically(-1)) {
return true;
}
return canScrollVertically.get() &&!canScrollVertically(-1);
}
//----------------------------------------------------------------------------------------------
// NestedScroll. fix:当ChildRecyclerView下滑时(手指未放开),ChildRecyclerView滑动到顶部(非fling),此时ParentRecyclerView不会继续下滑。
//----------------------------------------------------------------------------------------------
@Override
public boolean onStartNestedScroll(View child, View target,int nestedScrollAxes) {
return targetinstanceof ChildRecyclerView;
}
@Override
public void onNestedPreScroll(View target,int dx,int dy,int[] consumed) {
//1.当前Parent RecyclerView没有滑动底,且dy> 0 是下滑
boolean isParentCanScroll = dy >0 && !isScrollEnd();
//2.当前Child RecyclerView滑到顶部了,且dy < 0,即上滑
boolean isChildCanNotScroll = !(dy >=0 || mChildRecyclerView ==null || !mChildRecyclerView.isScrollTop());
//以上两种情况都需要让Parent RecyclerView去scroll,和下面onNestedPreFling机制类似
if(isParentCanScroll || isChildCanNotScroll) {
scrollBy(0,dy);
consumed[1] = dy;
}
}
@Override
public boolean onNestedFling(View target,float velocityX,float velocityY,boolean consumed) {
return true;
}
@Override
public boolean onNestedPreFling(View target,float velocityX,float velocityY) {
boolean isParentCanFling = velocityY >0f && !isScrollEnd();
boolean isChildCanNotFling = !(velocityY >=0 || mChildRecyclerView ==null || !mChildRecyclerView.isScrollTop());
if(!isParentCanFling && !isChildCanNotFling) {
return false;
}
fling(0, (int) velocityY);
return true;
}
// override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
// val childRecyclerView = findNestedScrollingChildRecyclerView()
// if(isScrollEnd().not() || childRecyclerView == null || childRecyclerView.isScrollTop()) {
// return super.dispatchNestedPreFling(velocityX, velocityY)
// }
// childFling(velocityY.toInt())
// return true
// }
//----------------------------------------------------------------------------------------------
// NestedScroll. fix:当ChildRecyclerView下滑时(手指未放开),ChildRecyclerView滑动到顶部(非fling),此时ParentRecyclerView不会继续下滑。
//----------------------------------------------------------------------------------------------
}
/**
* RecyclerView -ViewPage-RecyclerView 内层嵌套的RV, 解决上下滑动冲突
*/
public class ChildRecyclerViewextends RecyclerView {
private FlingHelper mFlingHelper;
private int mVelocityY =0;
private boolean isStartFling =false;
private int totalDy;
private ParentRecyclerView mParentRecyclerView;
public ChildRecyclerView(Context context) {
super(context);
init();
}
public ChildRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ChildRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mFlingHelper =new FlingHelper(getContext());
setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
initScrollListener();
}
private void initScrollListener() {
addOnScrollListener(new OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,int dx,int dy) {
super.onScrolled(recyclerView, dx, dy);
if(isStartFling) {
totalDy =0;
isStartFling =false;
}
totalDy += dy;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView,int newState) {
if(newState == RecyclerView.SCROLL_STATE_IDLE) {
dispatchParentFling();
}
super.onScrollStateChanged(recyclerView, newState);
}
});
}
private void dispatchParentFling() {
if (mParentRecyclerView ==null) {
mParentRecyclerView = findParentRecyclerView();
}
if (mParentRecyclerView !=null) {
LogUtil.i("childRecyclerView","isScrollTop:"+isScrollTop()+",mVelocityY:"+mVelocityY);
if(isScrollTop() && mVelocityY !=0) {
//当前ChildRecyclerView已经滑动到顶部,且竖直方向加速度不为0,如果有多余的需要交由父RecyclerView继续fling
double flingDistance = mFlingHelper.getSplineFlingDistance(mVelocityY);
if(flingDistance > (Math.abs(totalDy))) {
fling(0,-mFlingHelper.getVelocityByDistance(flingDistance + totalDy));
}
//fix 在run方法里面,注意 this@ChildRecyclerView的使用,否则使用的是ParentRecyclerView的变量
totalDy =0;
mVelocityY =0;
}
}
}
private float downX;//按下时 的X坐标
private float downY;//按下时 的Y坐标
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float x = ev.getX();
float y = ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mVelocityY =0;
findParentRecyclerView().setChildRecyclerView(this);
//将按下时的坐标存储
downX = x;
downY = y;
// True if the child does not want the parent to
// intercept touch events.
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
//获取到距离差
float dx = x - downX;
float dy = y - downY;
//通过距离差判断方向
int orientation = getOrientation(dx, dy);
switch (orientation) {
//左右滑动交给ViewPager处理
case 'r':
case 'l':
getParent().requestDisallowInterceptTouchEvent(false);
break;
case 'u':
if (isScrollEnd())
getParent().requestDisallowInterceptTouchEvent(false);
break;
}
break;
}
return super.dispatchTouchEvent(ev);
}
private int getOrientation(float dx,float dy) {
if (Math.abs(dx) > Math.abs(dy)) {
//X轴移动
return dx >0 ?'r' :'l';//右,左
}else {
//Y轴移动
return dy >0 ?'d' :'u';//下//上
}
}
@Override
public boolean fling(int velocityX,int velocityY) {
if(!isAttachedToWindow())return false;
boolean fling =super.fling(velocityX, velocityY);
if(!fling || velocityY >=0) {
//fling为false表示加速度达不到fling的要求,将mVelocityY重置
mVelocityY =0;
}else {
//正在进行fling
isStartFling =true;
mVelocityY = velocityY;
}
return fling;
}
public boolean isScrollTop() {
//RecyclerView.canScrollVertically(-1)的值表示是否能向下滚动,false表示已经滚动到顶部
return !canScrollVertically(-1);
}
public boolean isScrollEnd() {
//RecyclerView.canScrollVertically(1)的值表示是否能向上滚动,false表示已经滚动到底部
return !canScrollVertically(1);
}
private ParentRecyclerView findParentRecyclerView() {
ViewParent parentView = getParent();
while (!(parentViewinstanceof ParentRecyclerView)) {
parentView = parentView.getParent();
}
return (ParentRecyclerView) parentView;
}
}