CoordinatorLayout用法学习

来源:互联网 发布:帧中继网络 编辑:程序博客网 时间:2024/05/01 19:18

以前我们创建项目时候,一个页面的布局是线性或者相对等,当我在AS上新建一个module时,系统默认的最外层布局不再是我们熟悉的五大布局中的一种,而是一个全新的布局:CoordinatorLayout。它是Material风格的重要组件, 作为布局的顶层控件,协调(Coordinate)其他组件, 实现联动。

activity_main.xml主布局

<?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"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="true"    tools:context="com.example.administrator.myapplication.MainActivity">    <android.support.design.widget.AppBarLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:theme="@style/AppTheme.AppBarOverlay">        <android.support.v7.widget.Toolbar            android:id="@+id/toolbar"            android:layout_width="match_parent"            android:layout_height="?attr/actionBarSize"            android:background="?attr/colorPrimary"            app:layout_scrollFlags="scroll|enterAlways"            app:popupTheme="@style/AppTheme.PopupOverlay" />    </android.support.design.widget.AppBarLayout>    <include layout="@layout/content_main" />    <android.support.design.widget.FloatingActionButton        android:id="@+id/fab"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="bottom|end"        android:layout_margin="@dimen/fab_margin"        android:src="@android:drawable/ic_dialog_email" /></android.support.design.widget.CoordinatorLayout>

content_main.xml

<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.NestedScrollView    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context="com.example.administrator.myapplication.MainActivity"    tools:showIn="@layout/activity_main">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Hello World!" /></android.support.v4.widget.NestedScrollView>

看看效果图如下:
这里写图片描述

??很明显,状态栏是有问题的,我们只需要将主布局的
android:fitsSystemWindows=”true”取调即可
以下是去掉之后的显示状态
这里写图片描述

这里写图片描述

以上的布局需要注意几个点:
app:layout_scrollFlags=”scroll|enterAlways”
app:layout_behavior=”@string/appbar_scrolling_view_behavior”

其中控制ToolBar是否可以滚出屏幕的属性是由app:layout_scrollFlags=”scroll|enterAlways”

enterAlwaysCollapsed: 顾名思义,这个flag定义的是何时进入(已经消失之后何时再次显示)。假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。

exitUntilCollapsed: 同样顾名思义,这个flag时定义何时退出,当你定义了一个minHeight,这个view将在滚动到达这个最小高度的时候消失

我们来了解下,如何自定义Behavior
CoordinatorLayout的工作原理是搜索定义了CoordinatorLayout Behavior 的子view,不管是通过在xml中使用app:layout_behavior标签还是通过在代码中对view类使用@DefaultBehavior修饰符来添加注解。当滚动发生的时候,CoordinatorLayout会尝试触发那些声明了依赖的子view。要自己定义CoordinatorLayout Behavior,你需要实现layoutDependsOn() 和onDependentViewChanged()两个方法。

<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>

其实它并不是一个字符串资源,而它代表的是一个类

public static class ScrollingViewBehavior extends ViewOffsetBehavior<View>class ViewOffsetBehavior<V extends View> extends Behavior<V>

1.某个view需要根据监听另一个的行为来控制自己的行为,这个时候我们需要重写2个方法

public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {             return false;         } 
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {             return false;         } 

结合我们一个列子来理理思路,首先看例子的效果图:
这里写图片描述

先看这个例子的主布局activity_main.xml

<?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"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.administrator.myapplication.MainActivity">    <android.support.design.widget.AppBarLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:theme="@style/AppTheme.AppBarOverlay">        <android.support.v7.widget.Toolbar            android:id="@+id/toolbar"            android:layout_width="match_parent"            android:layout_height="?attr/actionBarSize"            android:background="?attr/colorPrimary"            app:layout_scrollFlags="scroll|enterAlways"            app:popupTheme="@style/AppTheme.PopupOverlay" />    </android.support.design.widget.AppBarLayout>    <include layout="@layout/content_main" />    <android.support.design.widget.FloatingActionButton        android:id="@+id/fab"        android:layout_width="50dp"        android:layout_height="50dp"        android:layout_gravity="bottom|center"        app:layout_behavior="com.example.administrator.myapplication.FooterBehavior"        android:src="@android:drawable/ic_dialog_email" /></android.support.design.widget.CoordinatorLayout>

上面的主布局中,我们为FloatingActionButton增加了一个自定义的属性行为

 app:layout_behavior="com.example.administrator.myapplication.FooterBehavior"

意思就是:这个FloatingActionButton根据监听AppBarLayout滑动的行为来控制自己的行为

content_main.xml

<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.NestedScrollView    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context="com.example.administrator.myapplication.MainActivity"    tools:showIn="@layout/activity_main">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Hello World!" /></android.support.v4.widget.NestedScrollView>

