android中layout的作用,Android CoordinatorLayout使用总结

设计支持库

设计软件包提供的 API 支持向应用中添加 Material Design 组件和模式。设计支持库添加了对应用开发者依赖的各种 Material Design 组件和模式的支持,例如抽屉式导航栏、浮动操作按钮 (FAB)、快捷信息栏和标签页。

使用gradle引入:

compile 'com.android.support:design:25.2.0'

CoordinatorLayout可以做什么

CoordinatorLayout is a super-powered FrameLayout

CoordinatorLayout is intended for two primary use cases:

As a top-level application decor or chrome layout

As a container for a specific interaction with one or more child views

用作顶层布局

作为一个容器与一个或者多个子View进行交互

Behavior

CoordinatorLayout中子view进行交互就是通过Behavior来完成的。

指定Behavior有三种方式:

通过构造方法实例,并在java代码中设置到LayoutParamas里

android.support.design.widget.CoordinatorLayout.LayoutParams#setBehavior

在layout xml里指定:

xmlns:app="http://schemas.android.com/apk/res-auto"

...

app:layout_behavior="com.xujun.contralayout.behavior.NoNestedBehavior"/>

通过DefaultBehavior注解指定

@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)

public class AppBarLayout extends LinearLayout {

...

}

Behavior几个重要方法作用

/**

* Determine whether the supplied child view has another specific sibling view as a

* layout dependency.

*

*

This method will be called at least once in response to a layout request. If it

* returns true for a given child and dependency view pair, the parent CoordinatorLayout

* will:

*

*

Always lay out this child after the dependent child is laid out, regardless

* of child order.

*

Call {@link #onDependentViewChanged} when the dependency view's layout or

* position changes.

*

*

* @param parent the parent view of the given child

* @param child the child view to test

* @param dependency the proposed dependency of child

* @return true if child's layout depends on the proposed dependency's layout,

* false otherwise

*

* @see #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)

*/

public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {

return false;

}

/**

* Respond to a change in a child's dependent view

*

*

This method is called whenever a dependent view changes in size or position outside

* of the standard layout flow. A Behavior may use this method to appropriately update

* the child view in response.

*

*

A view's dependency is determined by

* {@link #layoutDependsOn(CoordinatorLayout, android.view.View, android.view.View)} or

* if {@code child} has set another view as it's anchor.

*

*

Note that if a Behavior changes the layout of a child via this method, it should

* also be able to reconstruct the correct position in

* {@link #onLayoutChild(CoordinatorLayout, android.view.View, int) onLayoutChild}.

* onDependentViewChanged will not be called during normal layout since

* the layout of each child view will always happen in dependency order.

*

*

If the Behavior changes the child view's size or position, it should return true.

* The default implementation returns false.

*

* @param parent the parent view of the given child

* @param child the child view to manipulate

* @param dependency the dependent view that changed

* @return true if the Behavior changed the child view's size or position, false otherwise

*/

public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {

return false;

}

public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency)

layoutDependsOn方法用来确定依赖关系,返回true表示依赖关系确定,即child依赖dependency。dependency对child是无感知的,当dependency的大小或位置变化时,会回调所有依赖dependency的child对应behavior的onDependentViewChanged方法,onDependentViewChanged返回true表示对应child的位置或大小变化了。

依赖关系会改变子View onMeasure onLayout的顺序,被依赖的dependency在之前执行。

public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev)

public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev)

这两个方法分别在CoordinatorLayout中的onInterceptTouchEvent、onTouchEvent中被调用。只要有一个behavior的onInterceptTouchEvent返回true时,所有子View的touch事件都会被拦截。

public boolean onMeasureChild(CoordinatorLayout parent, V child,

int parentWidthMeasureSpec, int widthUsed,

int parentHeightMeasureSpec, int heightUsed)

public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection)

这两个方法分别在CoordinatorLayout中的onMeasure、onLayout中调用,当返回true时,表示测量、布局完成,则该child默认的onMeasure、onLayout不会执行,被拦截了。

public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,

V child, View directTargetChild, View target, int nestedScrollAxes)

public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,

View directTargetChild, View target, int nestedScrollAxes)

public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target)

public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,

int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)

public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,

int dx, int dy, int[] consumed)

public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target,

float velocityX, float velocityY, boolean consumed)

public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,

float velocityX, float velocityY)

CoordinatorLayout实现了NestedScrollingParent接口,这几个方法分别对应NestedScrollingParent接口中的方法,当CoordinatorLayout中方法回调后,调用所有Behavior中对应方法,主要用于嵌套滑动,必须配合NestedScrollingChild使用。

参考:

sidhu眼中的CoordinatorLayout.Behavior(一)

sidhu眼中的CoordinatorLayout.Behavior(二)

sidhu眼中的CoordinatorLayout.Behavior(三)

案例分析

layout布局:

xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/refresh_layout"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:ptr_ratio_of_header_height_to_refresh="1.0"

>

android:id="@+id/coordinator_layout"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/app_bar"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="#00000000">

android:id="@+id/frag_head_container"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="@drawable/xxx"

app:layout_collapseMode="parallax"

app:layout_scrollFlags="scroll|exitUntilCollapsed" />

android:id="@+id/tab_layout"

android:layout_width="match_parent"

android:layout_height="wrap_content"

app:tabIndicatorColor="@color/colorAccent"

app:tabIndicatorHeight="4dp"

app:tabSelectedTextColor="#000"

app:tabTextColor="#fff">

android:id="@+id/container"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:layout_behavior="@string/appbar_scrolling_view_behavior" />

