Android UI界面架构

  每个Activity包含一个PhoneWindow对象,PhoneWindow设置DecorView为应用窗口的根视图。在里面就是熟悉的TitleViewContentView,没错,平时使用的setContentView()就是设置的ContentView

Android是如何绘制View的?

  当一个Activity启动时,会被要求绘制出它的布局。Android框架会处理这个请求,当然前提是Activity提供了合理的布局。绘制从根视图开始,从上至下遍历整棵视图树,每一个ViewGroup负责让自己的子View被绘制,每一个View负责绘制自己,通过draw()方法.绘制过程分三步走。

    • Measure
    • Layout
    • Draw

  整个绘制流程是在ViewRoot中的performTraversals()方法展开的。部分源代码如下:

  1. private void performTraversals() {
  2. ......
  3. //最外层的根视图的widthMeasureSpec和heightMeasureSpec由来
  4. //lp.width和lp.height在创建ViewGroup实例时等于MATCH_PARENT
  5. int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
  6. int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
  7. ......
  8. mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  9. ......
  10. mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
  11. ......
  12. mView.draw(canvas);
  13. ......
  14. }

  在绘制之前当然要知道view的尺寸和绘制。所以先进行measulayout(测量和定位)。如下图:

Measure过程

  1. public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  2. //....
  3.  
  4. //回调onMeasure()方法
  5. onMeasure(widthMeasureSpec, heightMeasureSpec);
  6.  
  7. //more
  8. }

  EXACTLY计算view的实际大小,获得高宽存入mMeasuredHeightmMeasureWidthmeasure(int, int)传入的两个参数。MeasureSpec是一个32位int值,高2位为测量的模式,低30位为测量的大小。测量的模式可以分为以下三种。

    • 精确值模式,当layout_widthlayout_height指定为具体数值,或者为match_parent时,系统使用EXACTLY。

    • AT_MOST  最大值模式,指定为wrap_content时,控件的尺寸不能超过父控件允许的最大尺寸。

    • UNSPECIFIED  不指定测量模式,View想多大就多大,一般不太使用。

  根据上面的源码可知,measure方法不可被重写,自定义时需要重写的是onMeasure方法

  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
  3. getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  4. }

    查看源码可知最终的高宽是调用setMeasuredDimension()设定的,如果不重写,默认是直接调用getDefaultSize获取尺寸的。

    使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

Layout过程

  Layout方法就是用来确定view布局的位置,就好像你知道了一件东西的大小以后,总要知道位置才能画上去。

    mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());

  layout获取四个参数,左,上,右,下坐标,相对于父视图而言。这里可以看到,使用了刚刚测量的宽和高。

  1. public void layout(int l, int t, int r, int b) {
  2. int oldL = mLeft;
  3. int oldT = mTop;
  4. int oldB = mBottom;
  5. int oldR = mRight;
  6. boolean changed = setFrame(l, t, r, b);
  7. if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
  8. .....
  9. onLayout(changed, l, t, r, b);
  10. .....
  11. }

  当layout结束以后getWidth()getHeight()才会返回正确的值。通过setFrame设置坐标。如果坐标改变过了,则重新进行定位。如果是View对象,那么onLayout是个空方法。因为定位是由ViewGroup确定的。

这里出现一个问题,getWidth/Height() and getMeasuredWidth/Height()有什么区别?

  • getWidth():View在設定好佈局後整個View的寬度。
  • getMeasuredWidth():對View上的內容進行測量後得到的View內容佔據的寬度

Draw过程

  1. public void draw(Canvas canvas) {
  2. ......
  3. /*
  4. * Draw traversal performs several drawing steps which must be executed
  5. * in the appropriate order:
  6. *
  7. * 1. Draw the background
  8. * 2. If necessary, save the canvas' layers to prepare for fading
  9. * 3. Draw view's content
  10. * 4. Draw children
  11. * 5. If necessary, draw the fading edges and restore layers
  12. * 6. Draw decorations (scrollbars for instance)
  13. */
  14.  
  15. // Step 1, draw the background, if needed
  16. ......
  17. if (!dirtyOpaque) {
  18. drawBackground(canvas);
  19. }
  20.  
  21. // skip step 2 & 5 if possible (common case)
  22. ......
  23.  
  24. // Step 2, save the canvas' layers
  25. ......
  26. if (drawTop) {
  27. canvas.saveLayer(left, top, right, top + length, null, flags);
  28. }
  29. ......
  30.  
  31. // Step 3, draw the content
  32. if (!dirtyOpaque) onDraw(canvas);
  33.  
  34. // Step 4, draw the children
  35. dispatchDraw(canvas);
  36.  
  37. // Step 5, draw the fade effect and restore layers
  38. ......
  39. if (drawTop) {
  40. matrix.setScale(1, fadeHeight * topFadeStrength);
  41. matrix.postTranslate(left, top);
  42. fade.setLocalMatrix(matrix);
  43. p.setShader(fade);
  44. canvas.drawRect(left, top, right, top + length, p);
  45. }
  46. ......
  47.  
  48. // Step 6, draw decorations (scrollbars)
  49. onDrawScrollBars(canvas);
  50. ......
  51. }

  重点是第三步调用onDraw方法。其它几步都是绘制一些边边角角的东西比如背景、scrollBar之类的。其中dispatchDraw,是用来递归调用子View,如果没有则不需要。

