美文网首页android开发技巧Android开发经验谈Android开发
Android自定义Behavior(2)-给你不一样的PgOn

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

作者: 任珉豪 | 来源:发表于2017-08-24 15:00 被阅读293次

    效果图

    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自定义控件+动画(4)-协调布局视差特效

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

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

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

    相关文章

      网友评论

        本文标题:Android自定义Behavior(2)-给你不一样的PgOn

        本文链接:https://www.haomeiwen.com/subject/uwhedxtx.html