15年的Google I/O大会上,Google发布了一个新的Material Design支持库。在它的UI组件中,你可以看到一些新的***ViewGroups***,例如:AppbarLayout, CollapsingToolbarLayout***和***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 library的 widget 包,我们能找到一个公共类: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,它并不像看起来那么困难。首先我们必须考虑两个核心因素:child 和 dependency
图5
Childs and dependencies
child***是增强行为的***View,dependency***作为一个触发器(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账号...