热门标签 | 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的协作调用过程有一些些的帮助。



推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • (三)多表代码生成的实现方法
    本文介绍了一种实现多表代码生成的方法,使用了java代码和org.jeecg框架中的相关类和接口。通过设置主表配置,可以生成父子表的数据模型。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • 面向对象之3:封装的总结及实现方法
    本文总结了面向对象中封装的概念和好处,以及在Java中如何实现封装。封装是将过程和数据用一个外壳隐藏起来,只能通过提供的接口进行访问。适当的封装可以提高程序的理解性和维护性,增强程序的安全性。在Java中,封装可以通过将属性私有化并使用权限修饰符来实现,同时可以通过方法来访问属性并加入限制条件。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
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社区 版权所有