Android Material Design之CoordinatorLayout

一、CoordinatorLayout简介

java.lang.Object
   ↳ 	android.view.View
  	   ↳ 	android.view.ViewGroup
  	  	   ↳ 	android.support.design.widget.CoordinatorLayout 

实现了NestedScrollingParent接口,CoordinatorLayout, NestedScrollView, SwipeRefreshLayout都实现了这个接口

CoordinatorLayout is a super-powered FrameLayout. 所以属性和FrameLayout有点像

CoordinatorLayout 控件是design下最重要的一个控件,也是最复杂、功能最强大的,这从他的作用就可以看的出来

二、CoordinatorLayout作用

CoordinatorLayout is intended for two primary use cases:

  • As a top-level application decor or chrome layout
  • As a container for a specific interaction with one or more child views

上面是官方给的解释,CoordinatorLayout的作用就两个:

  • 作为一个最顶层根布局因为CoordinatorLayout本身就是继承ViewGroup
  • 作为一个容器协调子View的行为(通过CoordinatorLayout.Behavior)

所以这里最重要就是这个Behavior了,如果你完全掌握了Behavior,CoordinatorLayout就搞定了

三、CoordinatorLayout.Behavior研究

public static abstract class Behavior<V extends View>

查看CoordinatorLayout的源码我们可以看到这是一个抽象类,里面定义了很多方法,如果需要使用我们要继承CoordinatorLayout.Behavior然后重新一些方法。

我们拿AppBarLayout为例,AppBarLayout中有两个Behavior,一个是拿来给它自己用的,另一个是拿来给它的兄弟结点用的,我们重点关注下AppBarLayout.ScrollingViewBehavior这个类。

我们用到的appbar_scrolling_view_behavior指的也是AppBarLayout.ScrollingViewBehavior这个类

 app:layout_behavior="@string/appbar_scrolling_view_behavior"

通过查看ScrollingViewBehavior也是继承CoordinatorLayout.Behavior

java.lang.Object
   ↳ 	android.support.design.widget.CoordinatorLayout.Behavior<V extends android.view.View>
  	   ↳ 	android.support.design.widget.AppBarLayout.ScrollingViewBehavior 

我们看看CoordinatorLayout.Behavior类中的以下方法

1、layoutDependsOn

 @Override
 public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
       //如果dependency是AppBarLayout的实例,说明它就是我们所需要的Dependency
       return dependency instanceof AppBarLayout;
 }

这个方法告诉CoordinatorLayout,这个view是依赖AppBarLayout的,后续父亲可以利用这个方法,查找到这个child所有依赖的兄弟结点。在CoordinatorLayout.Behavior直接return false;

2、onDependentViewChanged

 //每次dependency位置发生变化,都会执行onDependentViewChanged方法
 @Override
 public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
        View dependency) {
        return false;
 }

这个方法,可以在这个回调中记录dependency的一些位置信息,在onLayoutChild中利用保存下来的信息进行计算,然后得到自身的具体位置。

3、onLayoutChild

 public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
            return false;
        }

这个方法是用来子view用来布局自身使用,如果依赖其他view,那么系统会首先调用

4、onMeasureChild

public boolean onMeasureChild(CoordinatorLayout parent, V child,
                int parentWidthMeasureSpec, int widthUsed,
                int parentHeightMeasureSpec, int heightUsed) {
            return false;
        }

这个是CoordinatorLayout在进行measure的过程中,利用Behavior对象对子view进行大小测量的一个方法。
在这个方法内,我们可以通过parent.getDependencies(child);这个方法,获取到这个child依赖的view,然后通过获取这个child依赖的view的大小来决定自身的大小。

5、NestedScroll

 public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
                V child, View directTargetChild, View target, int nestedScrollAxes) {
            return false;
        }
        
 public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,
                View directTargetChild, View target, int nestedScrollAxes) {
            // Do nothing
        }  
      
 public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
            // Do nothing
        }

 public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,
                int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
            // Do nothing
        }    

这几个方法刚好是NestedScrollingParent的方法(CoodinatorLayout是继承了NestedScrollingParent的),也就是对CoodinatorLayout进行的一个代理(Proxy),即CoordinatorLayout自己不对这些消息进行处理,而是传递给子view的Behavior,进行处理。利用这样的方法,实现了view和view之间的交互和视觉的协同(布局、滑动)。
有兴趣可以看 鸿 洋大神的NestedScrolling机制完全解析:
Android NestedScrolling机制完全解析 带你玩转嵌套滑动

四、自定义Behavior

实现如下红色button,高度保持跟蓝色View一样,x轴方向相反
效果图

构造方法

 public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        width = display.widthPixels;
    }

一定要重写这个构造函数。因为CoordinatorLayout源码中parseBehavior()函数中直接反射调用这个构造函数。

static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
        Context.class,
        AttributeSet.class
};

然后我们实现layoutDependsOn、onDependentViewChanged方法,自定义Behavior就完成了

下面是MyBehavior 的源码

public class MyBehavior extends CoordinatorLayout.Behavior<Button> {

    private int width;

    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        width = display.widthPixels;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, Button child, View dependency) {
        //如果dependency是TempView的实例,说明它就是我们所需要的Dependency
        return dependency instanceof FollowView;
    }

    //每次dependency位置发生变化,都会执行onDependentViewChanged方法
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, Button child, View dependency) {

        //根据dependency的位置,设置Button的位置
        int top = dependency.getTop();
        int left = dependency.getLeft();

        int x = width - left - child.getWidth();
        int y = top;

        setPosition(child, x, y);

        return true;
    }

    private void setPosition(View v, int x, int y) {
        CoordinatorLayout.MarginLayoutParams layoutParams = (CoordinatorLayout.MarginLayoutParams) v.getLayoutParams();
        layoutParams.leftMargin = x;
        layoutParams.topMargin = y;
        v.setLayoutParams(layoutParams);
    }
}

项目源码

https://github.com/Yi520153/DesignCoordinatorLayout

总结

可以看到CoodinatorLayout给我们实现了一个可以被子view代理实现方法的一个布局。这和传统的ViewGroup不同,子view从此知道了彼此之间的存在,一个子view的变化可以通知到另一个子view。CoordinatorLayout所做的事情就是当成一个通信的桥梁,连接不同的view。使用Behavior对象进行通信。

参考资料

CoordinatorLayout的使用如此简单
关于CoordinatorLayout与Behavior的一点分析
CoordinatorLayout布局的使用方式
CoordinatorLayout与滚动的处理
Android Support Design 中 CoordinatorLayout 与 Behaviors 初探

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值