Android UI界面架构

  每个Activity包含一个PhoneWindow对象,PhoneWindow设置DecorView为应用窗口的根视图。在里面就是熟悉的TitleViewContentView,没错,平时使用的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的尺寸和绘制。所以先进行measulayout(测量和定位)。如下图:

Measure过程

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//.... //回调onMeasure()方法
onMeasure(widthMeasureSpec, heightMeasureSpec); //more
}

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

    • 精确值模式,当layout_widthlayout_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?的更多相关文章

  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. margin系列之内秀篇(二)

    本系列摘自  飘零雾雨的博客 可挖掘性 之前已经写过一篇关于 margin 应用场景的文章:margin系列之内秀篇,当然,它的应用场景会远大于文中所述,无法一一列举. 所以本篇权当是对此的补遗好了, ...

  2. c 的ui

    你这些问题是必须要搞清楚的. 1. 首先,UI的实现在不同的操作系统平台上是有所不同的.Windows的UI是内置于操作系统的,Linux/Unix的UI则是通过一个应用程序实现的.由此看来,变成语言 ...

  3. C#语言特性-运算符重载

    一.C#当中可以进行重载和不可重载的运算符: 1.简单的说明: 1.从上图中可以看到,可以重载的和不可以进行重载的运算符,比较特殊的是第二行和倒数第三行,的运算符,为什么会说它们特殊,是因为(第三行) ...

  4. odoo 清除所有运行数据

    测试odoo,如果需要一个干净的db.经常需要清除掉所有业务数据.做如下操作,较为方便 1:建立一个服务器动作,动作的python代码入下. 然后新建一个菜单,菜单动作关联到 这个动作.需要清空db, ...

  5. hdu 4442

    一道超级easy的贪心 一眼看出了他的本质: 代码: #define mod 31536000 #include<cstdio> #include<algorithm> #in ...

  6. ubuntu修改grub2

    转自修改系统启动项 grub2配置的方法 ubuntu 在早期的Ubuntu中,使用Grub作为系统的启动引导程序,想修改系统启动项非常简单,只要用gedit打开系统菜单设定文件( sudo gedi ...

  7. [转贴] C++ 判断主机是否处于联网状态下

    直接让本机访问一个网站,如果成功的话,就说明成功联网,没有访问成功,则说明没有联网!!! #include<iostream> #include <WINSOCK2.H> #p ...

  8. SectionIndexer中的getSectionForPosition()与getPositionForSection()

    大家在做字母索引的时候常常会用到SectionIndexer这个类,里面有2个重要的方法 1.   getSectionForPosition()通过该项的位置,获得所在分类组的索引号 2. getP ...

  9. Android用户界面UI组件--AdapterView及其子类(四) GridView

    GridView常用的XML属性: android:columnWidth  设置列的宽度. android:horizontalSpacing  两列之间的间距.  android:numColum ...

  10. 深入浅出 - Android系统移植与平台开发(十)- Android编译系统与定制Android平台系统(瘋耔修改篇二)

    第四章.Android编译系统与定制Android平台系统 4.1Android编译系统 Android的源码由几十万个文件构成,这些文件之间有的相互依赖,有的又相互独立,它们按功能或类型又被放到不同 ...