热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

androidlayout界面开发,Android开发之CoordinatorLayout使用详解一

官网描述为:CoordinatorLayout是一个增强版的FrameLayout(继承自ViewGroup)用途:1、作为应用的顶层视图。2、作为一个

官网描述为:CoordinatorLayout是一个增强版的FrameLayout(继承自ViewGroup)

用途:

1、作为应用的顶层视图。

2、作为一个可以指定子View之间相互作用的容器,通过给CoordinatorLayout的子View指定CoordinatorLayout.Behavior 来定义子view之间的相互作用。(你可以想象成:CoordinatorLayout相当于在两个View之间充当中介,这样子的好处就是两个view之间的耦合度降低了,只需要跟coordinatorLayout打交到即可,而CoordinatorLayout.Behavior 相当于两个view之间的协议,即通过怎样的规则来约束双方的行为。)

设计概念:

CoordinatorLayout:CoordinatorLayout 作为最顶层视图,将负责管理所有的子view,使其内部的子View彼此间产生一种联系。这个联系通过Behavior来实现(包括了滑动状态的处理以及View状态的处理)。

AppBarLayout:AppBarLayout 继承自限性布局,作为增强版的线性布局,他增加了对滑动手势的处理。

Behavior:Behavior 是google新提出的,能够让你以非侵入式的方式去处理目标View和其他View的交互行为。Behavior需要设置在触发事件(比如滚动)的view上,且这个View必须是CoordinatorLayout的第一层级下的子view,否则没有效果,因为Behavior的初始化是在CoordinatorLayout的LayoutParams中通过反射完成的。

Behavior实例化方式:1、通过app:layout_behavior声明 ;2、在你的自定义View类上添加@DefaultBehavior(MyBehavior.class);

Behavior只是个接口,其调用是由NestedScrollingParent与NestedScrollingChild接口负责调用。

接下来我们通过阅读部分源码进行学习:

首先,我们从两个view是如何通过coordinatorlayout产生关联来入手;看代码

LayoutParams(Context context, AttributeSet attrs) {

super(context, attrs);

final TypedArray a = context.obtainStyledAttributes(attrs,

R.styleable.CoordinatorLayout_Layout);

this.gravity = a.getInteger(

R.styleable.CoordinatorLayout_Layout_android_layout_gravity,

Gravity.NO_GRAVITY);

mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_Layout_layout_anchor,

View.NO_ID);

this.anchorGravity = a.getInteger(

R.styleable.CoordinatorLayout_Layout_layout_anchorGravity,

Gravity.NO_GRAVITY);

this.keyline = a.getInteger(R.styleable.CoordinatorLayout_Layout_layout_keyline,

-1);

insetEdge = a.getInt(R.styleable.CoordinatorLayout_Layout_layout_insetEdge, 0);

dodgeInsetEdges = a.getInt(

R.styleable.CoordinatorLayout_Layout_layout_dodgeInsetEdges, 0);

mBehaviorResolved = a.hasValue(

R.styleable.CoordinatorLayout_Layout_layout_behavior);

if (mBehaviorResolved) {

mBehavior = parseBehavior(context, attrs, a.getString(

R.styleable.CoordinatorLayout_Layout_layout_behavior));

}

a.recycle();

if (mBehavior != null) {

// If we have a Behavior, dispatch that it has been attached

mBehavior.onAttachedToLayoutParams(this);

}

}

mBehaviorResolved = a.hasValue(

R.styleable.CoordinatorLayout_Layout_layout_behavior);

if (mBehaviorResolved) {

mBehavior = parseBehavior(context, attrs, a.getString(

R.styleable.CoordinatorLayout_Layout_layout_behavior));

}

这几句我们可以看到。

mBehaviorResolved 是个boolean 变量,如果

R.styleable.CoordinatorLayout_Layout_layout_behavior CoordinatorLayout的

layout_behavior这个字段设置有值,

1、mBehaviorResolved = true -》调用parseBehavior方法,将所需参数传入通过java的反射技术返回一个Behavior实例。

