Coordinator Layout使用

Coordinator Layout使用

  15年的Google I/O大会上,Google发布了一个新的Material Design支持库。在它的UI组件中,你可以看到一些新的***ViewGroups***,例如:AppbarLayoutCollapsingToolbarLayout***和***CoordinatorLayout。恰当地组合和配置这些***ViewGroups*** 可以非常强大,因此我决定写一篇如何使用它的文章,并介绍一些相关的***tips***。

掘金不支持动图,所以我发布到公众号上了,点击链接

CoordinatorLayout

顾名思义,这个***ViewGroup***的功能是协调其中的子***View***。 请看下面的图片:

图1

在这个例子中,你可以直观地看到View之间是如何相互协调的,某些***View***是如何依赖其它***View***的。***CoordinatorLayout***是这些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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/background_light"
    android:fitsSystemWindows="true"
    >

    <android.support.design.widget.AppBarLayout
        android:id="@+id/main.appbar"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        android:fitsSystemWindows="true"
        >

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/main.collapsing"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginStart="48dp"
            app:expandedTitleMarginEnd="64dp"
            >

            <ImageView
                android:id="@+id/main.backdrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:fitsSystemWindows="true"
                android:src="@drawable/material_flat"
                app:layout_collapseMode="parallax"
                />

            <android.support.v7.widget.Toolbar
                android:id="@+id/main.toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_collapseMode="pin"
                />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:lineSpacingExtra="8dp"
            android:text="@string/lorem"
            android:padding="@dimen/activity_horizontal_margin"
            />
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_margin="@dimen/activity_horizontal_margin"
        android:src="@drawable/ic_comment_24dp"
        app:layout_anchor="@id/main.appbar"
        app:layout_anchorGravity="bottom|right|end"
        />
</android.support.design.widget.CoordinatorLayout>

复制代码

考虑该布局的骨架,在***CoordinatorLayout*** 只有三个子***View***:一个 AppbarLayout,一个***scrolleable*** 视图和一个锚定 FloatingActionButton

AppBarLayout

AppBarLayout 继承自***LinearLayout***,子***View*** 垂直放置。添加某些参数,子***View***可以在滚动内容时管理它们的行为。 一开始可能听起来很混乱,所以如果我认为一张图片胜过千言万语,那么.gif就更好了:

图2

该图中AppBarLayout是蓝色***View***,collapsing image下面是***Toolbar***和***LinearLayout***,该***LinearLayout***包含一个标题和副标题,它下面还有一个***TabLayout***。

我们可以通过 AppbarLayout 的参数:layout_scrollFlags 来管理子***View***(不包括子***View***的子***View***)的行为。值:scroll , 在这个UI例子中,它几乎存在于所有的***View*** 中。如果未在任何子***View***中设定值,则***AppbarLayout*** 的子***View***会保持静态,允许可滑动的内容在其后面滑动。

使用值:snap ,我们避免陷入mid-animation-states,这意味着动画将始终隐藏或扩展其视图的所有高度。

用户滚动的时候,LinearLayout 中的标题和副标题将始终显示(enterAlways 值),而TabLayout将永远可见的,因为它没有设置任何标志(值)。

正如你说看到的,***AppbarLayout***真正的威力在于设置它的子***View***的滚动值可以管理它们的行为。

<AppBarLayout>
    <CollapsingToolbarLayout
        app:layout_scrollFlags="scroll|snap"
        />

    <Toolbar
        app:layout_scrollFlags="scroll|snap"
        />

    <LinearLayout
        android:id="+id/title_container"
        app:layout_scrollFlags="scroll|enterAlways"
        />

    <TabLayout /> <!-- no flags -->
</AppBarLayout>
复制代码

这些都是根据Google Developers docs提供的参数。无论如何,我的建议是总是写个Demo验证。本文末尾有这些实现的Github库。

AppbarLayout flags

SCROLL_FLAG_ENTER_ALWAYS:当进入(在屏幕上滚动)时,无论滚动视图是否也在滚动,视图都将滚动任何向下滚动事件。

SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED:’enterAlways ‘的另一个标志,它修改返回的视图,最初只回滚到它的折叠高度。

SCROLL_FLAG_EXIT_UNTIL_COLLAPSED:当退出(滚动屏幕)时,视图将滚动直到“折叠”。

SCROLL_FLAG_SCROLL:视图将滚动与滚动事件直接相关。

SCROLL_FLAG_SNAP:滚动结束时,如果视图仅部分可见,则它将被捕捉并滚动到其最近的边缘。

CoordinatorLayout Behaviors

让我们做一点测试,转到Android Studio(> = 1.4)并使用模板创建一个项目:Scrolling Activity,不触及任何内容,我们编译它,这就是我们发现的:

图3

如果我们检查生成的代码,会发现没有布局和java类涉及到滚动时 FloatingActionButton 的缩放动画。为什么?

