Android Material Design 中的CorrdinatorLayout使用详解

作者: android 发布时间: 2017-07-06 浏览: 4250 次 编辑

转发请备注原文地址:https://www.niwoxuexi.com/blog/android/article/191.html

我在找了一些关于CoordinatorLayout的教程,大部分文章都是讲解把CoordinatorLayout, AppbarLayout ,CollapsingToolbarLayout以及Toolbar 等缓和一起使用,这很容易是大家混淆,不知道如何利用CoordinatorLayout使用到不同的场景中。其实CoordinatorLayout的功能并不是局限于与AppBarLayout一起使用,它的功能非常强大,本文将针对CoordinatorLayout的使用进行详细介绍,在后面再介绍将AppBarLayout和CollapsingToolBarLayout整合CoordinatorLayout一起。那么到底CoordinatorLayout是如何使用的呢,那就听我细细道来吧~

CoordinatorLayout 到底是什么,能干什么?

先看一下官方的描述吧:

CoordinatorLayout is a super-powered FrameLayout.

CoordinatorLayout is intended for two primary use cases:

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

简单的说CoordinatorLayout是一个超级的FrameLayout 布局,它的作用是帮助我们协调它的个个子View的布局,我们先看一个案例动画组:


稍微解释一下这个动画:小面的绿色小球是一个普通的View,上面的飞机是一个ImageView ,我们水平拖动小球,飞机向相反的方向运动;垂直方向拖动小球会跟着相

同的方向运动;

简而言之:水平方向反向运动,垂直方向同向运动;

这个例子就是运用CoordinatorLayout实现的,当然让你不用CoordinatorLayout去实现,应该没有任何问题,但是代码的耦合度应该非常大,你的代码必须要持有

2个View的引用,然后在<code>onTouchEvent里面做各种判断和逻辑处理,这耦合度未免太伤人伤力了~

CoordinatorLayout既然号称能帮我们协调子View的布局,我们接下来看看<code>CoordinatorLayout如何实现~

CoordinatorLayout 的使用

CoordinatorLayout的使用核心是Behavior,Behavior就是执行你定制的动作。

看先Behavior的官方解释吧:

Interaction behavior plugin for child views of CoordinatorLayout.

翻译:Behavior就是用来给CoordinatorLayout的子view们实现交互的。

在讲Behavior之前必须先理解两个概念:Child和Dependency,什么意思呢?Child当然是子View的意思了,是谁的子View呢,当然是CoordinatorLayout的子View;其实Child是指要执行动作的CoordinatorLayout的子View。而Dependency是指Child依赖的View。比如上面的gif图中,绿色的小球的View就是Dependency,上面的小飞机的ImgeView就是Child,上面小飞机的动作是依赖下面的绿色的View。简而言之,就是如过Dependency这个View发生了变化,那么Child这个View就要相应发生变化。发生变化是具体发生什么变化呢?这里就要引入Behavior,Child发生变化的具体执行的代码都是放在Behavior这个类里面。怎么使用Behavior呢,首先,我们定义一个类,继承CoordinatorLayout.Behavior<T>,其中,泛型参数T是我们要执行动作的View类,也就是Child。然后就是去实现

Behavior的两个方法:

/**
 * 判断child的布局是否依赖dependency
 */
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
    //返回false表示child不依赖dependency,ture表示依赖
    return true;
}
/**
 * 当dependency发生改变时(位置、宽高等),执行这个函数
 * 返回true表示child的位置或者是宽高要发生改变,否则就返回false
 */
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
    //根据dependency的位置,设置child的位置
    return true;
}

看了上面的概念后,我们看看具体怎么去实现吧~

为了响应跟随手指移动的操作,我们定义一个非常简单的View,这个View只响应跟随手指移动,将这个View作为Dependency。由于过于简单,这个View源码不粘贴,我们只需知道这个View的类名叫:DependentView。

先看一下:Behavior的实现:

package com.niwoxuexi.coordinatelayout;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
/**
 * Created by zhuxiaocheng on 2017/7/6.
 */
public class MyBehavior extends CoordinatorLayout.Behavior {
    private int screenWidth;
    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        screenWidth = display.widthPixels;
    }
    /**
     * 判断child的布局是否依赖dependency
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        //返回false表示child不依赖dependency,ture表示依赖
        return true;
    }
    /**
     * 当dependency发生改变时(位置、宽高等),执行这个函数
     * 返回true表示child的位置或者是宽高要发生改变,否则就返回false
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        //根据dependency的位置,设置child的位置
        int top = dependency.getTop();
        int left = dependency.getLeft();
        int x = screenWidth - left - child.getWidth();
        int y = top - 800;
        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);
    }
}


再看一下DependentView的实现:

package com.niwoxuexi.coordinatelayout;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
/**
 * Created by zhuxiaocheng on 2017/7/6.
 */
public class MyBehavior extends CoordinatorLayout.Behavior {
    private int screenWidth;
    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        DisplayMetrics display = context.getResources().getDisplayMetrics();
        screenWidth = display.widthPixels;
    }
    /**
     * 判断child的布局是否依赖dependency
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        //返回false表示child不依赖dependency,ture表示依赖
        return true;
    }
    /**
     * 当dependency发生改变时(位置、宽高等),执行这个函数
     * 返回true表示child的位置或者是宽高要发生改变,否则就返回false
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        //根据dependency的位置,设置child的位置
        int top = dependency.getTop();
        int left = dependency.getLeft();
        int x = screenWidth - left - child.getWidth();
        int y = top - 800;
        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);
    }
}

OK,现在我们有了Dependency 的控件DependentView,并且定义好了跟随Dependency一直变化的动作(Behavior)MyBehavior,接下来我们就要指定好为哪个具体的ImageView实例来绑定这些。

方法灰常简单,直接布局文件指定就好:

 app:layout_behavior="com.niwoxuexi.coordinatelayout.MyBehavior"

直接上代码,大家自己体会吧

<?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"
    tools:context="com.niwoxuexi.coordinatelayout.MainActivity">
    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@drawable/plane"
        android:scaleType="fitCenter"
        android:layout_marginLeft="180dp"
        android:layout_marginTop="100dp"
        app:layout_behavior="com.niwoxuexi.coordinatelayout.MyBehavior"
        />
    <com.niwoxuexi.coordinatelayout.DependentView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@drawable/button"
        android:layout_marginLeft="180dp"
        android:layout_marginTop="460dp" />
</android.support.design.widget.CoordinatorLayout>

是不是很简单呢?我们只需关注Behavior的编写就好了,把Child和Dependency之间的在xml文件中设置就可以了~

最后是上代码时间喽:https://image.niwoxuexi.com/blog/downloadCoordinateLayout.zip