static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {

if (TextUtils.isEmpty(name)) {

return null;

}

final String fullName;

if (name.startsWith(".")) {

// Relative to the app package. Prepend the app package name.

fullName = context.getPackageName() + name;

} else if (name.indexOf('.') >= 0) {

// Fully qualified package name.

fullName = name;

} else {

// Assume stock behavior in this package (if we have one)

fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)

? (WIDGET_PACKAGE_NAME + '.' + name)

: name;

}

try {

Map constructors = sConstructors.get();

if (constructors == null) {

constructors &#61; new HashMap<>();

sConstructors.set(constructors);

}

Constructor c &#61; constructors.get(fullName);

if (c &#61;&#61; null) {

final Class clazz &#61; (Class) Class.forName(fullName, true,

context.getClassLoader());

c &#61; 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 " &#43; fullName, e);

}

}

通过这一段我们可以知道&#xff0c;最后是通过调用Behavior的参数为(context,attrs)的构造函数进行实例化。

实例化出Behavior之后我们会调用behavior的onAttachedToLayoutParams方法 将LayoutParams的实例对象传进去mBehavior.onAttachedToLayoutParams(this);

mBehavior.onAttachedToLayoutParams是一个当LayoutParams被实例化后的回调方法。

通过这里&#xff0c;我们的

CoordinatorLayout就能够跟用layout_behavior标识的子View产生联系。

当子View发生变化时&#xff0c;CoordinatorLayout又是如何处理的的&#xff0c;请看下面代码&#xff1a;

final void onChildViewsChanged(&#64;DispatchChangeEvent final int type) {

final int layoutDirection &#61; ViewCompat.getLayoutDirection(this);

final int childCount &#61; mDependencySortedChildren.size();

final Rect inset &#61; acquireTempRect();

final Rect drawRect &#61; acquireTempRect();

final Rect lastDrawRect &#61; acquireTempRect();

for (int i &#61; 0; i

final View child &#61; mDependencySortedChildren.get(i);

final LayoutParams lp &#61; (LayoutParams) child.getLayoutParams();

if (type &#61;&#61; EVENT_PRE_DRAW && child.getVisibility() &#61;&#61; View.GONE) {

// Do not try to update GONE child views in pre draw updates.

continue;

}

// Check child views before for anchor

for (int j &#61; 0; j

final View checkChild &#61; mDependencySortedChildren.get(j);

if (lp.mAnchorDirectChild &#61;&#61; checkChild) {

offsetChildToAnchor(child, layoutDirection);

}

}

// Get the current draw rect of the view

getChildRect(child, true, drawRect);

// Accumulate inset sizes

if (lp.insetEdge !&#61; Gravity.NO_GRAVITY && !drawRect.isEmpty()) {

final int absInsetEdge &#61; GravityCompat.getAbsoluteGravity(

lp.insetEdge, layoutDirection);

switch (absInsetEdge & Gravity.VERTICAL_GRAVITY_MASK) {

case Gravity.TOP:

inset.top &#61; Math.max(inset.top, drawRect.bottom);

break;

case Gravity.BOTTOM:

inset.bottom &#61; Math.max(inset.bottom, getHeight() - drawRect.top);

break;

}

switch (absInsetEdge & Gravity.HORIZONTAL_GRAVITY_MASK) {

case Gravity.LEFT:

inset.left &#61; Math.max(inset.left, drawRect.right);

break;

case Gravity.RIGHT:

inset.right &#61; Math.max(inset.right, getWidth() - drawRect.left);

break;

}

}

// Dodge inset edges if necessary

if (lp.dodgeInsetEdges !&#61; Gravity.NO_GRAVITY && child.getVisibility() &#61;&#61; View.VISIBLE) {

offsetChildByInset(child, inset, layoutDirection);

}

if (type &#61;&#61; EVENT_PRE_DRAW) {

// Did it change? if not continue

getLastChildRect(child, lastDrawRect);

if (lastDrawRect.equals(drawRect)) {

continue;

}

recordLastChildRect(child, drawRect);

}

// Update any behavior-dependent views for the change

for (int j &#61; i &#43; 1; j

final View checkChild &#61; mDependencySortedChildren.get(j);

final LayoutParams checkLp &#61; (LayoutParams) checkChild.getLayoutParams();

final Behavior b &#61; checkLp.getBehavior();

if (b !&#61; null && b.layoutDependsOn(this, checkChild, child)) {

if (type &#61;&#61; EVENT_PRE_DRAW && checkLp.getChangedAfterNestedScroll()) {

// If this is from a pre-draw and we have already been changed

// from a nested scroll, skip the dispatch and reset the flag

checkLp.resetChangedAfterNestedScroll();

continue;

}

final boolean handled;

switch (type) {

case EVENT_VIEW_REMOVED:

// EVENT_VIEW_REMOVED means that we need to dispatch

// onDependentViewRemoved() instead

b.onDependentViewRemoved(this, checkChild, child);

handled &#61; true;

break;

default:

// Otherwise we dispatch onDependentViewChanged()

handled &#61; b.onDependentViewChanged(this, checkChild, child);

break;

}

if (type &#61;&#61; EVENT_NESTED_SCROLL) {

// If this is from a nested scroll, set the flag so that we may skip

// any resulting onPreDraw dispatch (if needed)

checkLp.setChangedAfterNestedScroll(handled);

}

}

}

}

