热门搜索 :
考研考公
您的当前位置:首页正文

Android自定义Behavior(2)-给你不一样的PgOn

来源:东饰资讯网

效果图

2017-08-23 18_37_05.gif

实现思路

图片滑动效果及LOGO滑动效果均采用自定义Behavior来实现

xml文件

  <android.support.design.widget.CoordinatorLayout 


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  {
            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中复写方法可以参考之前的文章

分解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依赖与被依赖的关系

本项目仅供学习使用,实际项目需要优化一些位置数值

具体细节可加群 :136705426 跟着谷歌混饭吃

Top