SeniorUI31_手写CoordinatorLayout和Behavior

SeniorUI_高级UI汇总目录

一 效果图

在这里插入图片描述

二 需求

  1. 组件: 标题+背景和滑动控件(显示内容)
  2. 当向上滑动时,标题渐渐不透明,图片逐渐变小,直到标题完全显示,图片不显示
  3. 当向下滑动时,显示背景图片并局域变大,标题变不透明,直到不能滑动,图片完全显示,标题完全不显示

三 CoordinatorLayout分析

CoordinatorLayout有两个主要的用法:

  1. 作为顶层应用的装饰或者chrome的布局;
  2. 作为一个能响应特定的一个或多个子视图交互的容器。

Behavior作为一个中间者,目的是协调 子控件之间的触摸事件,

NestedScrolling机制:
内部View在滚动的时候,首先将dx,dy交给NestedScrollingParent,NestedScrollingParent可对其进行部分消耗,剩余的部分还给内部View。

四 需求分析

  1. 自定义CoordinatorLayout处理事件,Behavior传递事件
  2. 标题为ToolBar,图片用ImageView,内容页用一个滑动控件NestedScrollView
  3. 分别定义不同的Behavior,接收事件,作用在ToolBar和ImageView上,实现渐变和缩小效果
public interface NestedScrollingParent {

onStartNestedScroll.当子view的调用NestedScrollingChild的方法startNestedScroll时,会调用该方法.
一定要按照自己的需求返回true,该方法决定了当前控件是否能接收到其内部View(非并非是直接子View)滑动时的参数
;假设你只涉及到纵向滑动,这里可以根据nestedScrollAxes这个参数,
进行纵向判断。
      // 参数child:ViewParent包含触发嵌套滚动的view的对象
      // 参数target:触发嵌套滚动的view  (在这里如果不涉及多层嵌套的话,child和target)是相同的
      // 参数nestedScrollAxes:就是嵌套滚动的滚动方向了
  public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);

onNestedScrollAccepted.如果onStartNestedScroll方法返回的是true的话,那么紧接着就会调用该方法.
它是让嵌套滚动在开始滚动之前,让布局容器(viewGroup)或者它的父类执行一些配置的初始化的.
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);


停止滚动了,当子view调用stopNestedScroll时会调用该方法.
    public void onStopNestedScroll(View target);

当子view调用dispatchNestedPreScroll方法是,会调用该方法.
该方法的会传入内部View移动的dx,dy,如果你需要消耗一定的dx,dy,就通过最后一个参数
consumed进行指定,例如我要消耗一半的dy,就可以写consumed[1]=dy/2
    // 参数target:同上
    // 参数dxConsumed:表示target已经消费的x方向的距离
    // 参数dyConsumed:表示target已经消费的x方向的距离
    // 参数dxUnconsumed:表示x方向剩下的滑动距离
    // 参数dyUnconsumed:表示y方向剩下的滑动距离
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed);

    // 参数dx:表示target本次滚动产生的x方向的滚动总距离
    // 参数dy:表示target本次滚动产生的y方向的滚动总距离
    // 参数consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离.
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);

onNestedFling你可以捕获对内部View的fling事件,如果return true则表示拦截掉内部View的事件

五 主要代码

1 自定义CoordinatorLayout和Behavior

BeaviorCoordinatorLayout

public class BehaviorCoordinatorLayout   extends RelativeLayout  implements ViewTreeObserver.OnGlobalLayoutListener , NestedScrollingParent {
    public float lastX;
    public float lastY;
    public BehaviorCoordinatorLayout(Context context) {
        super(context);
    }

