相关文章

Android View体系(一)视图坐标系

Android View体系(二)实现View滑动的六种方法

Android View体系(三)属性动画

Android View体系(四)从源代码解析Scroller

Android View体系(五)从源代码解析View的事件分发机制

Android View体系(六)从源代码解析Activity的构成

Android View体系(七)从源代码解析View的measure流程

前言

上一篇文章我们讲了View的measure的流程。接下来我们讲下View的layout和draw流程,假设你理解了View的measure的流程。那这篇文章自然就不在话下了。

1.View的layout流程

先来看看View的layout()方法:

 public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight; boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
} mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}

传进来里面的四个參数各自是View的四个点的坐标。它的坐标不是相对屏幕的原点,并且相对于它的父布局来说的。

l 和 t 是子控件左边缘和上边缘相对于父类控件左边缘和上边缘的距离。

r 和 b是子控件右边缘和下边缘相对于父类控件左边缘和上边缘的距离。来看看setFrame()方法里写了什么:

    protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false; if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
} if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true; // Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN; int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); // Invalidate our old position
invalidate(sizeChanged); mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom); ...省略
}
return changed;
}

在setFrame()方法里主要是用来设置View的四个顶点的值,也就是mLeft 、mTop、mRight和 mBottom的值。在调用setFrame()方法后,调用onLayout()方法:

  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

onLayout()方法没有去做什么,这个和onMeasure()方法相似,确定位置时依据不同的控件有不同的实现,所以在View和ViewGroup中均没有实现onLayout()方法。

既然这样,我们就来看看LinearLayout的onLayout()方法:

  @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mOrientation == VERTICAL) {
layoutVertical(l, t, r, b);
} else {
layoutHorizontal(l, t, r, b);
}
}

layoutVertical做了什么呢?

 void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft; int childTop;
int childLeft; // Where right end of child should go
final int width = right - left;
int childRight = width - mPaddingRight; // Space available for child
int childSpace = width - paddingLeft - mPaddingRight; final int count = getVirtualChildCount(); final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; switch (majorGravity) {
case Gravity.BOTTOM:
// mTotalLength contains the padding already
childTop = mPaddingTop + bottom - top - mTotalLength;
break; // mTotalLength contains the padding already
case Gravity.CENTER_VERTICAL:
childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
break; case Gravity.TOP:
default:
childTop = mPaddingTop;
break;
} for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null) {
childTop += measureNullChild(i);
} else if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight(); final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams(); int gravity = lp.gravity;
if (gravity < 0) {
gravity = minorGravity;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = paddingLeft + ((childSpace - childWidth) / 2)
+ lp.leftMargin - lp.rightMargin;
break; case Gravity.RIGHT:
childLeft = childRight - childWidth - lp.rightMargin;
break; case Gravity.LEFT:
default:
childLeft = paddingLeft + lp.leftMargin;
break;
} if (hasDividerBeforeChildAt(i)) {
childTop += mDividerHeight;
} childTop += lp.topMargin;
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child, i);
}
}
}

这种方法会遍历子元素并调用setChildFrame()方法:

  private void setChildFrame(View child, int left, int top, int width, int height) {
child.layout(left, top, left + width, top + height);
}

在setChildFrame()方法中调用子元素的layout()方法来确定自己的位置。我们看到childTop这个值是逐渐增大的,这是为了在垂直方向,子元素是一个接一个排列的而不是重叠的。

2.View的draw流程

View的draw流程非常easy。先来看看View的draw()方法:

 public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; // Step 1, draw the background, if needed
int saveCount; if (!dirtyOpaque) {
drawBackground(canvas);
}
...省略
// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft; final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
...省略
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children
dispatchDraw(canvas);
...省略
// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
...省略
// Step 6, draw decorations (scrollbars)
onDrawScrollBars(canvas); if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
}

从源代码的凝视我们看到draw流程有六个步骤。当中第2步和第5步能够跳过:

  1. 假设有设置背景。则绘制背景
  2. 保存canvas层
  3. 绘制自身内容
  4. 假设有子元素则绘制子元素
  5. 绘制效果
  6. 绘制装饰品(scrollbars)

好了,关于View的工作流程就说到这里了,接下来会讲到自己定义View。

