效果图
2017-08-23 18_37_05.gif实现思路
图片滑动效果及LOGO滑动效果均采用自定义Behavior来实现
xml文件
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_head"
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="@mipmap/pg_one" />
<LinearLayout
android:background="@drawable/shape_search_bg"
app:layout_behavior="@string/behavior_2_textview"
android:layout_height="50dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_width="wrap_content"
>
<TextView
android:layout_marginRight="10dp"
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="PgOne"
android:textColor="#333"
/>
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rcv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
app:layout_behavior="@string/behavior_2">
</android.support.v7.widget.RecyclerView>
</android.support.design.widget.CoordinatorLayout>
分解RecyclerView的Behavior(behavior_2)
依赖关系:RecyclerView是依赖方,ImageView是被依赖方。
依赖原理:通过对ImageView设置相应移动,让RecyclerView跟随滑动。
效果分析:RecyclerView开始位于ImageView的下方、ImageView滑动到上方会保留一部分高度,且滑动过程中图片的透明度、比例会变化,待ImageView滑动到顶部或者到底部,RecyclerView再去处理滑动事件。
@SuppressWarnings("unused")
public class View2Behavior extends CoordinatorLayout.Behavior<RecyclerView> {
private View dependency;
private final Scroller scroller;
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
//scroller是一帧一帧执行的
if (scroller.computeScrollOffset()) {
dependency.setTranslationY(scroller.getCurrY()); //去移动到下一个位置
handler.post(this);
} else {
isScrolling = false;
}
}
};
public View2Behavior(Context context, AttributeSet attrs) {
super(context, attrs);
scroller = new Scroller(context);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) {
if (dependency.getId() == R.id.iv_head) {
this.dependency = dependency;
return true;
}
return false;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
child.setTranslationY(dependency.getHeight() + dependency.getTranslationY()); // 跟随 被依赖布局一起移动
//百分比
float persent = Math.abs(dependency.getTranslationY() / (dependency.getHeight() - finalHeight));
//头部图片的放大缩小
dependency.setScaleY(persent + 1);
dependency.setScaleX(persent + 1);
dependency.setAlpha((float) (1 - persent + 0.3));
return true;
}
//在嵌套滚到将要开始的时候:用于判断滚动方向
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, View directTargetChild, View target, int nestedScrollAxes) {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
public float finalHeight = 200;
//滚动之前 dy y方向上移动的距离 : 手指的移动距离(单位时间)
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
//手指向下移动
if (dy <= 0) {
} else {
//手指向上移动
float newDy = dependency.getTranslationY() - dy;
float minDy = -(dependency.getHeight() - finalHeight);
if (newDy > minDy) {
dependency.setTranslationY(newDy);
consumed[1] = dy; // 系统去处理
}
}
}
//滚动中
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
float v = dependency.getTranslationY() - dyUnconsumed;
if (dyUnconsumed < 0) {
if (v < 0) {
dependency.setTranslationY(v);
}
}
}
private boolean isScrolling = false;
//快速滑动 velocityY 代表滑动速度
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, RecyclerView child, View target, float velocityX, float velocityY) {
if (!isScrolling) {
return openOrExpand(velocityY);
}
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
private boolean openOrExpand(float velocityY) {
float translationY = dependency.getTranslationY();
float upFinalTranslationY = -(dependency.getHeight() - finalHeight);
float downFinalTranslationY = 0;
boolean isClose = false;
if (Math.abs(velocityY) <= 800) {
//判断位置
if (Math.abs(translationY) < Math.abs(translationY - upFinalTranslationY)) {
isClose = false;
} else {
isClose = true;
}
} else {
if (velocityY > 0) {
//快速向上滑动
isClose = true;
} else {
//快速向下滑动
isClose = false;
}
}
//确定目标点
float targetPosition = isClose ? upFinalTranslationY : downFinalTranslationY;
scroller.startScroll(0, (int) translationY, 0, (int) (targetPosition - translationY));
handler.post(runnable);
isScrolling = true;
return true; //防止 recycleview 被滚动
}
//停止嵌套滚动 通常会执行两次:父布局一个 自身布局一次
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
openOrExpand(0);
}
//当子视图滚动被确认的时候
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, RecyclerView child, View directTargetChild, View target, int nestedScrollAxes) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
scroller.abortAnimation(); // 停止执行
}
}
所涉及到的重要方法
layoutDependsOn
只有被依赖方是ImageView才去执行Behavior事件
onDependentViewChanged
被依赖方处理位置的改变,及一些界面效果(缩放、透明度等)
onStartNestedScroll
用于判断滚动的方向
onNestedPreScroll
滚动之前会调用,这里主要处理被依赖方,向上移动的效果
onNestedScroll
滚动中调用,这里主要处理被依赖方,向下移动的效果
onNestedPreFling
惯性滑动,用于处理快速展开、闭合等界面效果
关于自定义Behavior中复写方法可以参考之前的文章
Android自定义Behavior(1)-给你想要的PgOne
分解LOGO的Behavior(behavior_2_textview)
依赖关系:LinearLayout是依赖方,ImageView是被依赖方。
效果分析:根据ImageView的滑动状态,控制LinearLayout的闭合和展开。
@SuppressWarnings("unused")
public class View2BehaviorTextView extends CoordinatorLayout.Behavior<LinearLayout>{
private final ArgbEvaluator argbEvaluator;
private TransitionSet mSet;
public View2BehaviorTextView(Context context, AttributeSet attrs) {
super(context, attrs);
argbEvaluator = new ArgbEvaluator();
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, LinearLayout child, View dependency) {
if (dependency.getId() == R.id.iv_head) {
return true;
}
return false;
}
private float initOffSet = 480;
private float endOffSet = 5;
private int finalHeight = 200;
private int initMagin = 20;
private int endMagin = 5;
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, LinearLayout child, View dependency) {
float persent = Math.abs(dependency.getTranslationY()/(dependency.getHeight() - finalHeight));
//随着图片移动而移动
float translationY = endOffSet +(initOffSet - endOffSet)*(1-persent);
child.setTranslationY(translationY);
int evaluate = (int)argbEvaluator.evaluate(persent, Color.GRAY, Color.DKGRAY);
int magin = (int) (endMagin + ((initMagin - endMagin) *(1 - persent)));
if (persent == 0) {
reduce(child);
} else if (persent == 1) {
expand(child);
} else {
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
params.setMargins(magin,0,magin,magin);
child.setLayoutParams(params);
}
return true;
}
private void expand(LinearLayout mSearchLayout) {
//设置伸展状态时的布局
CoordinatorLayout.LayoutParams LayoutParams = (CoordinatorLayout.LayoutParams) mSearchLayout.getLayoutParams();
LayoutParams.width = LayoutParams.MATCH_PARENT;
LayoutParams.setMargins(UIUtils.dip2Px(10), UIUtils.dip2Px(5), UIUtils.dip2Px(10), UIUtils.dip2Px(10));
mSearchLayout.setLayoutParams(LayoutParams);
//开始动画
beginDelayedTransition(mSearchLayout);
}
private void reduce(LinearLayout mSearchLayout) {
//设置收缩状态时的布局
CoordinatorLayout.LayoutParams LayoutParams = (CoordinatorLayout.LayoutParams) mSearchLayout.getLayoutParams();
LayoutParams.width = UIUtils.dip2Px(80);
LayoutParams.setMargins(UIUtils.dip2Px(10), UIUtils.dip2Px(10), UIUtils.dip2Px(10), UIUtils.dip2Px(10));
mSearchLayout.setLayoutParams(LayoutParams);
//开始动画
beginDelayedTransition(mSearchLayout);
}
private void beginDelayedTransition(LinearLayout view) {
mSet = new AutoTransition();
mSet.setDuration(300);
TransitionManager.beginDelayedTransition(view, mSet);
}
}
onDependentViewChanged
根据被依赖方移动的位置来改变自身的效果和位置。
总结
学习自定义Behavior中关于几个方法的使用技巧
理解Behavior依赖与被依赖的关系
本项目仅供学习使用,实际项目需要优化一些位置数值
网友评论