    public BehaviorCoordinatorLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public BehaviorCoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }



    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d("cici", "getHeight-->" + getHeight() + "h-->" + h + "oldf-->" + oldh);
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            LayoutParams param = (LayoutParams) child.getLayoutParams();
            if (param.getBehavior() != null) {
                param.getBehavior().onSizeChanged(this, child, w, h, oldw, oldh);
            }
        }
    }

    /**
     * 设置监听时一定要注意
     * 必须   当前绘制完成onFinishInflate
     * 设置监听
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    //layout_behavior   字符串  --》反射实例化
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(),attrs);
    }
    /**
     * ----------------布局发生变换事件------------------------------
     * @return
     */
    @Override
    public void onGlobalLayout() {
        for(int i=0;i<getChildCount();i++) {
            View child = getChildAt(i);
            //真是类型是   重写了的LayoutParams
            LayoutParams layoutParams= (LayoutParams) child.getLayoutParams();
            if (layoutParams.getBehavior() != null) {
                layoutParams.getBehavior().onLayoutFinish(this,child);
            }
        }
    }

    /**
     * ----------------触摸事件------------------------------
     * @param event
     * @return
     */

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX=event.getRawX();
                lastY=event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                onTouchMove(event);
                break;
        }
        return super.onTouchEvent(event);
    }

    private void onTouchMove(MotionEvent event) {
        float moveX=event.getRawX();
        float moveY=event.getRawY();
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            LayoutParams param = (LayoutParams) child.getLayoutParams();
            if (param.getBehavior() != null) {
                param.getBehavior().onTouchMove(this, child, event, moveX, moveY, lastX, lastY);
            }
        }
        lastY = moveY;
        lastX = moveX;
    }

    /**
     * ----------------滚动事件------------------move------------
     * move 肯定是拿不到
     *  一定返回 true
     * 实现了  NestedScrolling机制 的 滚动控件
     * @return
     */

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return true;
    }


    @Override
    public void onStopNestedScroll(View child) {

    }

    /**
     * 最重要
     // 参数target:同上
     // 参数dxConsumed:表示target已经消费的x方向的距离
     // 参数dyConsumed:表示target已经消费的x方向的距离
     // 参数dxUnconsumed:表示x方向剩下的滑动距离
     // 参数dyUnconsumed:表示y方向剩下的滑动距离
     */

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            LayoutParams param = (LayoutParams) child.getLayoutParams();
            if (param.getBehavior() != null) {
                param.getBehavior().onNestedScroll(target ,child, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
            }
        }
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {

    }
    // 参数dx:表示target本次滚动产生的x方向的滚动总距离
    // 参数dy:表示target本次滚动产生的y方向的滚动总距离
    // 参数consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离.
    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
    }
    //    onNestedFling你可以捕获对内部View的fling事件,如果return true则表示拦截掉内部View的事件
    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {

        return false;
    }


    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        return  false;
    }




    public  static  class LayoutParams extends RelativeLayout.LayoutParams{
        private static final String TAG = "tuch";
        private  Behavior behavior;

        public Behavior getBehavior() {
            return behavior;
        }

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            final TypedArray a = c.obtainStyledAttributes(attrs,
                    R.styleable.BeaviorCoordinatorLayout);
            behavior = parseBehavior(c, attrs, a.getString(
                    R.styleable.BeaviorCoordinatorLayout_layout_behavior));
            Log.i(TAG, "LayoutParams:   名字   "+ a.getString(
                    R.styleable.BeaviorCoordinatorLayout_layout_behavior));
            a.recycle();

        }

        private Behavior parseBehavior(Context context, AttributeSet attrs, String name) {

            if (TextUtils.isEmpty(name)) {
                return null;
            }
            try {
                final Class clazz = Class.forName(name, true,
                        context.getClassLoader());
                Constructor c = clazz.getConstructor(new Class[]{Context.class,AttributeSet.class});
                c.setAccessible(true);
                return (Behavior) c.newInstance(context, attrs);
            } catch (Exception e) {
                throw new RuntimeException("Could not inflate Behavior subclass " + name, e);
            }
        }

        public LayoutParams(int w, int h) {
            super(w, h);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }


    }

Behavior