Android View体系(八)从源代码解析View的layout和draw流程的更多相关文章

  1. Android View体系(八)从源码解析View的layout和draw流程

    前言 上一篇文章我们讲了View的measure的流程,接下来我们讲下View的layout和draw流程,如果你理解了View的measure的流程,那这篇文章自然就不在话下了. 1.View的la ...

  2. Android View体系(九)自定义View

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  3. Android View 事件分发机制 源代码解析 (上)

    一直想写事件分发机制的文章,无论咋样,也得自己研究下事件分发的源代码.写出心得~ 首先我们先写个简单的样例来測试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个M ...

  4. 源码分析篇 - Android绘制流程(二)measure、layout、draw流程

    performTraversals方法会经过measure.layout和draw三个流程才能将一帧View需要显示的内容绘制到屏幕上,用最简化的方式看ViewRootImpl.performTrav ...

  5. Android View体系(二)实现View滑动的六种方法

    1.View的滑动简介 View的滑动是Android实现自定义控件的基础,同时在开发中我们也难免会遇到View的滑动的处理.其实不管是那种滑动的方式基本思想都是类似的:当触摸事件传到View时,系统 ...

  6. Android View体系(四)从源码解析Scroller

    在Android View体系(二)实现View滑动的六种方法这篇文章中我们讲到了用Scroller来实现View的滑动,所以这篇文章我们就不介绍Scroller是如何使用的了,本篇就从源码来分析下S ...

  7. Android View体系(十)自定义组合控件

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  8. Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)

    View 的绘制系列文章: Android View 的绘制流程之 Measure 过程详解 (一) Android View 绘制流程之 DecorView 与 ViewRootImpl 在上一篇  ...

  9. Android View框架总结(三)View工作原理

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52180375 测量/布局/绘制顺序 如何引起View的测量/布局/绘制? Perfor ...

随机推荐

  1. Python使用matplotlib模块绘制多条折线图、散点图

    用matplotlib模块 #!usr/bin/env python #encoding:utf-8 ''' __Author__:沂水寒城 功能:折线图.散点图测试 ''' import rando ...

  2. 跟我学SharePoint 2013视频培训课程——删除恢复、文档离线工作(11)

    课程简介 第11天,怎样在SharePoint 2013中删除.恢复文档.文档离线工作. 视频 SharePoint 2013 交流群 41032413

  3. Python 文件 readline() 方法

    描述 Python 文件 readline() 方法用于从文件读取整行,包括 "\n" 字符.如果指定了一个非负数的参数,则返回指定大小的字符数,包括 "\n" ...

  4. jdk动态代理与cglib动态代理例子

    1.JAVA的动态代理特征:特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代理类的对象 ...

  5. nodejs 最受欢迎的orm sequelize

    传送门 # 视频教程 https://nodelover.me/course/sequelize/ # 官方文档 http://docs.sequelizejs.com/manual/tutorial ...

  6. 转 阿里Dubbo疯狂更新,关Spring Cloud什么事?

    原文地址: http://www.ityouknow.com/springcloud/2017/11/20/dubbo-update-again.html阿里Dubbo疯狂更新,关Spring Clo ...

  7. Knockout: 使用knockout validation插件进行校验, 给未通过校验的输入框添加红色边框突出显示.

    之前整理了三篇帖子: Knockout: 使用CSS绑定和event的blur失去焦点事件, 给未通过校验的输入框添加红色边框突出显示. http://www.cnblogs.com/liuzhend ...

  8. java Web的MVC最基础暂定分层包

    数据封装 entity, 数据访问对象  Dao, 业务逻辑 servlet, ----- 网页显示层 jsp 加上 jsp附带技术 el表达式和jstl ,jsp 标记库语言, el表达式 ${us ...

  9. JDK1.6新特性,基础类库篇,IO支持

    1. JDK1.6中提供了java.io.Console类 JDK1.6中提供了java.io.Console 类专用来访问基于字符的控制台设备.你的程序如果要与Windows下的cmd或者Linux ...

  10. Python无限元素列表实例教程

    有关Python中无限元素列表的实现方法. 本文实例讲述了Python怎么实现无限元素列表的方法,具体实现可使用Yield来完成.下面所述的2段实例代码通过Python Yield 生成器实现了简单的 ...