FooterBehavior

package com.example.administrator.myapplication;import android.content.Context;import android.support.design.widget.AppBarLayout;import android.support.design.widget.CoordinatorLayout;import android.util.AttributeSet;import android.view.View;public class FooterBehavior extends CoordinatorLayout.Behavior<View>{    public FooterBehavior(Context context,AttributeSet attributeSet){        super(context,attributeSet);      }      @Override      public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {          return dependency instanceof AppBarLayout;    }      @Override      public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {          float scaleY = Math.abs(dependency.getY()) / dependency.getHeight();          child.setTranslationY(child.getHeight() * scaleY);          return true;      }  }  

这两个方法的参数都是一样的,解释一下,第一个不用说,就是当前的CoordinatorLayout,第二个参数是我们设置这个Behavior的View,也就是FloatingActionButton, 第三个是我们关心的那个View。如何知道关心的哪个呢?layoutDependsOn的返回值决定了一切!

我们的例子layoutDependsOn方法中,return dependency instanceof AppBarLayout;是关心的AppBarLayout,也就是Toolbar(AppBarLayout)变化位置,引起FloatingActionButton的位置变化

onDependentViewChanged方法意思是什么呢?
先计算出Toolbar(AppBarLayout)暴露在屏幕的比例大小
然后计算出FloatingActionButton需要移动到的位置

以下是log日志

 Log.i("TAG","scaleY-->>"+scaleY+",,child.getHeight() * scaleY-->>"+child.getHeight() * scaleY);
 I/TAG: scaleY-->>0.05952381,,child.getHeight() * scaleY-->>8.928572I/TAG: scaleY-->>0.31547618,,child.getHeight() * scaleY-->>47.321426I/TAG: scaleY-->>0.8214286,,child.getHeight() * scaleY-->>123.21429 I/TAG: scaleY-->>1.0,,child.getHeight() * scaleY-->>150.0 I/TAG: scaleY-->>0.875,,child.getHeight() * scaleY-->>131.25 I/TAG: scaleY-->>0.10119048,,child.getHeight() * scaleY-->>15.178572 I/TAG: scaleY-->>0.0,,child.getHeight() * scaleY-->>0.0

2、第二种情况,滑动。因为这个是根据CoordinatorLayout里子view的滚动行为来改变我们的状态的,所以情况1中的2个方法我们就不需要重写了。下面,我们用情况2来实现上面的效果。

package com.example.administrator.myapplication;import android.content.Context;import android.support.design.widget.CoordinatorLayout;import android.support.v4.view.ViewCompat;import android.util.AttributeSet;import android.view.View;public class FooterBehavior extends CoordinatorLayout.Behavior<View>{    private float targetY = -1;    private static final String TAG = "FooterBehavior";    public FooterBehavior(Context context,AttributeSet attributeSet){        super(context, attributeSet);    }    @Override    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,                                       View directTargetChild, View target, int nestedScrollAxes) {        if(targetY == -1){            targetY = target.getY();        }        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;    }    @Override    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target,                                  int dx, int dy, int[] consumed) {        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);        float scrooY = targetY - Math.abs(target.getY());        float scaleY = scrooY / targetY;        child.setTranslationY(child.getHeight() * scaleY);    }}  

在方法onStartNestedScroll中,首先获取target在Y轴上距离屏幕顶端的距离,然后判断是否是在Y轴上滚动。

方法onNestPreScroll中,就是时时根据target距离屏幕顶端的距离计算出滚动的距离,然后根据比例计算出child移动的距离。

我们简单看下源码

 * @param coordinatorLayout the CoordinatorLayout parent of the view this Behavior is         *                          associated with         * @param child the child view of the CoordinatorLayout this Behavior is associated with         * @param directTargetChild the child view of the CoordinatorLayout that either is or         *                          contains the target of the nested scroll operation         * @param target the descendant view of the CoordinatorLayout initiating the nested scroll         * @param nestedScrollAxes the axes that this nested scroll applies to. See         *                         {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},         *                         {@link ViewCompat#SCROLL_AXIS_VERTICAL}         * @return true if the Behavior wishes to accept this nested scroll         *         * @see NestedScrollingParent#onStartNestedScroll(View, View, int)         */        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,                V child, View directTargetChild, View target, int nestedScrollAxes) {            return false;        }

百度翻译如下:
child:coordinatorlayout孩子的这种行为是与(本例就是FloatingActionButton)
target :启动嵌套滚动coordinatorlayout后裔的观点(本例就是AppBarLayout)
directTargetChild 这个涉及到深层次的布局问题,我们的布局中只有一个控件FloatingActionButton

2 1
原创粉丝点击