public class Behavior {
    public Behavior(Context context, AttributeSet set) {

    }
    public void onTouchMove(View parent, View child, MotionEvent event, float x, float y, float oldx, float oldy) {

    }
    /**
     * 布局绘制完成
     * @param parent
     * @param child
     */
    public void onLayoutFinish(View parent, View child) {

    }
    //将所有的事件  类型kaolv齐全
    public void onSizeChanged(View parent, View child, int w, int h, int oldw, int oldh){

    }

    public boolean onTouchEvent(BehaviorCoordinatorLayout parent, View child, MotionEvent ev) {
        return false;
    }
    public boolean onLayoutChild(BehaviorCoordinatorLayout parent, View child, int layoutDirection) {
        return false;
    }
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        Log.d("cici","onStartNestedScroll");
        return true;
    }

    public void onStopNestedScroll(View child) {
        Log.d("cici","onStopNestedScroll");
    }

    public void onNestedScrollAccepted(View child, View target, int axes) {
        Log.d("cici","onNestedScrollAccepted");
    }

    public void onNestedScroll(View scrollView, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
    }

    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
    }

    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        return false;
    }

    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        return false;
    }
}

2 实现Behavior

ImageBehavior

public class ImageBehavior extends Behavior {

    private static final String TAG = "tuch";
    private int maxHeight =100;
    private int originHeight;

    public ImageBehavior(Context context, AttributeSet set) {
        super(context, set);
    }

    @Override
    public void onLayoutFinish(View parent, View child) {
        super.onLayoutFinish(parent, child);
        if (originHeight == 0) {
            originHeight = child.getHeight();
        }
    }

    @Override
    public void onNestedScroll(View scrollView, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        if (scrollView.getScrollY() > 0) {
            ViewGroup.LayoutParams params = target.getLayoutParams();
            params.height = params.height - Math.abs(scrollView.getScrollY());
            if (params.height < maxHeight) {
                params.height = maxHeight;
            }
            target.setLayoutParams(params);
        } else if (scrollView.getScrollY() == 0) {
            ViewGroup.LayoutParams params = target.getLayoutParams();
            params.height = params.height+Math.abs(dyUnconsumed);
            if(params.height >= originHeight){
                params.height = originHeight;
            }

            target.setLayoutParams(params);
        }
    }
}

ToolBarBehavior

public class ImageBehavior extends Behavior {

    private  int maxHeight=500;
    private  int originHeight;
    
    public ImageBehavior(Context context, AttributeSet set) {
        super(context, set);
    }

    @Override
    public void onLayoutFinish(View parent, View child) {
        super.onLayoutFinish(parent, child);
        if (originHeight == 0) {
            originHeight=child.getHeight();
        }
    }

    //滚动方法就  在这里进行缩放
    @Override
    public void onNestedScroll(View scrollView, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        if (scrollView.getScrollY() >0) {
            ViewGroup.LayoutParams parmas=target.getLayoutParams();
            parmas.height = parmas.height - Math.abs(dyConsumed);
            if (parmas.height < originHeight) {
                parmas.height=originHeight;
            }
            target.setLayoutParams(parmas);
        } else if (scrollView.getScrollY()== 0){
            ViewGroup.LayoutParams params = target.getLayoutParams();
            params.height = params.height+ Math.abs(dyUnconsumed);
            if(params.height>= maxHeight){
                params.height =maxHeight;
            }
            target.setLayoutParams(params);
        }
    }
}

3 使用

<com.example.baopengjian.ray_seniorui.thirty_first.design.BehaviorCoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

        <ImageView
            android:id="@+id/img"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:src="@mipmap/main"
            android:scaleType="centerCrop"
            app:layout_behavior="com.example.baopengjian.ray_seniorui.thirty_first.ImageBehavior"
            />
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            app:layout_behavior="com.example.baopengjian.ray_seniorui.thirty_first.ImageBehavior"
            />
        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/img"
            >

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/large_text" />

        </android.support.v4.widget.NestedScrollView>

</com.example.baopengjian.ray_seniorui.thirty_first.design.BehaviorCoordinatorLayout>

六 Demo

CoodrinlayoutActivity

发布了211 篇原创文章 · 获赞 63 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/103672969