releaseTempRect(inset);

releaseTempRect(drawRect);

releaseTempRect(lastDrawRect);

}

我们可以看到文档说明&#xff0c;大概意思是当子view发生变化会调用该方法。该方法会遍历所有的子view&#xff0c;

然后调用如下代码&#xff0c;layoutDependsOn()这个方法是做什么的呢&#xff1f;我们接下来看下该方法。

if (b !&#61; null && b.layoutDependsOn(this, checkChild, child)) {

if (type &#61;&#61; EVENT_PRE_DRAW && checkLp.getChangedAfterNestedScroll()) {

// If this is from a pre-draw and we have already been changed

// from a nested scroll, skip the dispatch and reset the flag

checkLp.resetChangedAfterNestedScroll();

continue;

}

final boolean handled;

switch (type) {

case EVENT_VIEW_REMOVED:

// EVENT_VIEW_REMOVED means that we need to dispatch

// onDependentViewRemoved() instead

b.onDependentViewRemoved(this, checkChild, child);

handled &#61; true;

break;

default:

// Otherwise we dispatch onDependentViewChanged()

handled &#61; b.onDependentViewChanged(this, checkChild, child);

break;

}

if (type &#61;&#61; EVENT_NESTED_SCROLL) {

// If this is from a nested scroll, set the flag so that we may skip

// any resulting onPreDraw dispatch (if needed)

checkLp.setChangedAfterNestedScroll(handled);

}

}

/**

* 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 {&#64;link #onDependentViewChanged} when the dependency view&#39;s layout or * position changes.

*

*

* &#64;param parent the parent view of the given child

* &#64;param child the child view to test

* &#64;param dependency the proposed dependency of child

* &#64;return true if child&#39;s layout depends on the proposed dependency&#39;s layout,

* false otherwise

*

* &#64;see #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)

*/

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

return false;

}

这个方法&#xff0c;大概意思是如果我们返回true&#xff0c;说明当前发生变化的子view发生变化时。也就是该方法决定我们用

layout_behavior标识的view是否应该做出相应的变化。默认返回false&#xff0c;该方法需要我们在创建自己的Behavior时重写。

当返回true的话&#xff0c;可以看到会调用

b.onDependentViewRemoved(this, checkChild, child);

handled &#61; b.onDependentViewChanged(this, checkChild, child);

为了更好理解这两句代码&#xff0c;我们举个例子&#xff0c;假设有ViewA和ViewB &#xff0c;当ViewB发生移动时&#xff0c;ViewA要向反方向移动。

1、当ViewB被移除时会调用

b.onDependentViewRemoved(this, checkChild, child);

2、当ViewB发生变化时&#xff0c;会调用

handled &#61; b.onDependentViewChanged(this, checkChild, child);

那么我们就可以在

b.onDependentViewChanged里面写我们的功能代码了。

通过以上的分析&#xff0c;希望能帮到大家对CoordinatorLayout的协作调用过程有一些些的帮助。



推荐阅读
author-avatar
22222wxr
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有