安卓复杂滑动案例 自定义behavior源码分析 实现头布局图片的缩放透明度变化,RecycleView的滑动布局,坐标变化
#复杂滑动案例
* 上方图片放大,* 透明
* 输入框,不断增大
* 变色
* 滑动的时候,条目不会改变.
* 再滑的时候,条目滑动
#
* 1,让rv居于头部的下方
* 方案?:
* 1,重写layoutDependsOn 让当前rv去以来头部视图
* 2,获取都以来的头部视图的高度onDependentViewChanged 设置给rv 设置setTranslationY
* 2,让rv跟着头部 向上平移
* 处理头部的向上平移:在onNestedPreScroll中计算移动距离,还没有真正移动
* float newTranslationY = dependency.getTranslationY() - dy;
//计算出最小平移的y的距离
float minTranslationY = -(dependency.getHeight() - finalHeight);
if (newTranslationY > minTranslationY) {
dependency.setTranslationY(newTranslationY);
/**
* 在图片折叠的情况下
* 1不允许RecycleView自身滚动,不能和图片一起平移
* 2只能和图片一起向上移动
*/
//1,
consumed[1] = dy;
//2,
}
3,rv向上平移
onDependentViewChanged
child.setTranslationY(dependency.getHeight()+dependency.getTranslationY());
后面是一个负数,所以用+号
4,向下平移的处理,
* onNestedScroll方法中
* float newTranslation = dependency.getTranslationY() - dyUnconsumed;
//平移的最大距离,坐标0
if (newTranslation <0) {
dependency.setTranslationY(newTranslation);
}
* 5处理图片的缩放和透明都,变化
* onDependentViewChanged中拿到移动百分比,
* 按照百分比,进行图片的缩放,和透明度变化
* 6,手指快读滑动图片的缓慢打开和关闭
* 快速滑动
<?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" > <ImageView android:src="@drawable/header" android:scaleType="centerCrop" android:id="@+id/iv" android:layout_width="match_parent" android:layout_height="300dp" /> <android.support.v7.widget.RecyclerView android:background="#efffffff" app:layout_behavior="@string/HeaderScrollBehavior" android:id="@+id/rv" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.v7.widget.RecyclerView> </android.support.design.widget.CoordinatorLayout>
package com.example.yangg.commplexscrol; import android.content.Context; import android.os.Handler; import android.support.design.widget.CoordinatorLayout; import android.support.v4.view.ViewCompat; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.Scroller; /** * Created by yangg on 2017/7/6. */ public class HeaderScrollBehavior extends CoordinatorLayout.Behavior<RecyclerView> { private View dependency; private final Scroller mSroller; public HeaderScrollBehavior(Context context, AttributeSet attrs) { super(context, attrs); mSroller = new Scroller(context); } /** * 去定当前空间rv 以来的是哪个子试图 */ @Override public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) { if (dependency.getId() == R.id.iv) { this.dependency = dependency; return true; } return false; } /** * 依赖的视图变化 * * @param parent * @param child * @param dependency * @return rv 就是child * 让rv在y轴上平移一段距离:移动图片的高度 * <p> * 1,图片向上折叠 */ @Override public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) { //把图片移动的高度给rv 图片走多少,rv就走多少 child.setTranslationY(dependency.getHeight() + dependency.getTranslationY()); /** * 在这个方法中控制图片的变化 * 计算出移动的百分比,,图片就缩放百分之多少 * */ float persent = Math.abs(dependency.getTranslationY() / (dependency.getHeight() - finalHeight)); dependency.setScaleX(1 + persent); dependency.setScaleY(1 + persent); dependency.setAlpha(1 - persent); return true; } /** * 判断 在嵌套滚动将要开始的时候,一般用于判断滚动方向 顺序1!!! * * @param coordinatorLayout * @param child * @param directTargetChild * @param target * @param nestedScrollAxes * @return */ @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, View directTargetChild, View target, int nestedScrollAxes) { //判断垂直滚动,,滚动之前 告诉系统,垂直滚动 return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; } private float finalHeight = 150; /** * 在滚动之前,告诉系统, 要滚动的距离,dy * 在onStartNestedScroll 之后 顺序2!!! * 在嵌套滚动之前执行,这个方法在在onStartNestedScroll 执行 * dy:y方向上移动的距离,值得是单位时间内手指移动的距离 * * @param coordinatorLayout * @param child * @param target * @param dx * @param dy * @param consumed :十一恶搞数组:消耗的x和y的距离 * 0元素:表示x方向系统小号的距离 * 1元素:表示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); //判断手指向下移动dy<=0 向上移动dy>0 if (dy <= 0) { return; } //计算他移动了多少 float newTranslationY = dependency.getTranslationY() - dy; //计算出最小平移的y的距离 float minTranslationY = -(dependency.getHeight() - finalHeight); if (newTranslationY > minTranslationY) { dependency.setTranslationY(newTranslationY); /** * 在图片折叠的情况下 * 1不允许RecycleView自身滚动,不能和图片一起平移 * 2只能和图片一起向上移动 */ //1, consumed[1] = dy; //2, } } /** * 开始滚动 顺序3!!! * 当rv 消耗掉,y方向上的距离,则dyUnconsumed不等于0 * * @param coordinatorLayout * @param child * @param target * @param dxConsumed * @param dyConsumed * @param dxUnconsumed * @param dyUnconsumed */ @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); //Log.i("test", "dy--->" + dyConsumed); Log.i("test", "dyUnconsumed" + dyUnconsumed); /** * 向下移动dyUnconsumed <0的时候 */ if (dyUnconsumed > 0) { return; } float newTranslation = dependency.getTranslationY() - dyUnconsumed; //平移的最大距离,坐标0 if (newTranslation <= 0) { dependency.setTranslationY(newTranslation); } } /** * Fling 快速滑动,执行, 猛动 * * @param coordinatorLayout * @param child * @param target * @param velocityX * @param velocityY 表示快速滑动松开收瞬间y方向的的速度,向上为+ ,向下喂- * @return */ //定义 boolean值,判断是否自动滚动中,, private boolean isScrolling = false; @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, RecyclerView child, View target, float velocityX, float velocityY) { Log.i("test", "velocityY" + velocityY); if (!isScrolling) { return startExpandOrClose(velocityY); } Log.i("test", "onNestedPreFling"); return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); } /** * 慢慢滚动的时候走这里 * 父布局布局停止滚动的时候,执行一次, * 手指停了之后,又执行一次 * @param coordinatorLayout * @param child * @param target */ @Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, RecyclerView child, View target) { super.onStopNestedScroll(coordinatorLayout, child, target); //800以下都可以,表示速度很慢 startExpandOrClose(0); Log.i("test","onStopNestedScroll"); } //在滚动被子视图接受的时候执行 @Override public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, RecyclerView child, View directTargetChild, View target, int nestedScrollAxes) { super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); //如果我滑动的过程中的时候停止了,但是没有松开收调用onNestedScrollAccepted //继续滑动-->停止-->onNestedScrollAccepted //再执行,-->松开-->onNestedScrollAccepted 最后一次执行这个方法onNestedScrollAccepted //手指松开的一瞬间,我才真正停止,滚动了, //让动画禁止执行 ,,第二次不会被调用,第二次,的时候不影响上个方法,执行 mSroller.abortAnimation(); } private boolean startExpandOrClose(float velocityY) { //获取松开收瞬间,图片已经平移的距离 float translationY = dependency.getTranslationY(); float upFinalTranslationY = -(dependency.getHeight() - finalHeight); float downFinalTranslationY = 0; //dingyi boolean值却动是否闭合的状态fun东 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 targetTranslationY = isClose ? upFinalTranslationY : downFinalTranslationY; int starY = (int) translationY; //变化的 int dy = (int) (targetTranslationY - translationY); mSroller.startScroll(0, starY, 0, dy); handler.post(flingRunable); isScrolling = true; /** * ????????????????????? */ return true; } private Handler handler = new Handler(); private Runnable flingRunable = new Runnable() { @Override public void run() { //Scroller滚滚的用户那里 // 是一阵一帧的向前滚动的,眼不断的计算是否滚动到目标,如果未滚动到,则据需滚动 //判断scroller是否滚动到莫表 //这个方法还可以判断是否有一个新的小目标点 if (mSroller.computeScrollOffset()) { //mSroller.getCurrY() 指获取下一个新的位置 dependency.setTranslationY(mSroller.getCurrY()); handler.post(this); } } }; }