项目
图片实例
解决几个冲突
- 上面RecyclerView的高度扩宽
- 头部和下面ViewPager的同时滑动
- 头部隐藏后,ViewPager中RecyclerView继续滑动
解决步骤
1. 扩宽头部RecyclerView
布局加入这个,自适应高度
android:layout_width="match_parent"
android:layout_height="wrap_content"
2. 解决冲突
public class NestedScrollLayout extends NestedScrollView {
private View topView; //头部的View
private ViewGroup contentView; // ViewPager中的ReyclerView
private static final String TAG = "NestedScrollLayout"; //TAG
public NestedScrollLayout(Context context) {
this(context, null);
init();
}
public NestedScrollLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
init();
}
public NestedScrollLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
init();
}
public NestedScrollLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
init();
}
private FlingHelper mFlingHelper; // veltociy和distance的转换
int totalDy = 0;
/**
* 用于判断RecyclerView是否在fling
*/
boolean isStartFling = false;
/**
* 记录当前滑动的y轴加速度
*/
private int velocityY = 0;
private void init() {
mFlingHelper = new FlingHelper(getContext());
setOnScrollChangeListener(new View.OnScrollChangeListener() { //监听自己(NestedScrollView)滑动
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
if (isStartFling) {
totalDy = 0;
isStartFling = false;
}
if (scrollY == 0) { // 到达顶部的时候
Log.i(TAG, "TOP SCROLL");
// refreshLayout.setEnabled(true);
}
// topView完全消失了,该子View处理了
if (scrollY == (getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) {
Log.i(TAG, "BOTTOM SCROLL");
dispatchChildFling();
}
//在RecyclerView fling情况下,记录当前RecyclerView在y轴的偏移
totalDy += scrollY - oldScrollY;
}
});
}
private void dispatchChildFling() {
if (velocityY != 0) { // 滑动的速度,和distance可以相互转换
// 转换成距离
Double splineFlingDistance = mFlingHelper.getSplineFlingDistance(velocityY);
if (splineFlingDistance > totalDy) {
// 转换成velocityY
childFling(mFlingHelper.getVelocityByDistance(splineFlingDistance - Double.valueOf(totalDy)));
}
}
// 处理完之后,恢复默认值
totalDy = 0;
velocityY = 0;
}
private void childFling(int velY) {
RecyclerView childRecyclerView = getChildRecyclerView(contentView); // 找到子View(RecyclerView)
if (childRecyclerView != null) {
childRecyclerView.fling(0, velY); //fling事件传出去
}
}
@Override
public void fling(int velocityY) { // 自己的fling
super.fling(velocityY);
if (velocityY <= 0) {
this.velocityY = 0;
} else {
isStartFling = true; //开始fling滑动
this.velocityY = velocityY; //滑动的速度
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//顶部的View
topView = ((ViewGroup) getChildAt(0)).getChildAt(0);
//下面的RecyclerView,
contentView = (ViewGroup) ((ViewGroup) getChildAt(0)).getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 调整contentView的高度为父容器高度,使之填充布局,避免父容器滚动后出现空白
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ViewGroup.LayoutParams lp = contentView.getLayoutParams();
lp.height = getMeasuredHeight(); //测量的是整个父View的高度
contentView.setLayoutParams(lp);
}
@Override
public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
Log.i("NestedScrollLayout", getScrollY()+"::onNestedPreScroll::"+topView.getMeasuredHeight());
// 向上滑动。若当前topview可见,需要将topview滑动至不可见
boolean hideTop = dy > 0 && getScrollY() < topView.getMeasuredHeight();
if (hideTop) {
scrollBy(0, dy); //相对滑动了多少位置
consumed[1] = dy; // 消费掉y轴的滑动事件
}
}
private RecyclerView getChildRecyclerView(ViewGroup viewGroup) {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View view = viewGroup.getChildAt(i);
if (view instanceof RecyclerView && view.getClass() == NestedLogRecyclerView.class) {
return (RecyclerView) viewGroup.getChildAt(i);
} else if (viewGroup.getChildAt(i) instanceof ViewGroup) {
ViewGroup childRecyclerView = getChildRecyclerView((ViewGroup) viewGroup.getChildAt(i));
if (childRecyclerView instanceof RecyclerView) {
return (RecyclerView) childRecyclerView;
}
}
continue;
}
return null;
}
}
工具类
FlingHelper
public class FlingHelper {
private static float DECELERATION_RATE = ((float) (Math.log(0.78d) / Math.log(0.9d)));
private static float mFlingFriction = ViewConfiguration.getScrollFriction();
private static float mPhysicalCoeff;
public FlingHelper(Context context) {
mPhysicalCoeff = context.getResources().getDisplayMetrics().density * 160.0f * 386.0878f * 0.84f;
}
private double getSplineDeceleration(int i) {
return Math.log((double) ((0.35f * ((float) Math.abs(i))) / (mFlingFriction * mPhysicalCoeff)));
}
private double getSplineDecelerationByDistance(double d) {
return ((((double) DECELERATION_RATE) - 1.0d) * Math.log(d / ((double) (mFlingFriction * mPhysicalCoeff)))) / ((double) DECELERATION_RATE);
}
public double getSplineFlingDistance(int i) {
return Math.exp(getSplineDeceleration(i) * (((double) DECELERATION_RATE) / (((double) DECELERATION_RATE) - 1.0d))) * ((double) (mFlingFriction * mPhysicalCoeff));
}
public int getVelocityByDistance(double d) {
return Math.abs((int) (((Math.exp(getSplineDecelerationByDistance(d)) * ((double) mFlingFriction)) * ((double) mPhysicalCoeff)) / 0.3499999940395355d));
}
}