CoordinatorLayout初探
  HvTJUzsxOBtS 2023年11月18日 10 0


CoordinatorLayout是Material Design的一个核心布局, 它能起什么作用呢?

从名字上看, 它是帮我们协调子View的, 根据我们的定制要求, 帮助我们协调各个子view的布局.

CoordinatorLayout

我们先来个最简单的例子.

CoordinatorLayout初探_Dependency

在一个布局最右下角增加一个FloatingActionButton悬浮按钮, 点击这个按钮后弹出一个Snackbar.

普通设置如下:

导包

dependencies {
    ...
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.android.support:design:27.0.2'
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    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.truly.coordinatorlayoutdemo.MainActivity">
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:layout_gravity="bottom|end"
        android:src="@drawable/ic_add_white_24dp"/>
</FrameLayout>

 代码:

private FloatingActionButton fab;
private void initViews() {
    fab = findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Snackbar.make(v,"Add something here",Snackbar.LENGTH_SHORT).show();
        }
    });
}

 我们来看下效果.

CoordinatorLayout初探_ide_02

嗯嗯, Snackbar把悬浮按钮挡住了. 我们做一点小小的改动, 将布局中的FrameLayout改为CoordinatorLayout看看.

<android.support.design.widget.CoordinatorLayout
    ...
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.truly.coordinatorlayoutdemo.MainActivity">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        ...
    />

</android.support.design.widget.CoordinatorLayout>

现在再来看看效果.

CoordinatorLayout初探_android_03

可以看到, Snackbar把悬浮按钮顶起来了.

这就是因为CoordinatorLayout有协调子View的作用. 我们来看一下对它的介绍:

这是一个父控件,继承自ViewGroup,它是加强的FramLayout, 可以协调其它控件并实现控件之间的联动。通过在其直接子View上设置behavior来实现子View的不同交互效果。一般作为一个界面的根布局,来协调AppbarLayout,ToolBarLayout以及ScrollView之间的联动。

那我们没有对FloatingActionButton设置layout_behavior布局行为啊. 这是因为悬浮按钮有一个默认的behavior来检测Snackbar的添加, 并让按钮在Snackbar之上呈现上移与Snackbar等高的动画.

 

Behavior概念

CoordinatorLayout的使用核心是behavior. 在将behavior之前必须先理解两个概念:

1-Child
2-Dependency

Child当然是子View了, 就是CoordinatorLayout的子View, 更准确的来说, Child是指CoordinatorLayout父布局下要执行动作的子View. 也被称为观察者. 而Dependency是指Child依赖的View. 也被称为被观察者.

简单点说, 就是如果Dependency这个View发生了变化, 那么Child这个View就要发生相应变化. 具体变化就是Behavior引入的.

Child发生变化的具体执行代码都是放在Behavior这个类里面. 怎么使用它呢?

  • 首先定义一个类, 继承CoordinatorLayout.Behavior<T>, 其中泛型参数T是我们要执行动作的View类, 也就是Child
  • 实现Behavior的两个方法:
/**
* 判断child的布局是否依赖dependency
*/ 
    @Override
public boolean layoutDependsOn(CoordinatorLayout parent, T child, View dependency) {
    boolean rs; 
    //根据逻辑判断rs的取值 
    //返回false表示child不依赖dependency,ture表示依赖 
    return rs;
} 

/**
* 当dependency发生改变时(位置、宽高等),执行这个函数 
* 返回true表示child的位置或者是宽高要发生改变,否则就返回false 
*/ 
    @Override 
public boolean onDependentViewChanged(CoordinatorLayout parent, T child, View dependency) {
    //child要执行的具体动作
    return true;
}

 

我们来写一个简单例子辅助理解Behavior的概念.

简单例子

CoordinatorLayout初探_Dependency_04

 

左侧是个Button, 内容特意注明是"Dependency", 右侧是个TextView, 也特意注明了"Child".

我们希望在Button上实现OnTouchListener监听, 在移动这个Button时, Textview做相应的移动.

按照上面所讲, 我们需要定义一个类, 继承CoordinatorLayout.Behavior<T>. 这个Behavior很简单, 就是让child这个TextView在Button下面一点一起移动.

public class MyBehavior extends CoordinatorLayout.Behavior<TextView> {

    //Tip: 必须重写带双参的构造器, 因为从xml反射需要调用
    public MyBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

        @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child, View dependency) {
        //根据dependency的位置, 设置TextView的位置
        child.setX(dependency.getX());
        child.setY(dependency.getY()+200);
        return true;
    }

}

布局

 

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    ...>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        .../>

    <Button
        android:id="@+id/btn"
        android:layout_width="120dp"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"
        android:text="Dependency"
        android:textColor="@color/colorAccent"
        android:layout_margin="16dp"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        android:textAllCaps="false"/>

    <TextView
        android:id="@+id/view"
        android:layout_width="120dp"
        android:layout_height="50dp"
        android:layout_margin="16dp"
        android:layout_gravity="right"
        android:text="Child"
        android:textAllCaps="false"
        android:gravity="center"
        android:textColor="@color/colorAccent"
        android:background="@android:color/holo_orange_light"
        app:layout_behavior=".MyBehavior"/>

</android.support.design.widget.CoordinatorLayout>

 

注意, 在TextView属性中有一条:

app:layout_behavior=".MyBehavior"

引用的就是我们刚才自己创建的Behavior.

我们还需要在程序中增加一些代码, 让Button动起来.

先是实现Button移动功能的内部类接口.

class MyOnTouch implements View.OnTouchListener {

    int[] temp = new int[]{0, 0};
    Boolean ismove = false;
    int downX = 0;
    int downY = 0;

        @Override
    public boolean onTouch(View v, MotionEvent event) {
        int eventaction = event.getAction();
        int x = (int) event.getRawX();//event.getRawX获取的是绝对位置
        int y = (int) event.getRawY();

        switch (eventaction) {
            case MotionEvent.ACTION_DOWN:
                temp[0] = (int) event.getX();
                temp[1] = y - v.getTop();
                downX = (int) event.getRawX();
                downY = (int) event.getRawY();
                ismove = false;
                break;
            case MotionEvent.ACTION_MOVE:
                v.layout(x - temp[0], y - temp[1], x + v.getWidth() - temp[0], y - temp[1] + v.getHeight());
                if (Math.abs(downX - x) > 5 || Math.abs(downY - y) > 5)
                ismove = true;
                break;
            case MotionEvent.ACTION_UP:
                if (!ismove)
                    Toast.makeText(MainActivity.this, "你点击了这个按钮", Toast.LENGTH_LONG).show();
                break;
        }
        return false;
    }

}

 

然后在按钮初始化的时候设置onTouchListener监听.

btn = findViewById(R.id.btn);
btn.setOnTouchListener(new MyOnTouch());

现在可以来看下效果了.

CoordinatorLayout初探_ide_05

 

可以看到, CoordinatorLayout确实帮我们协调了2个控件的布局行为layout_behavior.

Google在Android 5.0(Lollipop, API 21)开始, 对某些特定控件内置定义了继承自CoordinatorLayout.Behavior的各种Behavior.

如FloatingActionButton.Behavior, AppBarLayout.Behavior等.

后面我们来用用这些系统Behavior, 达成一些有意思的交互效果. 且看下一章.

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月18日 0

暂无评论

推荐阅读
  anLrwkgbyYZS   2023年12月30日   14   0   0 ideciciMaxideMax
HvTJUzsxOBtS