CoordinatorLayout 使用
很久没开发过android应用了,很多东西忘了。查看资料后备份~~~~~
转自:https://blog.csdn.net/xyz_lmn/article/details/48055919 https://www.jianshu.com/p/5cc3bd23be7b https://www.jianshu.com/p/a506ee4afecb
CoordinatorLayout
作用: 使用新的思路通过协调调度子布局的形式实现触摸影响布局的形式产生动画效果
。
使用:CoordinatorLayout通过设置子View的 Behaviors来调度子View
。
使用CoordinatorLayout需要先添加依赖:
api "com.android.support:design:26.1.0"
CoordinatorLayout与FloatingActionButton
CoordinatorLayout与AppBarLayout
AppBarLayout嵌套CollapsingToolbarLayout
大家自己看效果,经常使用的效果,再决定在自己的APP中要不要使用coordinatoelayout。
主要说一个属性 fitsSystemWindows
根据官方文档,如果某个View 的fitsSystemWindows 设为true,那么该View的padding属性将由系统设置,用户在布局文件中设置的
padding会被忽略。系统会为该View设置一个paddingTop,值为statusbar的高度。fitsSystemWindows默认为false。
重要说明:
- 只有将statusbar设为透明,或者界面设为全屏显示(设置View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag)时,fitsSystemWindows才会起作用。不然statusbar的空间轮不到用户处理,这时会由ContentView的父控件处理,如果用HierarchyView 工具查看,将会看到,ContentView的父控件的paddingTop将会被设置。
- 如果多个view同时设置了fitsSystemWindows,只有第一个会起作用。这是一般情况,后面会介绍特殊情况。
fitsSystemWindows属性的个性化
第一次接触fitsSystemWindows是在CoordinatorLayout控件。发现有很多诡异的地方。
- fitsSystemWindows的表现和官方文档描述的不一样。
- 有时CoordinatorLayout的子控件也会设置fitsSystemWindows属性,而且子控件的fitsSystemWindows也会有作用。
这些令我很困惑,查了些资料之后找到了原因:设置paddingTop只是fitsSystemWindows属性的默认行为,View可以对fitsSystemWindows进行个性化。
CoordinatorLayout对fitsSystemWindows的个性化。API 21 以上可以通过调用View的setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener)函数,改变fitsSystemWindows的默认行为。在OnApplyWindowInsetsListener的onApplyWindowInsets函数,可以决定如何处理statusbar的空间。
重要说明:
- 在API 21以前,好像也可以重写View的某个函数达到类似效果。
- 必须将statusbar设为透明,或者界面设为全屏显示setOnApplyWindowInsetsListener才会起作用。这点很容易理解,你都没有statusbar空间,你个性化个屁啊。
CoordinatorLayout对fitsSystemWindows的个性化,关键代码:
if (ViewCompat.getFitsSystemWindows(view)) {
// First apply the insets listener
ViewCompat.setOnApplyWindowInsetsListener(view, insetsListener);
// Now set the sys ui flags to enable us to lay out in the window insets
view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
final class ApplyInsetsListener implements android.support.v4.view.OnApplyWindowInsetsListener {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
setWindowInsets(insets);
return insets.consumeSystemWindowInsets();
}
}
总结:CoordinatorLayout对fitsSystemWindows主要做了以下处理。
- 将界面设为全屏。view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
- 自己绘制statusbar背景。setStatusBarBackground函数可以设置statusbar背景。或者在布局文件中通过app:statusBarBackground设置。
- 如果CoordinatorLayout的子View没有设置fitsSystemWindows,在layout时将子Viwe向下偏移statusbar的高度,用来显示CoordinatorLayout绘制的statusbar。如果子view设置了fitsSystemWindows,子View会覆盖CoordinatorLayout的statusbar。setStatusBarBackground设置的状态栏
将被覆盖,不再起作用。具体逻辑可参考CoordinatorLayout的layoutChild 函数。 - 调用dispatchApplyWindowInsets,让子view的behavior或子view接着处理fitsSystemWindows属性。CoordinatorLayout的很多常用的子view如AppBarLayout也对fitsSystemWindows进行了个性化处理。
CoordinatorLayout控件控制子控件滑动主要是通过behavior来控制的。
CoordinatorLayout功能如此强大,而他的神奇之处在于Behavior对象,CoordinatorLayout自己并不控制View,所有的控制权都在Behavior。前面写到了FloatingActionButton.Behavior,AppBarLayout.Behavior, AppBarLayout.ScrollingViewBehavior。 AppBarLayout中有两个Behavior,一个是拿来给它自己用的,另一个是拿来给它的兄弟结点用的。这些Behavior实现了复杂的控制功能。系统的Behavior毕竟有限,我们可以通过自定义的方式来实现自己的Behavior。
通过 CoordinatorLayout.Behavior(YourView.Behavior.class) 来定义自己的Behavior,并在layout 文件中设置 app:layout_behavior=”com.example.app.YourView$Behavior” 来达到效果。
自定义Behavior 需要重写两个方法:
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency)
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)
- 1
- 2
- 3
如下面的例子,实现了点击FloatingActionButton点击旋转90度,并适配Snackbar。
public class RotateBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private static final String TAG = RotateBehavior.class.getSimpleName();
public RotateBehavior() {
}
public RotateBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
return dependency instanceof Snackbar.SnackbarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
float translationY = getFabTranslationYForSnackbar(parent, child);
float percentComplete = -translationY / dependency.getHeight();
child.setRotation(-90 * percentComplete);
child.setTranslationY(translationY);
return false;
}
private float getFabTranslationYForSnackbar(CoordinatorLayout parent,
FloatingActionButton fab) {
float minOffset = 0;
final List<View> dependencies = parent.getDependencies(fab);
for (int i = 0, z = dependencies.size(); i < z; i++) {
final View view = dependencies.get(i);
if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(fab, view)) {
minOffset = Math.min(minOffset,
ViewCompat.getTranslationY(view) - view.getHeight());
}
}
return minOffset;
}
}
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_done"
app:layout_behavior="com.meizu.coordinatorlayoutdemo.RotateBehavior"/>
</android.support.design.widget.CoordinatorLayout>
效果: