下面就用思路二来分析一下如何实现php
public class NestedRecyclerView extends RecyclerView {
private float topPadding = Utils.dp2Px(50);
private float scrollSlop = Utils.dp2Px(50); //滑动超过这个距离松手后自动自动滑动到顶部
private float downY;
private float moveY;
private float deltaMoveY;
private float deltaDownMoveY;
private float lastMoveY;
public OverScroller mScroller;
private int firstCompletelyVisiblePosition;
public NestedRecyclerView(@NonNull Context context) {
this(context, null);
}
public NestedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mScroller = new OverScroller(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d("NestedRecyclerView", "ACTION_DOWN");
downY = event.getRawY();
lastMoveY = event.getRawY();
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d("NestedRecyclerView", "ACTION_DOWN");
downY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
moveY = event.getRawY();
deltaMoveY = moveY - lastMoveY;
deltaDownMoveY = moveY - downY;
//向上滑动 手指滑动到 recyclerview 顶部或者以上 触发嵌套滑动
//event.getY() <= 0 且 getTop() - ((View) getParent()).getScrollY() > topPadding
if (deltaMoveY < 0) {
if (event.getY() <= 0) {
if (getTop() - ((View) getParent()).getScrollY() > topPadding) {
setNestedScrollingEnabled(true);
startNestedScroll(SCROLL_AXIS_VERTICAL);
} else {
setNestedScrollingEnabled(false);
}
} else {
setNestedScrollingEnabled(false);
}
} else {
//向下滑动
//先向下滑动 recyclerview,等待 recyclerview 所有展示后触发嵌套滑动
GridLayoutManager layoutManager = ((GridLayoutManager) getLayoutManager());
firstCompletelyVisiblePosition = layoutManager.findFirstCompletelyVisibleItemPosition();
if (firstCompletelyVisiblePosition == 0 && ((View) getParent()).getScrollY() > 0) {
setNestedScrollingEnabled(true);
startNestedScroll(SCROLL_AXIS_VERTICAL);
} else {
setNestedScrollingEnabled(false);
}
}
lastMoveY = moveY;
break;
case MotionEvent.ACTION_UP:
int parentScrollY = ((View) getParent()).getScrollY();
int startY = parentScrollY;
int dy;
if (deltaDownMoveY < 0) {
//向上滑动切滑动距离超过 scrollSlop 滑动到顶部
//向上滑动切滑动距离不超过 scrollSlop 滑回到底部
if (parentScrollY >= scrollSlop) {
dy = (int) (getTop() - ((View) getParent()).getScrollY() - topPadding);
} else {
dy = -parentScrollY;
}
} else {
//向下滑动切滑动距离超过 scrollSlop 滑动到底部
//向下滑动切滑动距离不超过 scrollSlop 滑回到顶部
if (getTop() - ((View) getParent()).getScrollY() > scrollSlop + topPadding) {
dy = -parentScrollY;
} else {
dy = (int) (getTop() - ((View) getParent()).getScrollY() - topPadding);
}
}
mScroller.startScroll(0, startY, 0
, dy, 300);
((View) getParent()).invalidate();
break;
}
return super.onTouchEvent(event);
}
public OverScroller getScroller() {
return mScroller;
}
}
复制代码
因为RecyclerView默认实现了NestedScrollingChild2,不须要咱们本身实现NestedScrollingChild。java
重写RecyclerView的onTouchEvent方法,在ACTION_MOVE里面合适的时候开启/禁止弹性滑动,具体逻辑参考前面流程图和代码注释。android
在ACTION_UP的时候经过 Scroller 实现弹性滑动的效果,关键是计算出 dy,代码注释里面写的很清楚。app
关于 Scroller 的使用参考这里ide
public class LinearLayoutNestScrollParent extends LinearLayout implements NestedScrollingParent {
private NestedScrollingParentHelper mParentHelper;
private View imgView;
private NestedRecyclerView recyclerview;
public LinearLayoutNestScrollParent(Context context) {
this(context, null);
}
public LinearLayoutNestScrollParent(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mParentHelper = new NestedScrollingParentHelper(this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
imgView = findViewById(R.id.img);
recyclerview = findViewById(R.id.rv_photos);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int imgHeight = imgView.getMeasuredHeight();
//从新测量高度
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() + imgHeight, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, newHeightMeasureSpec);
}
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
if (target instanceof RecyclerView) {
return true;
}
return false;
}
@Override
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
}
@Override
public void onStopNestedScroll(View target) {
mParentHelper.onStopNestedScroll(target);
}
//先于 child 滚动
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
scrollBy(0, dy);//滚动
consumed[1] = dy;//告诉child我消费了多少
}
//后于child滚动
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
}
//返回值:是否消费了fling
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return false;
}
//返回值:是否消费了fling
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
return false;
}
@Override
public int getNestedScrollAxes() {
return mParentHelper.getNestedScrollAxes();
}
@Override
public void computeScroll() {
OverScroller scroller = recyclerview.getScroller();
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurrY());
invalidate();
}
}
}
复制代码
实现NestedScrollingParent接口,只要实现onNestedScroll这个方法,所有消费 NestedScrollingChild传来的 dy 。布局
另外须要注意的onMeasure方法,从新测量高度,不然上滑时候RecyclerView下面部分会出现空白。this
<?xml version="1.0" encoding="utf-8"?>
<cn.feng.xhsimageview.views.LinearLayoutNestScrollParent xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<ImageView android:id="@+id/img" android:layout_width="match_parent" android:layout_height="wrap_content"/>
<cn.feng.xhsimageview.views.NestedRecyclerView android:id="@+id/rv_photos" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="2dp" android:layout_weight="1" />
</cn.feng.xhsimageview.views.LinearLayoutNestScrollParent>
复制代码