android:id="@+id/title_bar_group"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical">

android:id="@+id/status_bar_holder"

android:layout_width="match_parent"

android:layout_height="0dp" />

android:id="@+id/title_bar"

android:layout_width="match_parent"

android:layout_height="wrap_content" />

android.support.design.widget.AppBarLayout$ScrollingViewBehavior

CoordinatorLayout的直接子View两个,分别是AppBarLayout、ViewPager,Behavior只能设置给CoordinatorLayout的直接子View,因为Behavior是android.support.design.widget.CoordinatorLayout.LayoutParams的一个属性。

AppBarLayout的Behavior是通过注解的方式指定的

@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)

public class AppBarLayout extends LinearLayout {

...

}

ViewPager的Behavior是在xml布局中指定的,由app:layout_behavior指定为

android.support.design.widget.AppBarLayout.ScrollingViewBehavior

AppBarLayout.Behavior主要重写了以下方法

//这两个方法用来处理触摸在AppBarLayout上的手势,使AppBarLayout进行上下滑动

public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev)

public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev)

//这几个方法是用来处理手势不在AppBarLayout上时,比如在ViewPager中的RecycleView上滑动时,使AppBarLayout响应嵌套滚动事件

public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,

View directTargetChild, View target, int nestedScrollAxes)

public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,

View target, int dx, int dy, int[] consumed)

public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,

View target, int dxConsumed, int dyConsumed,

int dxUnconsumed, int dyUnconsumed)

public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl,

View target)

public boolean onNestedFling(final CoordinatorLayout coordinatorLayout,

final AppBarLayout child, View target, float velocityX, float velocityY,

boolean consumed)

AppBarLayout.ScrollingViewBehavior主要重写的方法

//这两个方法主要作用是让child(即ViewPager)一直跟随在AppBarLayout在下面

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

return dependency instanceof AppBarLayout;

}

public boolean onDependentViewChanged(CoordinatorLayout parent, View child,

View dependency){

offsetChildAsNeeded(parent, child, dependency);

return false;

}

//用来确定child(即ViewPager)的高度

public boolean onMeasureChild(CoordinatorLayout parent, View child,

int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec,

int heightUsed){

...

//header即AppBarLayout

final View header = findFirstDependency(dependencies);

...

int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);

...

//getScrollRange(header)是指AppBarLayout可滑动的高度

final int height = availableHeight - header.getMeasuredHeight()

+ getScrollRange(header);

}

可以看到ScrollingViewBehavior并没有重写任何的滑动事件,如果ViewPager里没有可滑动的控件,则手势在ViewPager上时将不可以上下滑动,如果可滑动的是ListView这种没有实现NestedScrollingChild接口的View,则AppBarLayout将也不能跟着滑动,只有实现NestedScrollingChild时,AppBarLayout才能配合实现嵌套滚动。

AppBarLayout要可滚动,需要设置几个参数,可滚动的子View必须要设置下面这个属性,并且第一个直接子View一定要有这个属性

app:layout_scrollFlags="scroll"

加上exitUntilCollapsed并且设置minHeight可以减少可滚动高度,不设置默认是整个子View的高度。

layout_scrollFlags的几个值

scroll:子View 添加layout_scrollFlags属性 的值scroll 时,这个View将会随着可滚动View(如:ScrollView,以下都会用ScrollView 来代替可滚动的View )一起滚动,就好像子View 是属于ScrollView的一部分一样。

enterAlways:子View 添加layout_scrollFlags属性 的值有enterAlways 时, 当ScrollView 向下滑动时,子View 将直接向下滑动,而不管ScrollView 是否在滑动。注意:要与scroll 搭配使用,否者是不能滑动的。

enterAlwaysCollapsed:enterAlwaysCollapsed 是对enterAlways 的补充,当ScrollView 向下滑动的时候,滑动View(也就是设置了enterAlwaysCollapsed 的View)下滑至折叠的高度,当ScrollView 到达滑动范围的结束值的时候,滑动View剩下的部分开始滑动。这个折叠的高度是通过View的minimum height (最小高度)指定的。

exitUntilCollapsed:当ScrollView 滑出屏幕时(也就时向上滑动时),滑动View先响应滑动事件,滑动至折叠高度,也就是通过minimum height 设置的最小高度后,就固定不动了,再把滑动事件交给 scrollview 继续滑动

snap:在滚动结束后,如果view只是部分可见,它将滑动到最近的边界。

一个bug记录

AppBarLayout遮挡布局在其上面View问题:

解决1、setCollapsedState设置为true就会遮挡,设置为false则不会

解决2、mAppBarLayout.setStateListAnimator(null);

解决3、设置要显示在前面View的elevation大于appbarlayout的值(4dp)。

原因:design_appbar_state_list_animator.xml中在状态state_collapsed为true时设elevation为4dp了

xmlns:app="http://schemas.android.com/apk/res-auto">

android:propertyName="elevation"

android:valueTo="0dp"

android:valueType="floatType"/>

android:propertyName="elevation"

android:valueTo="@dimen/design_appbar_elevation"

android:valueType="floatType"/>

android:propertyName="elevation"

android:valueTo="0"

android:valueType="floatType"/>

总结

CoordinatorLayout通过对子View设置Behavior来完成各种复杂交互,Behavior可以拦截touch事件、测量、布局、嵌套滚动、指定依赖及响应依赖位置大小等变化。

优点:

对View无侵入式,用组合替代继承来拦截一切。

缺点:

嵌套滚动View必须实现NestedScrollingChild接口,ListView、ScrollView等将不能正常使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值