答案在于FloatingActionButton源代码,因为Android Studio v1.2中包含了一个java**反编译器,ctrl/cmd + click 我们可以检查源代码并查看会发生什么:

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 *  Floating action buttons are used for a
 *  special type of promoted action. 
 *  They are distinguished by a circled icon 
 *  floating above the UI and have special motion behaviors 
 *  related to morphing, launching, and the transferring anchor point.
 * 
 *  blah.. blah.. 
 */
@CoordinatorLayout.DefaultBehavior(
    FloatingActionButton.Behavior.class)
public class FloatingActionButton extends ImageButton {
    ...

    public static class Behavior 
        extends CoordinatorLayout.Behavior<FloatingActionButton> {

        private boolean updateFabVisibility(
           CoordinatorLayout parent, AppBarLayout appBarLayout, 
           FloatingActionButton child {

           if (a long condition) {
                // If the anchor's bottom is below the seam, // we'll animate our FAB out
                child.hide();
            } else {
                // Else, we'll animate our FAB back in child.show(); } } } ... } 复制代码

负责该缩放动画的是 design library 引入的新元素***Behavior***,这里是:***CoordinatorLayout.Behavior***,根据包括滑动在内一些因素,FAB 显示与否,有趣,对吗?

SwipeDismissBehavior

继续深入研究代码,如果查看design support librarywidget 包,我们能找到一个公共类:SwipeDismissBehavior,有了这个新的***Behavior***,在我们的***CoordinatorLayout***布局中,我们很容易实现***swipe to dismiss***功能。

图4

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_swipe_behavior);
    mCardView = (CardView) findViewById(R.id.swype_card);

    final SwipeDismissBehavior<CardView> swipe 
        = new SwipeDismissBehavior();

        swipe.setSwipeDirection(
            SwipeDismissBehavior.SWIPE_DIRECTION_ANY);

        swipe.setListener(
            new SwipeDismissBehavior.OnDismissListener() {
            @Override public void onDismiss(View view) {
                Toast.makeText(SwipeBehaviorExampleActivity.this,
                    "Card swiped !!", Toast.LENGTH_SHORT).show();
            }

            @Override 
            public void onDragStateChanged(int state) {}
        });

        LayoutParams coordinatorParams = 
            (LayoutParams) mCardView.getLayoutParams();

        coordinatorParams.setBehavior(swipe);
    }
复制代码

Custom Behaviors

创建一个自定义的 Behavior,它并不像看起来那么困难。首先我们必须考虑两个核心因素:childdependency

图5

Childs and dependencies

child***是增强行为的***Viewdependency***作为一个触发器(trigger)和***child***交互。如下示例,child***将是***ImageView,依赖将是***Toolbar。这样,如果***Toolbar***移动,***ImageView***也会移动。

图6

现在我们来实现我们已经定义好的概念。第一步是继承CoordinatorLayout.Behavior,泛型***T*** 是我们感兴趣协调的***View***,这里是***ImageView***,接着我们需要重写着些方法:

  • layoutDependsOn
  • onDependentViewChanged

方法 layoutDependsOn:每当布局中发生某些事情的时候都会调用,一旦我们识别出了依赖关系,方法必须返回***True***,在这个例子中,当用户滚动时(因为***Toolbar*** 会移动),这个方法会自动触发,就这样我们能使我们的子***View***看到相应的反应。

   @Override
   public boolean layoutDependsOn(
      
      CoordinatorLayout parent, 
      CircleImageView, child, 
      View dependency) {

      return dependency instanceof Toolbar; 
  } 
复制代码

每当 layoutDependsOn 返回 ***true***时,第二个方法 onDependentViewChanged 将会被调用。这里,我们必须实现和假定的依赖相关的动画,平移或移动。

   public boolean onDependentViewChanged(

      CoordinatorLayout parent, 
      CircleImageView avatar, 
      View dependency) {


      modifyAvatarDependingDependencyState(avatar, dependency);
   }

   private void modifyAvatarDependingDependencyState(
    CircleImageView avatar, View dependency) {
        //  avatar.setY(dependency.getY());
        //  avatar.setBlahBlat(dependency.blah / blah);
    } 
复制代码

完整代码:


public static class AvatarImageBehavior 
   extends 
CoordinatorLayout.Behavior<CircleImageView> {

   @Override
   public boolean layoutDependsOn(

    CoordinatorLayout parent, 
    CircleImageView, child, 
    View dependency) {

       return dependency instanceof Toolbar; 
  } 

   

  public boolean onDependentViewChanged(
      
    CoordinatorLayout parent, 
    CircleImageView avatar, 
    View dependency) {

      modifyAvatarDependingDependencyState(avatar, dependency);
   }

  private void modifyAvatarDependingDependencyState(
    CircleImageView avatar, View dependency) {
        //  avatar.setY(dependency.getY());
        //  avatar.setBlahBlah(dependency.blah / blah);
    }    
}
复制代码

参考资料

转载于:https://juejin.im/post/5c0ddb3f5188257c3045de42

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101274.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号