onDraw方法是需要自己实现的,因为每个控件绘制的内容不同。主要用canvas对象进行绘制。

简单了解View是What?的更多相关文章

  1. 使用属性动画简单实现view飞入效果

    比较简单的效果,可以用来菜单飞入之类,作为记录吧, package com.test.animation; import android.app.Activity; import android.os ...

  2. iOS开发-简单获取View截图图像(Quartz2D)

    1. 先指定图像的大小 UIGraphicsBeginImageContext(view.frame.size); 2. 在指定的区域绘制图像 [view drawViewHierarchyInRec ...

  3. Android Scroller简单用法 --View滚动

    转:http://ipjmc.iteye.com/blog/1615828 Android里Scroller类是为了实现View平滑滚动的一个Helper类.通常在自定义的View时使用,在View中 ...

  4. iOS 简单提示view

    +(void)showMessage:(NSString *)message{    UIWindow * window = [UIApplication sharedApplication].key ...

  5. 简单模板view调用

    $this->display();//模板名称与当前控制器.当前操作方法的名称不一致 $this->display();//调用当前控制器对应目录指定名称的模板 $this->dis ...

  6. 简单说说Android自定义view学习推荐的方式

    这几天比较受关注,挺开心的,嘿嘿. 这里给大家总结一下学习自定义view的一些技巧.  以后写自定义view可能不会写博客了,但是可以开源的我会把源码丢到github上我的地址:https://git ...

  7. IOS Animation-贝塞尔曲线与Layer简单篇(一)

    IOS Animation-贝塞尔曲线与Layer简单篇 swift篇 1.介绍 贝塞尔曲线: 贝塞尔曲线是计算机图形图像造型的基本工具,是图形造型运用得最多的基本线条之一.它通过控制曲线上的四个点( ...

  8. View的onSaveInstanceState和onRestoreInstanceState过程分析

    为什么要介绍这2个方法呢?这是因为在我们的开发中最近遇到了一个很诡异的bug.大体是这样的:在我们的ViewPager中 有2页的root view都是ScrollView,我们在xml里面都用了an ...

  9. Android -- 自定义View小Demo,绘制四位数随机码(一)

    1,现在有这样一个需求,实现显示随机随机数可能在代码中直接很简单的就实现了,但是现在我们直接自定义View来实现这个效果,那么我们来分析一波吧,我们允许开发者自己设置这个textview的大小,颜色, ...

随机推荐

  1. hbuilder工具快捷键 http://www.qq210.com/shoutu/android

    http://www.qq210.com/shoutu/android 创建HTML结构: h 8 (敲h激活代码块列表,按8选择第8个项目,即HTML代码块,或者敲h t Enter)中途换行: ' ...

  2. Uniqueidentifier数据类型

    一.Uniqueidentifier数据类型 可存储16字节的二进制值 Uniqueidentifier用来存储一个全局唯一标识符,即GUID.GUID是唯一的二进制数:世界上的任何两台计算机都不会生 ...

  3. html网页音乐播放器自带播放列表

    基于网页的音乐播放器demo  http://pan.baidu.com/s/1dDgm7HR 自己diy了一个手机端在线音乐播放器演示地址http://shanxi2014.com/zhuandiz ...

  4. python【第二十篇】Django表的多对多、Ajax

    1 创建多对多表的方式有两种 1.1 方式一:自定义关系表 class Host(models.Model): nid = models.AutoField(primary_key=True) hos ...

  5. 关于Java(不同工具或平台与“Hello World”)

    对于任何编程语言,都最常见的入门应用: Hello World NetBeans 和 “Hello World” 编写 Java 程序前,先要准备好: Java SE Development Kit ...

  6. JavaScript学习代码整理(一)

    /** * Created by wyl on 15-1-23. */ function displayDate() { document.getElementById("demo" ...

  7. CENTOS6上禁用IPV6和DHCP

    centos 6禁用ipv6方法   首先,在终端输入 ip -6 addr show 和 lsmod | grep ipv6查询一下有无安装IPV6模块,或用netstat -nuptl查看是否有i ...

  8. myeclipse启动报“java was started but returned exit code=13”

    在win8系统中的myeclipse拷贝到win7系统中后,解压缩打开提示"java was started but returned exit code=13", 可能是myec ...

  9. 使用Spring AOP预处理Controller的参数

    实际编程中,可能会有这样一种情况,前台传过来的参数,我们需要一定的处理才能使用,比如有这样一个Controller @Controller public class MatchOddsControll ...

  10. iOS -view横向变成竖向

    -------