简单了解View是What?
Android UI界面架构
每个Activity包含一个PhoneWindow
对象,PhoneWindow
设置DecorView
为应用窗口的根视图。在里面就是熟悉的TitleView
和ContentView
,没错,平时使用的setContentView()
就是设置的ContentView
。
Android是如何绘制View的?
当一个Activity启动时,会被要求绘制出它的布局。Android框架会处理这个请求,当然前提是Activity提供了合理的布局。绘制从根视图开始,从上至下遍历整棵视图树,每一个ViewGroup
负责让自己的子View
被绘制,每一个View
负责绘制自己,通过draw()
方法.绘制过程分三步走。
- Measure
- Layout
- Draw
整个绘制流程是在ViewRoot
中的performTraversals()
方法展开的。部分源代码如下:
- private void performTraversals() {
- ......
- //最外层的根视图的widthMeasureSpec和heightMeasureSpec由来
- //lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT
- int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
- int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
- ......
- mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
- ......
- mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
- ......
- mView.draw(canvas);
- ......
- }
在绘制之前当然要知道view的尺寸和绘制。所以先进行measu
和layout
(测量和定位)。如下图:
Measure过程
- public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
- //....
- //回调onMeasure()方法
- onMeasure(widthMeasureSpec, heightMeasureSpec);
- //more
- }
EXACTLY计算view的实际大小,获得高宽存入mMeasuredHeight
和mMeasureWidth
,measure(int, int)
传入的两个参数。MeasureSpec
是一个32位int值,高2位为测量的模式,低30位为测量的大小。测量的模式可以分为以下三种。
精确值模式,当
layout_width
或layout_height
指定为具体数值,或者为match_parent
时,系统使用EXACTLY。AT_MOST 最大值模式,指定为
wrap_content
时,控件的尺寸不能超过父控件允许的最大尺寸。UNSPECIFIED 不指定测量模式,View想多大就多大,一般不太使用。
根据上面的源码可知,measure方法不可被重写,自定义时需要重写的是onMeasure
方法
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
- getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
- }
查看源码可知最终的高宽是调用setMeasuredDimension()
设定的,如果不重写,默认是直接调用getDefaultSize
获取尺寸的。
使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。
Layout过程
Layout方法就是用来确定view布局的位置,就好像你知道了一件东西的大小以后,总要知道位置才能画上去。
mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
layout获取四个参数,左,上,右,下坐标,相对于父视图而言。这里可以看到,使用了刚刚测量的宽和高。
- public void layout(int l, int t, int r, int b) {
- int oldL = mLeft;
- int oldT = mTop;
- int oldB = mBottom;
- int oldR = mRight;
- boolean changed = setFrame(l, t, r, b);
- if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
- .....
- onLayout(changed, l, t, r, b);
- .....
- }
当layout结束以后getWidth()
与getHeight()
才会返回正确的值。通过setFrame
设置坐标。如果坐标改变过了,则重新进行定位。如果是View对象,那么onLayout是个空方法。因为定位是由ViewGroup确定的。
这里出现一个问题,getWidth/Height()
and getMeasuredWidth/Height()
有什么区别?
getWidth()
:View在設定好佈局後整個View的寬度。getMeasuredWidth()
:對View上的內容進行測量後得到的View內容佔據的寬度
Draw过程
- public void draw(Canvas canvas) {
- ......
- /*
- * Draw traversal performs several drawing steps which must be executed
- * in the appropriate order:
- *
- * 1. Draw the background
- * 2. If necessary, save the canvas' layers to prepare for fading
- * 3. Draw view's content
- * 4. Draw children
- * 5. If necessary, draw the fading edges and restore layers
- * 6. Draw decorations (scrollbars for instance)
- */
- // Step 1, draw the background, if needed
- ......
- if (!dirtyOpaque) {
- drawBackground(canvas);
- }
- // skip step 2 & 5 if possible (common case)
- ......
- // Step 2, save the canvas' layers
- ......
- if (drawTop) {
- canvas.saveLayer(left, top, right, top + length, null, flags);
- }
- ......
- // 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
- ......
- if (drawTop) {
- matrix.setScale(1, fadeHeight * topFadeStrength);
- matrix.postTranslate(left, top);
- fade.setLocalMatrix(matrix);
- p.setShader(fade);
- canvas.drawRect(left, top, right, top + length, p);
- }
- ......
- // Step 6, draw decorations (scrollbars)
- onDrawScrollBars(canvas);
- ......
- }
重点是第三步调用onDraw方法。其它几步都是绘制一些边边角角的东西比如背景、scrollBar之类的。其中dispatchDraw
,是用来递归调用子View,如果没有则不需要。
onDraw方法是需要自己实现的,因为每个控件绘制的内容不同。主要用canvas对象进行绘制。
简单了解View是What?的更多相关文章
- 使用属性动画简单实现view飞入效果
比较简单的效果,可以用来菜单飞入之类,作为记录吧, package com.test.animation; import android.app.Activity; import android.os ...
- iOS开发-简单获取View截图图像(Quartz2D)
1. 先指定图像的大小 UIGraphicsBeginImageContext(view.frame.size); 2. 在指定的区域绘制图像 [view drawViewHierarchyInRec ...
- Android Scroller简单用法 --View滚动
转:http://ipjmc.iteye.com/blog/1615828 Android里Scroller类是为了实现View平滑滚动的一个Helper类.通常在自定义的View时使用,在View中 ...
- iOS 简单提示view
+(void)showMessage:(NSString *)message{ UIWindow * window = [UIApplication sharedApplication].key ...
- 简单模板view调用
$this->display();//模板名称与当前控制器.当前操作方法的名称不一致 $this->display();//调用当前控制器对应目录指定名称的模板 $this->dis ...
- 简单说说Android自定义view学习推荐的方式
这几天比较受关注,挺开心的,嘿嘿. 这里给大家总结一下学习自定义view的一些技巧. 以后写自定义view可能不会写博客了,但是可以开源的我会把源码丢到github上我的地址:https://git ...
- IOS Animation-贝塞尔曲线与Layer简单篇(一)
IOS Animation-贝塞尔曲线与Layer简单篇 swift篇 1.介绍 贝塞尔曲线: 贝塞尔曲线是计算机图形图像造型的基本工具,是图形造型运用得最多的基本线条之一.它通过控制曲线上的四个点( ...
- View的onSaveInstanceState和onRestoreInstanceState过程分析
为什么要介绍这2个方法呢?这是因为在我们的开发中最近遇到了一个很诡异的bug.大体是这样的:在我们的ViewPager中 有2页的root view都是ScrollView,我们在xml里面都用了an ...
- Android -- 自定义View小Demo,绘制四位数随机码(一)
1,现在有这样一个需求,实现显示随机随机数可能在代码中直接很简单的就实现了,但是现在我们直接自定义View来实现这个效果,那么我们来分析一波吧,我们允许开发者自己设置这个textview的大小,颜色, ...
随机推荐
- hbuilder工具快捷键 http://www.qq210.com/shoutu/android
http://www.qq210.com/shoutu/android 创建HTML结构: h 8 (敲h激活代码块列表,按8选择第8个项目,即HTML代码块,或者敲h t Enter)中途换行: ' ...
- Uniqueidentifier数据类型
一.Uniqueidentifier数据类型 可存储16字节的二进制值 Uniqueidentifier用来存储一个全局唯一标识符,即GUID.GUID是唯一的二进制数:世界上的任何两台计算机都不会生 ...
- html网页音乐播放器自带播放列表
基于网页的音乐播放器demo http://pan.baidu.com/s/1dDgm7HR 自己diy了一个手机端在线音乐播放器演示地址http://shanxi2014.com/zhuandiz ...
- python【第二十篇】Django表的多对多、Ajax
1 创建多对多表的方式有两种 1.1 方式一:自定义关系表 class Host(models.Model): nid = models.AutoField(primary_key=True) hos ...
- 关于Java(不同工具或平台与“Hello World”)
对于任何编程语言,都最常见的入门应用: Hello World NetBeans 和 “Hello World” 编写 Java 程序前,先要准备好: Java SE Development Kit ...
- JavaScript学习代码整理(一)
/** * Created by wyl on 15-1-23. */ function displayDate() { document.getElementById("demo" ...
- CENTOS6上禁用IPV6和DHCP
centos 6禁用ipv6方法 首先,在终端输入 ip -6 addr show 和 lsmod | grep ipv6查询一下有无安装IPV6模块,或用netstat -nuptl查看是否有i ...
- myeclipse启动报“java was started but returned exit code=13”
在win8系统中的myeclipse拷贝到win7系统中后,解压缩打开提示"java was started but returned exit code=13", 可能是myec ...
- 使用Spring AOP预处理Controller的参数
实际编程中,可能会有这样一种情况,前台传过来的参数,我们需要一定的处理才能使用,比如有这样一个Controller @Controller public class MatchOddsControll ...
- iOS -view横向变成竖向
-------