[Android]CoordinatorLayout简介(二)几
参考资料
Behavior的基类是CoordinatorLayout#Behavior,先看一下类继承关系:
![](https://img.haomeiwen.com/i3112838/66f9ad3a2b87584f.png)
可以看出,系统默认实现的Behavior大致分类六类:
- 继承于SwipeDismissBehavor的
- 继承于ExpandableBehavior的
- 继承于FloatingActionButton的
- 继承于ViewOffsetBehavior的
- BottomSheetBehavior
我们逐个分析
SwipeDismissBehavor
看先文档简介:
An interaction behavior plugin for child views of CoordinatorLayout to provide support for the 'swipe-to-dismiss' gesture.
翻译:一个为CoordinatorLayout的子View提供"滑动消失"支持的交互插件
OK 那我们大概就知道它的作用了
看下使用方式:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.dafasoft.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.behavior.SwipeDismissBehavior">
<ImageView
android:layout_width="match_parent"
android:layout_height="230dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:scaleType="fitXY"
android:src="@drawable/yellow_zero"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?attr/colorPrimary"
app:tabIndicatorColor="@color/teal_700"
app:tabIndicatorHeight="4dp"
app:tabSelectedTextColor="#000"
app:tabTextColor="#fff"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
然后运行代码 啪一下闪退了,很快啊!
发生甚么事了? 怎么回事?
看下CoordinatorLayout#parseBehavior的部分代码,这个方法的作用是根据我们在xml中设置的layout_behavior
来解析成真正的Behavior对象,我们看下其中的部分逻辑:
try {
Map<String, Constructor<Behavior>> constructors = sConstructors.get();
if (constructors == null) {
constructors = new HashMap<>();
sConstructors.set(constructors);
}
Constructor<Behavior> c = constructors.get(fullName);
if (c == null) {
final Class<Behavior> clazz =
(Class<Behavior>) Class.forName(fullName, false, context.getClassLoader());
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
constructors.put(fullName, c);
}
return c.newInstance(context, attrs);
} catch (Exception e) {
throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
}
我们获取Constructor的时候,是获取的一个带参数的Constructor方法,CONSTRUCTOR_PARAMS
的值分别是Context和AttributeSet
因此,假如我们要使用SwipeDismissBehavor,需要自定义一下:
public class TestSwipeDismissBehavior extends SwipeDismissBehavior {
public TestSwipeDismissBehavior(Context context, AttributeSet attributes) {
super();
}
}
然后将app:layout_behavior="com.google.android.material.behavior.SwipeDismissBehavior"
这一行改为
app:layout_behavior=".behavior.TestSwipeDismissBehavior"
看下效果:
![](https://img.haomeiwen.com/i3112838/3f1941bb49d1994e.gif)
官方为我们提供的SwipeDismissBehavior的实现类,有且只有一个BaseTransientBottomBar#Behavior, 它的使用是在SnackBar中
什么是SnackBar
Snackbar显示在所有屏幕其它元素之上(屏幕最顶层),同一时间只能显示一个snackbar。
Snackbar的基本使用很简单,与Toast类似。
Snackbar.make(view, message_text, duration)
.setAction(action_text, click_listener)
.show();
make()方法是生成Snackbar的。Snackbar需要一个控件容器view用来容纳,官方推荐使用CoordinatorLayout来确保Snackbar和其他组件的交互,比如滑动取消Snackbar、Snackbar出现时FloatingActionButton上移。显示时间duration有三种类型LENGTH_SHORT、LENGTH_LONG和LENGTH_INDEFINITE。
setAction()方法可设置Snackbar右侧按钮,增加进行交互事件。如果不使用setAction()则只显示左侧message。
Snackbar.make(coordinatorLayout,"这是massage", Snackbar.LENGTH_LONG).setAction("这是action", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"你点击了action",Toast.LENGTH_SHORT).show();
}
}).show();
效果:
![](https://img.haomeiwen.com/i3112838/48108c412b4f492b.gif)
ExpandableBehavior
看下官方文档
![](https://img.haomeiwen.com/i3112838/a0e497f956ee93ad.png)
过期了...使用MaterialContainerTransform替代了
pass吧...
FloatingActionButton#Behavior
这个Behavior官方是给FloatingActionButton特供的
我们看一下FloatingActionButton吧
FloatingActionButton是5.0版本出现的控件,显示一个圆形悬浮按钮。需要添加Design依赖库(现在已迁移至Androidx中)并且使用Theme.AppCompat主题
FloatingActionButton配置
属性 | 说明 |
---|---|
android:src | 显示的图标,最好是24dp的 |
app:backgroundTint | 正常的背景颜色 |
app:rippleColor | 按下时的背景颜色 |
app:elevation | 正常的阴影大小(默认6dp) |
app:pressedTranslationZ | 按下时的阴影大小(默认12dp) |
app:borderWidth | 边框宽度 |
app:layout_anchor | 设置FAB的锚点,即以哪个控件为参照设置位置 |
app:layout_anchorGravity | FAB相对于锚点的位置 |
app:fabSize | normal或mini(对应56dp和40dp) |
布局:
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_margin="20dp"
app:rippleColor="#ffe5e5e5"
app:backgroundTint="@color/black"
app:elevation="6dp"
app:pressedTranslationZ="6dp"
app:fabSize="mini"
app:borderWidth="0dp"
android:src="@drawable/ic_dragon_avatar_9"/>
效果如下
![](https://img.haomeiwen.com/i3112838/24adcac1eb2b6a4e.jpg)
与SnackBar的联动效果:
FloatingActionButton天然支持与SnackBar的联动,我们只需要将FloatingActionButton设置为CoordinatorLayout的子View,在展示SnackBar时,SnackBar#make方法的View参数传入CoordinatorLayout的对象即可
Snackbar.make(coordinatorLayout,"这是massage", Snackbar.LENGTH_LONG).setAction("这是action", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"你点击了action",Toast.LENGTH_SHORT).show();
}
}).show();
效果图:
![](https://img.haomeiwen.com/i3112838/0589874a8d5f1810.gif)
通过继承FloatingActionButton#Behavior还可以实现其他各种各样的联动效果
继承自ViewOffsetBehavior
这一部分Behavior是我们在日常开发中使用频次最高的
其中两个最重要的Behavior:
- ViewOffsetBehavior
- AppBarLayout#Behavior
我们在上篇博客中分享的几种效果都是通过这两种Behavior实现的
当我们需要实现滚动控件和AppBarLayout的折叠、悬浮等效果时,滚动控件必须制定Behavior为ViewOffsetBehavior或其子类,否则是不会有协同滑动的效果的
AppBarLayout#Behavior是AppBarLayout中各种炫酷效果的基石,AppBarLayout#Behavior负责收取和处理从CoordinatorLayout传来的滑动或者嵌套滑动事件,并根据这些事件对AppBarLayout的子控件和其自身进行变换
有同学会说我们在xml中定义AppBarLayout时并没有指定Behavior啊,是的,如果我们没有手动指定,系统会默认指定AppBarLayout的Behavior为AppBarLayout#Behavior,这是通过AppBarLayout的类注解实现的:
@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
public class AppBarLayout extends LinearLayout {
static final int PENDING_ACTION_NONE = 0x0;
static final int PENDING_ACTION_EXPANDED = 0x1;
static final int PENDING_ACTION_COLLAPSED = 0x2;
static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
static final int PENDING_ACTION_FORCE = 0x8;
...
这两种Behavior的代码较为复杂且江湖地位显赫,本篇不作过多介绍,在接下来我们分析CoordinatorLayout源码时会着重讲解
BottomSheetBehavior
BottonSheetBehavior用来实现从底部滑出的抽屉效果,我们经常可以在音乐播放APP和地图APP中见到这种效果
BottomSheetBehavior支持的属性:
属性 | 含义 |
---|---|
behavior_peekHeight | 当控件隐藏时折叠的高度 |
behavior_hideable | 是否可以隐藏 |
app:behavior_skipCollapsed | 是否跳过折叠状态 |
使用方式;
xml的定义:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/bottomSheetLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_peekHeight="50dp"
app:behavior_hideable="true">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/yellow_zero_two"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<Button
android:id="@+id/clickBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击"/>
</RelativeLayout>
Activity:
public class BottomSheetBehaviorActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_sheet_behavior);
findViewById(R.id.clickBtn).setOnClickListener(v -> {
LinearLayout bottomSheetLayout = findViewById(R.id.bottomSheetLayout);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
bottomSheetBehavior.setState(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED);
});
findViewById(R.id.floatingActionButton).setOnClickListener(v -> {
LinearLayout bottomSheetLayout = findViewById(R.id.bottomSheetLayout);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
bottomSheetBehavior.setState(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED);
});
}
}
效果图:
![](https://img.haomeiwen.com/i3112838/b4f4d423824d3d91.gif)