[Android] CoordinatorLayout 基本使用方法
Preface
近些天在做个 Android app demo,有个需求是 默认情况下放大 App 的图标,向上滚动后把它缩小 给下面的内容留出空间。
下面展示的这种效果就是用 CoordinatorLayout
的 Behavior
来做.
基本概念
CoordinatorLayout
,官方文档给出的意思是 “CoordinatorLayout is a super-powered FrameLayout. “,也就是说,直接使用它的话和用 FrameLayout 没有区别。
CoordinatorLayout 与 FrameLayout 的区别就在于:CoordinatorLayout 提供了一个内部类 Behavior
,用它可以在运行时动态的调整各个 View 的布局。
通过覆盖 Behavior
的两个方法:
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency)
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)
来对具体的 View 进行布局.
布局文件
比如布局文件是这样的:
<?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"
android:background="@android:color/background_light">
<!--
所有 CoordinatorLayout 的第一层 子View 都能对其配置 Behavior,比如在当前
这个配置文件里第一层子View就包含:
AppBarLayout, NestedScrollView, TextView, ImageView.
对这些 View 设置属性 "app:layout_behavior" 为 继承自 CoordinatorLayout.Behavior
的类,CoordinatorLayout 就会在适当的时候调用 Behavior 类的相关方法.
-->
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:titleEnabled="false">
<Space
android:layout_width="match_parent"
android:layout_height="166dp" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
app:layout_collapseMode="pin"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:titleTextColor="@color/colorWhite">
<Space
android:id="@+id/spaceAppIconPlaceholder"
android:layout_width="56dp"
android:layout_height="match_parent" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/nestedScrollViewText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:lineSpacingExtra="3sp"
android:text="@string/lorem_ipsum"
android:textColor="@android:color/darker_gray"
android:textSize="18sp" />
</android.support.v4.widget.NestedScrollView>
<TextView
android:id="@+id/textViewAppName"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="3dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="20sp"
android:textStyle="bold"
android:translationZ="6dp"
app:layout_behavior="oing.android.packageviewer.mvc.view.behavior.ToolbarAppNameMoveBehavior"
tools:ignore="RtlHardcoded,UnusedAttribute" />
<!-- 下面将以这个 ImageView 来展示 Behavior 类的用法 -->
<ImageView
android:id="@+id/imageViewAppIcon"
android:layout_width="@dimen/app_icon_size_initial"
android:layout_height="@dimen/app_icon_size_initial"
android:layout_gravity="center_horizontal"
android:contentDescription="@string/content_desc_app_icon"
android:padding="6dp"
android:src="@mipmap/ic_launcher_round"
android:translationZ="6dp"
app:layout_behavior="oing.android.packageviewer.mvc.view.behavior.ToolbarAppIconMoveBehavior"
tools:ignore="UnusedAttribute" />
</android.support.design.widget.CoordinatorLayout>
Behavior
类
接下来具体说下 Behavior
类的两个主要方法的用法.
boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency)
CoordinatorLayot parent
可以用它来findViewById(int)
View child
比如上面配置文件里ImageView
设置了app:layout_behavior
属性,那么child
就是这个ImageView
了.View dependency
child
所依赖的 View
下面来详细的说一下 child
和 dependency
还有 parent
的关系. dependency
和 parent
这两个词都是相对于 child
来讲的:
- 在布局文件中,
child
被parent
包含, 所以child
是parent
的 child child
想改变自己的样子(比如位置和大小)就需要根据其它 View 作为参考,也就是依赖其它的 View,也就是dependency
一词的由来。 如果有依赖多个 View 的话, 请选择依赖得最亲近的那个. 有不依赖dependency
的情况的话,这种情况就忽略dependency
参数就好了。
layoutDependsOn
这个方法的具体用途只有一个: 判断 child
是否依赖 dependency
。每当布局发生变动时,CoordinatorLayout 都会用不同的 View (上面提到的第一层子View)作为 dependency
调用 layoutDependsOn
方法。如果 layoutDependsOn
返回 true
,也就意味着你通过覆盖 layoutDependsOn
来告诉 CoordinatorLayout 这个 child
依赖于 dependency
,那么下一步 CoordinatorLayout 就会去调用 onDependentViewChanged()
.
boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)
参数的含义和上面的 layoutDependsOn
的参数含义是完全一样的,这个方法的目的是用来写具体的修改 View 的代码。你不需要手动的在 layoutDependsOn
里调用这个方法,CoordinatorLayout 会来调用.
Behavior
类示例
package oing.android.packageviewer.mvc.view.behavior;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
public class ToolbarAppIconMoveBehavior extends CoordinatorLayout.Behavior<ImageView> {
// 从布局文件指定到这个类的话, 就使用这样的构造函数.
public ToolbarAppNameMoveBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, ImageView child, View dependency) {
// 用 instanceof 来判断 dependency 是不是 child 想要依赖的 View
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, ImageView child, View dependency) {
// 根据 dependency 对 child 做相应的改动, 同时依赖其他的 View 的话也
// 可以用 parent.findViewById(int) 来查找.
child.setX(dependency.getX());
return true;
}
}