到目前为止,我们已经在很多教程中使用了Android协调员布局。然而,我们还没有深入到它的细节。在本教程中,我们将讨论并定制Android应用程序中的协调员布局。
Android# CoordinatorLayout
Android CoordinatorLayout是一个超级强大的[FrameLayout](/community/tutorials/android-framelayout-absolutelayout-example-tutorial)。它能提供的比看起来要多得多。它对子视图有额外的控制级别。它协调子视图的动画和过渡。让我们创建一个新的Android Studio项目,并选择默认具有)组成。点击它会显示一个[SnackBar](/community/tutorials/android-snackbar-example-tutorial),如下所示。您是否注意到,浮动操作按钮会向上移动,为SnackBar让路,并在SnackBar消失时返回?这不是魔法这是CoordinatorLayout中浮动操作按钮的行为方式。备注 :CoordinatorLayout还可以扩展工具栏以显示更多内容,或者在滚动时将其折叠,这在您滚动WhatsApp用户的个人资料屏幕时很常见。别担心,我们将在后面的教程中研究这个问题。一个问题会突然出现在我们的脑海中--CoordinatorLayout如何知道如何处理子视图?答案就在下一节。
协调员布局行为
协调员布局中的FAB已被指定为默认的行为 ,当另一个视图与其交互时,它会相应地产生动画效果。在布局/活动中按Ctrl/CMD+单击FloatingActionButton,您将看到已在类上定义了一个带有注释的** 行为** 。它应该如下所示:
1@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
FloatingActionButton.Behavior 是FAB上使用的默认** 行为** 类。我们可以通过扩展类** OrganatorLayout.Behavior** 来定义我们自己的** 行为** 。这里的T是我们希望定义其** 行为** 的类。在上面的例子中,它是** 协调员布局.Behavior** 。
行为 仅作用于协调员Layout的直接子对象。
- 协调员布局有必要成为活动的根布局
现在让我们在屏幕底部添加一个Button小部件。activity_main.xml
1<?xml version="1.0" encoding="utf-8"?>
2<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
3 xmlns:app="https://schemas.android.com/apk/res-auto"
4 xmlns:tools="https://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:fitsSystemWindows="true"
8 tools:context="com.journaldev.coordinatorlayoutbehaviours.MainActivity">
9
10 <android.support.design.widget.AppBarLayout
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:theme="@style/AppTheme.AppBarOverlay">
14
15 <android.support.v7.widget.Toolbar
16 android:id="@+id/toolbar"
17 android:layout_width="match_parent"
18 android:layout_height="?attr/actionBarSize"
19 android:background="?attr/colorPrimary"
20 app:popupTheme="@style/AppTheme.PopupOverlay" />
21
22 </android.support.design.widget.AppBarLayout>
23
24 <include layout="@layout/content_main" />
25
26 <!--<android.support.design.widget.FloatingActionButton
27 android:id="@+id/fab"
28 android:layout_width="wrap_content"
29 android:layout_height="wrap_content"
30 android:layout_gravity="bottom|end"
31 android:layout_margin="@dimen/fab_margin"
32 android:src="@android:drawable/ic_dialog_email" />-->
33
34 <android.support.v7.widget.AppCompatButton
35 android:layout_height="wrap_content"
36 android:layout_gravity="bottom|start"
37 android:text="CLICK ME"
38 android:id="@+id/button"
39 android:layout_margin="@dimen/fab_margin"
40 android:layout_width="match_parent"/>
41
42</android.support.design.widget.CoordinatorLayout>
我们已经从上面的布局中注释掉了FAB。现在将MainActivity.Java 中的FloatingActionButton监听器替换为AppCompatButton,如下所示。
1AppCompatButton fab = (AppCompatButton) findViewById(R.id.button);
2 fab.setOnClickListener(new View.OnClickListener() {
3 @Override
4 public void onClick(View view) {
5 Snackbar.make(view, "Hey Button. Define a Custom Behaviour. Else I'll take your space", Snackbar.LENGTH_LONG)
6 .setAction("Action", null).show();
7 }
8 });
这就是应用程序现在的样子。有什么猜测吗?要定义自定义行为,我们需要注意两个重要元素:
child :执行* 行为 ** 的视图。 dependency :视图将触发子对象的* 行为 **
在上面的例子中,AppCompatButton是子对象,Snackbar是依赖项。注意 :对于FloatingActionButton的默认** 行为** ,依赖关系不仅仅是Snackbar。还有其他在FloatingActionButton上触发** 行为** 的View元素。让我们首先创建我们自己的自定义** Behavior** 类,它向上移动AppCompatButton。我们将其命名为** CustomMoveUpBehavior.Java** 。
1public class CustomMoveUpBehavior extends CoordinatorLayout.Behavior {
2
3 public CustomMoveUpBehavior(Context context, AttributeSet attrs) {
4 super(context, attrs);
5 }
6
7}
在上述类中需要重写的两个必备方法是layoutDependsOn 和** onDependentViewChanged** 。让我们在我们的类中添加覆盖它们。
1package com.journaldev.coordinatorlayoutbehaviours;
2
3import android.os.Build;
4import android.support.design.widget.CoordinatorLayout;
5import android.support.design.widget.Snackbar;
6import android.view.View;
7
8public class CustomMoveUpBehavior extends CoordinatorLayout.Behavior {
9
10 @Override
11 public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
12 return dependency instanceof Snackbar.SnackbarLayout;
13 }
14
15 @Override
16 public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
17 float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
18 child.setTranslationY(translationY);
19 return true;
20 }
21
22}
layoutDependsOn 检查触发行为的依赖项是否是实例Snackbar。** onDependentViewChanged** 用于基于基本的数学计算上移子视图(AppCompatButton)。
将该行为附加到Android协调员Layout
为了附加CustomMoveUpBehavior.java ,我们将创建一个Custom AppCompatButton并添加注释,如下所示。
1package com.journaldev.coordinatorlayoutbehaviours;
2
3import android.content.Context;
4import android.support.design.widget.CoordinatorLayout;
5import android.support.v7.widget.AppCompatButton;
6import android.util.AttributeSet;
7
8@CoordinatorLayout.DefaultBehavior(CustomMoveUpBehavior.class)
9public class CustomButton extends AppCompatButton {
10 public CustomButton(Context context) {
11 super(context);
12 }
13
14 public CustomButton(Context context, AttributeSet attrs) {
15 super(context, attrs);
16 }
17
18 public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
19 super(context, attrs, defStyleAttr);
20 }
21}
在 activity_main.xml 和** MainActivity.java** 中执行以下更改:将AppCompatButton
xml标记替换为以下标记。
1<com.journaldev.coordinatorlayoutbehaviours.CustomButton
2 android:layout_height="wrap_content"
3 android:layout_gravity="bottom|start"
4 android:text="CLICK ME"
5 android:id="@+id/button"
6 android:layout_margin="@dimen/fab_margin"
7 android:layout_width="match_parent"/>
替换MainActivity.Java中各自的ButtononClickListener
。
1CustomButton fab = (CustomButton) findViewById(R.id.button);
2 fab.setOnClickListener(new View.OnClickListener() {
3 @Override
4 public void onClick(View view) {
5 Snackbar.make(view, "Hey Button. Define a Custom Behaviour. Else I'll take your space", Snackbar.LENGTH_LONG)
6 .setAction("Action", null).show();
7 }
8 });
现在运行的应用程序应该是这样的:不是很酷吗?现在,让我们尝试实现FAB的自定义行为。当Snackbar显示时,我们将触发它旋转和向上移动。我们实现了一个CustomRotateBehavior.Java类。下面给出了它。
1public class CustomRotateBehavior extends CoordinatorLayout.Behavior {
2
3 public CustomRotateBehavior(Context context, AttributeSet attrs) {
4 super(context, attrs);
5 }
6
7@Override
8 public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
9 return dependency instanceof Snackbar.SnackbarLayout;
10 }
11
12 @Override
13 public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
14 float translationY = getFabTranslationYForSnackbar(parent, child);
15 float percentComplete = -translationY / dependency.getHeight();
16 child.setRotation(180 * percentComplete);
17 child.setTranslationY(translationY);
18 return false;
19 }
20
21 private float getFabTranslationYForSnackbar(CoordinatorLayout parent,
22 FloatingActionButton fab) {
23 float minOffset = 0;
24 final List dependencies = parent.getDependencies(fab);
25 for (int i = 0, z = dependencies.size(); i < z; i++) {
26 final View view = dependencies.get(i);
27 if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
28 minOffset = Math.min(minOffset,
29 ViewCompat.getTranslationY(view) - view.getHeight());
30 }
31 }
32
33 return minOffset;
34 }
35
36}
方法`getFabTranslationYForSnackbar(父项,子项)‘计算Snackbar应该出现在屏幕上多远的位置,以便Fab开始更改。在进行相关更改之前,让我们先浏览一下我们的项目结构。
Android协调员布局示例项目结构
Android CoordinatorLayout示例代码
现在,我们只需在FloatingActionButton视图中定义app:Layout_Behavior 并将其指向我们的子类,而不是扩展FloatingActionButton。这就是我们的** active_main.xml** 现在的样子。
1<?xml version="1.0" encoding="utf-8"?>
2<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
3 xmlns:app="https://schemas.android.com/apk/res-auto"
4 xmlns:tools="https://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent"
7 android:fitsSystemWindows="true"
8 tools:context="com.journaldev.coordinatorlayoutbehaviours.MainActivity">
9
10 <android.support.design.widget.AppBarLayout
11 android:layout_width="match_parent"
12 android:layout_height="wrap_content"
13 android:theme="@style/AppTheme.AppBarOverlay">
14
15 <android.support.v7.widget.Toolbar
16 android:id="@+id/toolbar"
17 android:layout_width="match_parent"
18 android:layout_height="?attr/actionBarSize"
19 android:background="?attr/colorPrimary"
20 app:popupTheme="@style/AppTheme.PopupOverlay" />
21
22 </android.support.design.widget.AppBarLayout>
23
24 <include layout="@layout/content_main" />
25
26 <android.support.design.widget.FloatingActionButton
27 android:id="@+id/fab"
28 android:layout_width="wrap_content"
29 android:layout_height="wrap_content"
30 android:layout_gravity="bottom|end"
31 android:layout_margin="@dimen/fab_margin"
32 app:layout_behavior="com.journaldev.coordinatorlayoutbehaviours.CustomRotateBehavior"
33 android:src="@android:drawable/arrow_down_float" />
34
35 <!--<com.journaldev.coordinatorlayoutbehaviours.CustomButton
36 android:layout_height="wrap_content"
37 android:layout_gravity="bottom|start"
38 android:text="CLICK ME"
39 android:id="@+id/button"
40 android:layout_margin="@dimen/fab_margin"
41 android:layout_width="match_parent"/>-->
42
43</android.support.design.widget.CoordinatorLayout>
MainActivity.java 现在如下所示。
1package com.journaldev.coordinatorlayoutbehaviours;
2
3import android.os.Bundle;
4import android.support.design.widget.FloatingActionButton;
5import android.support.design.widget.Snackbar;
6import android.support.v7.app.AppCompatActivity;
7import android.support.v7.widget.AppCompatButton;
8import android.support.v7.widget.Toolbar;
9import android.view.View;
10import android.view.Menu;
11import android.view.MenuItem;
12
13public class MainActivity extends AppCompatActivity {
14
15 @Override
16 protected void onCreate(Bundle savedInstanceState) {
17 super.onCreate(savedInstanceState);
18 setContentView(R.layout.activity_main);
19 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
20 setSupportActionBar(toolbar);
21
22 /*CustomButton fab = (CustomButton) findViewById(R.id.button);
23 fab.setOnClickListener(new View.OnClickListener() {
24 @Override
25 public void onClick(View view) {
26 Snackbar.make(view, "You've added the CustomMoveUpBehavior. Now I'll let you move", Snackbar.LENGTH_LONG)
27 .setAction("Action", null).show();
28 }
29 });*/
30
31 FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
32 fab.setOnClickListener(new View.OnClickListener() {
33 @Override
34 public void onClick(View view) {
35 Snackbar.make(view, "Hey FAB. Please Rotate 180 degrees when I'm up.", Snackbar.LENGTH_LONG)
36 .setAction("Action", null).show();
37 }
38 });
39 }
40
41 @Override
42 public boolean onCreateOptionsMenu(Menu menu) {
43 // Inflate the menu; this adds items to the action bar if it is present.
44 getMenuInflater().inflate(R.menu.menu_main, menu);
45 return true;
46 }
47
48 @Override
49 public boolean onOptionsItemSelected(MenuItem item) {
50 // Handle action bar item clicks here. The action bar will
51 // automatically handle clicks on the Home/Up button, so long
52 // as you specify a parent activity in AndroidManifest.xml.
53 int id = item.getItemId();
54
55 //noinspection SimplifiableIfStatement
56 if (id == R.id.action_settings) {
57 return true;
58 }
59
60 return super.onOptionsItemSelected(item);
61 }
62}
让我们最后一次运行我们的应用程序,看看新的行为 。这结束了安卓协调员布局的例子。我们从浏览Fab小部件的默认行为开始,最后用我们自己的旋转行为覆盖了它。也不要错过按钮上的行为。这是一个很长的路。您可以通过下面的链接下载** Android协调员布局行为项目** 。
参考资料:官方Doc