Android-View的绘制源码学习总结

##前言

算是第一篇正式的github博文,回顾了一下之前看过的view源码解析,做一个对目前为止View学习小的总结。

我觉得对于源码的解析和学习,把所有流程记下来意义并不是很大,最关键的是:

1.知道基本作用和用法
2.大概了解整个流程和实现方法
3.了解里面可扩展的地方在哪,更灵活地使用
4.整个源码设计和细节有没有什么亮点值得参考和学习
5.源码设计的思路

这也是写这篇文章的目的所在。

##加载布局

####LayoutInflater

http://www.cnblogs.com/qlky/p/5674975.html

- **作用**

LayoutInflater是用来加载布局的,我们最简单的代码:

```
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainlayout);
}
```

中的setContentView就是用LayoutInflater实现的

- **用法**

可以用来动态加载布局
```
LayoutInflater layoutInflater = LayoutInflater.from(this);
View buttonlayout = layoutInflater.inflate(R.layout.button_layout,null);
mainlayout.addView(buttonlayout);
```

- **原理**

inflate是用pull来解析xml格式的,其中调用了createViewFromTag()这个方法,并把节点名和参数传了进去。看到这个方法名,我们就应该能猜到,它是用于根据节点名来创建View对象的。
确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。

对于布局子元素会用rinflate()去循环实现。

- **拓展**

从源码可以知道

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)的第三个参数attachToRoot

1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。

2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。

3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。

4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。

所有控件的layout_width等属性,都要有一个父布局才能生效,因为这是用来设置view在布局中的大小,而不是view的大小,所以叫layout_width,不是width。

而LinearLayout的layout_width有效是因为,在setContentView()方法中,Android会自动在布局文件的最外层再嵌套一个FrameLayout,id为content

所以叫setContentView(),其实这个方法也是用Layoutlnflater()实现的

##视图绘制

http://www.cnblogs.com/qlky/p/5676578.html
http://www.jianshu.com/p/5a71014e7b1b

View的绘制要经过三个过程,measure,layout和draw

####Measure

- **作用**

测量view及其子view的大小

- **原理**

MeasureSpec:由父View的MeasureSpec和子View的LayoutParams通过简单的计算得出一个针对子View的测量要求

计算原理很简单:
如果我们在xml 的layout_width或者layout_height 把值都写死,那么上述的测量完全就不需要了,之所以要上面的这步测量,是因为 match_parent 就是充满父容器,wrap_content 就是自己多大就多大, 我们写代码的时候特别爽,我们编码方便的时候,google就要帮我们计算你match_parent的时候是多大,wrap_content的是多大,这个计算过程,就是计算出来的父View的MeasureSpec不断往子View传递,结合子View的LayoutParams 一起再算出子View的MeasureSpec,然后继续传给子View,不断计算每个View的MeasureSpec,子View有了MeasureSpec才能更测量自己和自己的子View。

几种情况:
1.父view MeasureSpec = exactly 大小确定,子view:
match_parent:EXACTLY
warp_content:AT MOST
确定值

2.父view MeasureSpec = exactly 大小不确定,子view:
match_parent:AT MOST
warp_content:AT MOST
确定值

View的测量主要在onMeasure方法里

对于View默认是测量很简单,大部分情况就是拿计算出来的MeasureSpec的size 当做最终测量的大小。而对于其他的一些View的派生类,如TextView、Button、ImageView等,它们的onMeasure方法系统了都做了重写,不会这么简单直接拿 MeasureSpec 的size来当大小,而去会先去测量字符或者图片的高度等,然后拿到View本身content这个高度(字符高度等),如果MeasureSpec是AT_MOST,而且View本身content的高度不超出MeasureSpec的size,那么可以直接用View本身content的高度(字符高度等),而不是像View.java 直接用MeasureSpec的size做为View的大小。

整个View的Root是DecorView,那么View的绘制是从哪里开始的呢,我们知道每个Activity 均会创建一个 PhoneWindow对象,是Activity和整个View系统交互的接口,每个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,对于Activity来说,ViewRootImpl是连接WindowManager和DecorView的纽带,绘制的入口是由ViewRootImpl的performTraversals方法来发起Measure,Layout,Draw等流程的。

具体流程: DecorView -> ... -> content -> LinearLayout/... -> TextView/...
得到每层的MeasureSpec,最后在底层得到大小。然后再往上得到父view的大小

- **拓展**

对于onMeasure方法的重写

####Layout

- **作用**

确定view在布局中的位置。ViewRoot的performTraversals()方法会在measure结束后继续执行,并调用View的layout()方法来执行此过程,如下所示:

```
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
```

- **原理**

首先会调用setFrame()方法来判断视图的大小是否发生过变化,以确定有没有必要对当前的视图进行重绘,同时还会在这里把传递过来的四个参数分别赋值给mLeft、mTop、mRight和mBottom这几个变量。
接下来会调用onLayout()方法。
View中的onLayout()方法是一个空方法,因为onLayout()过程是为了确定视图在布局中所在的位置,而这个操作应该是由布局来完成的,即父视图决定子视图的显示位置。
ViewGroup中的onLayout()方法是一个抽象方法,子类必须重载onLayout函数,而重载onLayout的目的就是安排其children在父视图的具体位置

