版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_35816092/article/details/83027349
摘要:整体思路就是通过CoordinatorLayout来实现头部view和RecyclerView联动,其中重要的一部分是利用了布局的behavior属性来控制头部view折叠效果,先来个gif图效果:
github完整项目地址:https://github.com/Bellriver/HeadFoldingRecyclerView
下面来讲解下部分类的核心代码理解:
Activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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:background="#0f3"
tools:context="com.rivers.headFoldingRecyclerView.ui.MainActivity">
<RelativeLayout
android:id="@+id/card"
android:layout_width="match_parent"
android:layout_height="@dimen/main_topview_max_height"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:minHeight="@dimen/main_topview_min_height"
app:layout_behavior="com.rivers.headFoldingRecyclerView.view.HeadViewBehavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_marginTop="24dp"
android:background="@drawable/card_white_bg"
android:orientation="vertical"
android:paddingStart="18dp">
<!-- 标题 -->
<TextView
android:id="@+id/withdraw_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="36dp"
android:fontFamily="sans-serif"
android:maxWidth="160dp"
android:text="查看删除信息"
android:textColor="#00ae63"
android:textSize="18sp"
android:textStyle="bold"/>
<!-- 权限状态 -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="9dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/withdraw_status_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:text="推荐使用"
android:textColor="#fe4728"
android:textSize="12sp"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/ic_top_card_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="139dp"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="23dp"
android:layout_marginTop="2dp"
android:scaleType="centerInside"
android:src="@drawable/withdraw_area_icon"/>
</RelativeLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:background="@drawable/card_white_bg"
android:scrollbarFadeDuration="500"
android:scrollbars="vertical"
app:layout_behavior="com.rivers.headFoldingRecyclerView.view.RecycleBehavior"/>
</android.support.design.widget.CoordinatorLayout>
理解:根据CoordinatorLayout的属性要求,要求添加Behavior属性的子布局必须是在CorrdinatorLayout的第一级子布局下。
HeadViewBehavior.java:这个类主要是控制headview的缩放效果。
代码1:这部分主要是实现当CoordinatorLayout滑动停止时HeadView自动实现立体层叠式缩放效果。
@Override
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull RelativeLayout child, @NonNull View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target, type);
if (!mOverScroller.isFinished()) {
mOverScroller.forceFinished(true);
}
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
snapToChildIfNeeded(coordinatorLayout, child, params.height, params);
}
private void snapToChildIfNeeded(CoordinatorLayout coordinatorLayout, RelativeLayout child, int finalHeight,
CoordinatorLayout.LayoutParams layoutParams) {
if (!isAbleFold) {
return;
}
if (!mOverScroller.isFinished()) {
mOverScroller.forceFinished(true);
}
if (finalHeight > (maxHeight - minHeight) / 2 + minHeight) {//fold
mOverScroller.startScroll(0, finalHeight, 0, maxHeight - finalHeight, duration);
} else {//unfold
mOverScroller.startScroll(0, finalHeight, 0, minHeight - finalHeight, duration);
}
if (mFlingRunnable == null) {
mFlingRunnable = new FlingRunnable(coordinatorLayout, child, layoutParams);
}
ViewCompat.postOnAnimation(child, mFlingRunnable);
}
private class FlingRunnable implements Runnable {
private final CoordinatorLayout mParent;
private final RelativeLayout mCardView;
CoordinatorLayout.LayoutParams params;
FlingRunnable(CoordinatorLayout parent, RelativeLayout layout, CoordinatorLayout.LayoutParams params) {
mParent = parent;
mCardView = layout;
this.params = params;
}
@Override
public void run() {
if (mCardView != null && mOverScroller != null) {
if (mOverScroller.computeScrollOffset()) {
int abs = Math.abs(mOverScroller.getCurrY());
refresh(mParent,mCardView, params, abs);
ViewCompat.postOnAnimation(mCardView, this);
} else {
// onFlingFinished(mParent, mCardView, params);
}
}
}
}
private void refresh(CoordinatorLayout parent, @NonNull RelativeLayout child, CoordinatorLayout.LayoutParams layoutParams, int finalHeight) {
layoutParams.height = finalHeight;
child.setLayoutParams(layoutParams);
float factor = (float) finalHeight / maxHeight;
float alpha = 0.5f * (1 + factor);
float scale = 0.8f + 0.2f * factor;
child.setAlpha(alpha);
child.setScaleX(scale);
child.setScaleY(scale);
/*顶部右图固定方案*/
RelativeLayout rightImg = parent.findViewById(R.id.ic_top_card_right);
alpha = 0.9f + 0.1f * factor;
rightImg.setAlpha(alpha);
rightImg.setScaleY(scale);
rightImg.setScaleX(scale);
}
代码2:这个方法主要是实现在CoordinatorLayout滚动过程中实现HeadView的收缩变化效果,dy代表CoordinatorLlayout上下滚动的距离,dy<0这是向下滚动,dy>0则是向上滚动。
@Override
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull RelativeLayout child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
if (!isAbleFold) {
return;
}
Log.d(TAG, "onNestedPreScroll");
if (!mOverScroller.isFinished()) {
mOverScroller.forceFinished(true);
}
if (target instanceof RecyclerView) {
RecyclerView recyclerView = (RecyclerView) target;
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstCompletelyVisibleItemPosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
int oldheight = layoutParams.height;
int finalHeight = oldheight - dy;
if (dy > 0 && oldheight >= minHeight /*&& firstCompletelyVisibleItemPosition == 0*/) {//up
finalHeight = finalHeight > minHeight ? finalHeight : minHeight;
refresh(coordinatorLayout, child, layoutParams, finalHeight);
consumed[1] = /*dy*/oldheight - finalHeight;
} else if (dy < 0 && oldheight <= maxHeight && firstCompletelyVisibleItemPosition == 0) {//down
finalHeight = finalHeight > maxHeight ? maxHeight : finalHeight;
refresh(coordinatorLayout, child, layoutParams, finalHeight);
consumed[1] = dy/*oldheight - finalHeight*/;
} else {
// CoordinatorLayout.LayoutParams layoutParams1 = (CoordinatorLayout.LayoutParams) recyclerView.getLayoutParams();
// CoordinatorLayout.Behavior behavior = layoutParams1.getBehavior();
// behavior.onNestedPreScroll(coordinatorLayout, recyclerView, recyclerView, dx, dy, consumed, type);
// refresh(child, layoutParams, finalHeight);
}
}
}
RecycleBehavior.java:这个类是用来控制recyclerview依据headview的缩放高度来联动自己的Y轴的坐标也就是在布局中的高度位置。
代码1:这个方法是根据headView的高度计算RecyclerView的高度。
@Override
public boolean onMeasureChild(CoordinatorLayout parent, RecyclerView child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
Log.d(TAG,"onMeasureChild");
List<View> dependencies = parent.getDependencies(child);
View dependency = findFirstDependency(dependencies);
if (dependency!=null){
int mode = View.MeasureSpec.getMode(parentHeightMeasureSpec);
int size = View.MeasureSpec.getSize(parentHeightMeasureSpec);
CoordinatorLayout.LayoutParams layoutParams= (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
int i=size-minHeight-layoutParams.topMargin+gapMaxHeight;
int measureHeight = View.MeasureSpec.makeMeasureSpec(i, mode);
parent.onMeasureChild(child,parentWidthMeasureSpec,widthUsed,measureHeight,heightUsed);
return true;
}
return false;
}
代码2:这个方法主要是根据HeadView的高度设置RecyclerView的y坐标也就是顶部位置。
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
Log.d(TAG,"onDependentViewChanged");
CoordinatorLayout.LayoutParams params= (CoordinatorLayout.LayoutParams) dependency.getLayoutParams();
float factor=(float)params.height/maxHeight;
int gapHeight = (int) (factor* gapMaxHeight); //中間縫隙高度
int y= (int) (factor*params.height+params.topMargin+gapHeight); //child距离顶部的高度
child.setY(y);
return true;
}