- **拓展**

重载onLayout函数

####Draw

- **作用**
绘制view

- **原理**

分为六步,其中第二步和第五步在一般情况下很少用到

第一步:背景绘制

第三步,对View的内容进行绘制
onDraw(canvas) 方法是view用来draw 自己的,具体如何绘制,颜色线条什么样式就需要子View自己去实现,View.java 的onDraw(canvas) 是空实现,ViewGroup 也没有实现,每个View的内容是各不相同的,所以需要由子类去实现具体逻辑。

第四步 对当前View的所有子View进行绘制
dispatchDraw(canvas) 方法是用来绘制子View的,View.java 的dispatchDraw()方法是一个空方法,因为View没有子View,不需要实现dispatchDraw ()方法,ViewGroup就不一样了,它实现了dispatchDraw ()方法,就是遍历子View然后drawChild(),drawChild()方法实际调用的是子View.draw()方法,ViewGroup类已经为我们实现绘制子View的默认过程,这个实现基本能满足大部分需求,所以ViewGroup类的子类(LinearLayout,FrameLayout)也基本没有去重写dispatchDraw方法,我们在实现自定义控件,除非比较特别,不然一般也不需要去重写它

第6步 对View的滚动条进行绘制

- **拓展**

重写onDraw方法

时间: 2024-08-06 19:56:44

Android-View的绘制源码学习总结的相关文章

[Android FrameWork 6.0源码学习] View的重绘过程之WindowManager的addView方法

博客首页:http://www.cnblogs.com/kezhuang/p/ 关于Activity的contentView的构建过程,我在我的博客中已经分析过了,不了解的可以去看一下 <[Android FrameWork 6.0源码学习] Window窗口类分析> 本章博客是接着上边那篇博客分析,目的是为了引出分析ViewRootImpl这个类.现在只是分析完了Window和ActivityThread的调用过程 从ActivityThread到WindowManager再到ViewRoo

[Android阅读代码]android-async-http源码学习一

android-async-http 下载地址 一个比较常用的Http请求库,基于org.apache.http对http操作进行封装. 特点: 1.每一个HTTP请求发生在UI线程之外,Client通过回调处理HTTP请求的结果,使得Client代码逻辑清晰 2.每一个请求使用线程池管理执行 3.支持gzip , cookie等功能 4.支持自动重试连接功能 [Android阅读代码]android-async-http源码学习一,布布扣,bubuko.com

[Android FrameWork 6.0源码学习] View的重绘过程之Draw

View绘制的三部曲,测量,布局,绘画现在我们分析绘画部分测量和布局 在前两篇文章中已经分析过了.不了解的可以去我的博客里找一下 下面进入正题,开始分析调用以及函数原理 private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; } final boolean fullRedrawNeeded = mFullRedrawNeede

[Android FrameWork 6.0源码学习] ViewGroup的addView函数分析

Android中整个的View的组装是采用组合模式. ViewGroup就相当与树根,各种Layout就相当于枝干,各种子View,就相当于树叶. 至于View类.我们就当它是个种子吧.哈哈! ViewGroup属于树根,可以生长数很多枝干(继承自定义Layout)而枝干上有可以长出很多叶子(TextView,ImageVIew......) 好,闲话少叙,接下来步入正题! 首先,关于View的操作方法,被定义在一个叫做ViewManager的接口中,接口中还有两个方法,分别是移除和更新,这次主

[Android FrameWork 6.0源码学习] LayoutInflater 类分析

LayoutInflater是用来解析XML布局文件,然后生成对象的ViewTree的工具类.是这个工具类的存在,才能让我们写起Layout来那么省劲. 我们接下来进去刨析,看看里边的奥秘 //调用inflate方法就可以把XML解析成View对象 View contentView = LayoutInflater.from(this).inflate(R.layout.activity_main, null); 我们在使用这个类的时候,通常都是像上面这样写,首先通过from函数获取对象,在调用

[Android FrameWork 6.0源码学习] View的重绘过程

View绘制的三部曲,  测量,布局,绘画今天我们分析测量过程 view的测量是从ViewRootImpl发起的,View需要重绘,都是发送请求给ViewRootImpl,然后他组织重绘在重绘的过程中,有一步就是测量,通过代码来分析测量过程 private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWind

[Android FrameWork 6.0源码学习] View的重绘过程之Layout

View绘制的三部曲,测量,布局,绘画现在我们分析布局部分测量部分在上篇文章中已经分析过了.不了解的可以去我的博客里找一下 View的布局和测量一样,都是从ViewRootImpl中发起,ViewRootImpl先通过measure来初始化整个的view树之后会调用onLayout方法来布局,ViewRootImpl是通过performLayout函数来发起重绘的比较重要的部分我会写注释,注意看注释就行 private void performLayout(WindowManager.Layou

Android - View的绘制你知道多少?

https://github.com/android-cn/android-open-project-analysis/tree/master/tech/viewdrawflow Android应用层View绘制流程与源码分析 Android-View的绘制源码学习总结 原文地址:https://www.cnblogs.com/qlky/p/10676093.html

Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习

package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.View; public class UnderstandDispatchTouchEvent { /** * dispatchTouchEvent()源码学习及其注释 * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchE