第四章 View的工作原理

    4.1初识ViewRoot和DecorView

    ViewRoot是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的。

    View的三大流程指的是测量(measure),布局(layout),绘制(draw)。

    measure用来测量View的宽和高,layout用来确定View在父容器中的放置位置,draw负责将View绘制在屏幕上。

 
   measure过程决定了View的宽和高,Measure完成以后,可以通过getMeasuredWidth和
getMeasuredHeight方法来获取View测量后的宽和高;Layout过程决定了View的四个顶点的坐标和实际的View宽高,可通过
getTop、getBottom、getLeft、getRight来获取View的四个顶点的位置,并可通过getWidth和getHeight方
法来获取View的最终宽高。Draw过程决定了View的显示,只有draw方法完成以后View的内容才能呈现在屏幕上。

    DecorView是顶级的View;获取通过setContentView方法所设置的View的方法如下:

    View contentView=findViewById(ViewGroup)findViewById(android.R.id.content).getChildAt(0);

    DecorView其实是一个FrameLayout,View层的事件都先经过DecorView,然后才传递给我们的View。

    4.2理解MeasureSpec

    MeasureSpec(测量规格)在很大程度上决定了一个View的尺寸规格,之所以说是很大程度上是因为这个过程还受父容器的影响,因为父容器影响View的MeasureSpec的创建过程。

    MeasureSpec代表一个32位的int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,SpecSize是指在某种测量模式下的规格大小。

    SpecMode有三种分别是:

    (1)UNSPECIFIED

      父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态;

    (2)EXACTLY

      父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式。

    (3)AT_MOST

      父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体体现。它对应于LayoutParams中的wrap_content。

    DecorView的MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同决定;普通View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。

    总之,只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以快速的确定出子元素的MeasureSpec了,有了MeasureSpec就可以进一步确定出子元素测量后的大小了。

    4.3View的工作流程

      View的工作流程主要是指measure、layout、draw这三大过程,即测量、布局和绘制,其中measure确定View的测量宽高,layout确定View的最终宽高和四个顶点的位置,而draw则将View绘制到屏幕上。

      4.3.1 measure过程

        measure过程分为两种情况,一种是view的measure过程,一种是ViewGroup的measure过程。

      (1)view的measure过程

      view的measure过程直接由measure方法来完成。

      注意:直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent。

      (2)ViewGroup的measure过程

      对于ViewGroup来说,除了完成自己的measure过程以外,还会遍历去调用所有子元素的measure方法,各个子元素在递归去执行这个过程。

      注
意:在某些极端情况下,系统可能需要多次measure才能确定最终的测量宽高,在这种情况下,在onMeasure方法中拿到的测量宽高很可能是不准确
的。一个比较好的习惯是在onLayout方法中去获取View的测量宽高或者最终宽高(原因:在View的默认实现中,View的测量宽高和最终宽高是
相等的,只不过测量宽高形成于View的measure过程,而最终宽高形成于View的layout过程,即两者的赋值时机不同,测量宽高的赋值时机稍
微早一些)。

      举例测量宽高不等于最终宽高的情况:

  1. public void layout(int l,int t,int r, int b){
  2. surper.layout(l,t,r+100,b+100);
  3. }

       上述代码会导致在任何情况下View的最终宽高总是比测量宽高大100px!

      4.3.2 layout过程

      Layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用其layout方法,在layout方法中onLayout方法又会被调用。layout方法确定View本身的位置,而onLayout方法则会确定所有子元素的位置。

      4.3.2 draw过程

      draw过程的作用是将View绘制到屏幕上面。draw过程的步骤:

      (1)绘制背景background.draw(canvas);

      (2)绘制自己(onDraw);

      (3)绘制children(dispathDraw);

      (4)绘制装饰(onDrawScrollBars)。

      View绘制过程的传递是通过dispatchDraw来实现的。

    4.4自定义View

      4.4.1自定义View的分类

      1.继承View重写onDraw方法(需要自己支持wrap_content,并且padding也需要自己处理);

      2.继承ViewGroup派生特殊的Layout(需要合适的处理ViewGroup的测量、布局这两个过程,并同时处理子元素的测量和布局过程);

      3.继承特定的View(比如TextView,不需要自己支持wrap_content和padding等);

      4.继承特定的ViewGroup(比如LinearLayout,不需要自己处理ViewGroup的测量和布局这两个过程)。

      2和4的主要差别在于2更接近系统底层。

      4.4.2自定义View须知

      自定义View时主要有如下几个注意事项:

      (1)让View支持wrap_content;

      (2)如果有必要,让View支持padding;

      (3)尽量不要在View中使用handler,没必要(因为View本身就提供了post系列的方法);

 
     (4)View中如果有线程或者动画,需要及时停止,参考View#onDetachedFromWindow(当包含此View的
Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用;当包含此View的
Activity启动时,View的onAttachedToWindow方法会被调用);

      (5)View带有滑动嵌套情形时,需要处理好滑动冲突。

      4.4.3自定义View示例

      1.继承View重写onDraw方法

      其中主要是对wrap_content和padding的处理。

  1. public class CircleView extends View {
  2. private int mColor = Color.RED;
  3. private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  4.  
  5. public CircleView(Context context) {
  6. super(context);
  7. init();
  8. }
  9.  
  10. public CircleView(Context context, AttributeSet attrs) {
  11. this(context, attrs, 0);
  12. }
  13.  
  14. public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
  15. super(context, attrs, defStyleAttr);
  16. TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.CircleView);
  17. mColor=a.getColor(R.styleable.CircleView_circle_color,Color.RED);
  18. a.recycle();
  19. init();
  20. }
  21.  
  22. private void init() {
  23. mPaint.setColor(mColor);
  24. }
  25.  
  26. /**
  27. * 重写onMeasure方法主要是为了解决wrap_content的问题,否则设置wrap_content会无效。
  28. * @param widthMeasureSpec
  29. * @param heightMeasureSpec
  30. */
  31. @Override
  32. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  33. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  34. int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
  35. int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
  36. int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
  37. int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
  38. if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
  39. setMeasuredDimension(200, 200);
  40. } else if (widthSpecMode == MeasureSpec.AT_MOST) {
  41. setMeasuredDimension(200, heightSpecSize);
  42. } else if (heightSpecMode == MeasureSpec.AT_MOST) {
  43. setMeasuredDimension(widthSpecSize, 200);
  44. }
  45. }
  46.  
  47. /**
  48. * 重写onDraw方法注意其中对padding的处理
  49. * @param canvas
  50. */
  51. @Override
  52. protected void onDraw(Canvas canvas) {
  53. super.onDraw(canvas);
  54. final int paddingLeft=getPaddingLeft();
  55. final int paddingTop=getPaddingTop();
  56. final int paddingRight=getPaddingRight();
  57. final int paddingBottom=getPaddingBottom();
  58. int width=getWidth()-paddingLeft-paddingRight;
  59. int height=getHeight()-paddingTop-paddingBottom;
  60. int radius=Math.min(width,height)/2;
  61. canvas.drawCircle(paddingLeft+width/2,paddingTop+height/2,radius,mPaint);
  62. }
  63. }

      顺便总结一下Android自定义属性的步骤

      (1)在values目录下创建自定义属性的XML文件,例如attrs.xml,文件名没有限制,文件中的内容如下(这里自定义一个格式为“color”的属性“circle_color”):

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="CircleView">
  4. <attr name="circle_color" format="color"/>
  5. </declare-styleable>
  6. </resources>

      (2)在View的构造方法中解析自定义属性的值并作相应处理。

  1. public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
  2. super(context, attrs, defStyleAttr);
  3. TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.CircleView);
  4. mColor=a.getColor(R.styleable.CircleView_circle_color,Color.RED);
  5. a.recycle();
  6. init();
  7. }

      注意最后要调用recycle方法来释放资源;

      (3)在布局文件中使用自定义属性。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. xmlns:app="http://schemas.android.com/apk/res-auto"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity"
  8. tools:showIn="@layout/activity_main">
  9.  
  10. <tadakastu.myapplication.CircleView
  11. android:layout_margin="20dip"
  12. android:layout_width="wrap_content"
  13. android:layout_height="100dp"
  14. android:padding="20dip"
  15. app:circle_color="#00ff00"
  16. android:background="#000000" />
  17. </RelativeLayout>

      不要忘记添加

  1. xmlns:app="http://schemas.android.com/apk/res-auto"

      以上就是自定义属性的步骤。

      2.继承ViewGroup派生特殊的Layout

      这种方法主要用于实现自定义的布局(P 209页例子)。    

Android开发艺术探索学习笔记(四)的更多相关文章

  1. Android开发艺术探索学习笔记(三)

    第三章  View的事件体系 3.1 View基础知识 3.1.1 什么是view View 是Android中所有控件的基类,是一种界面层的控件的一种抽象,它代表了一个控件. 3.1.2 View的 ...

  2. Android开发艺术探索学习笔记(十一)

    第十一章  Android的线程和线程池 从用途上来说,线程分为子线程和主线程,主线程主要处理和界面相关的事情,而子线程往往用于执行耗时的操作.AsyncTask,IntentService,Hand ...

  3. Android开发艺术探索学习笔记(六)

    第六章 Android的Drawable  Drawable的优点:使用简单,比自定义view的成本要低:非图片类型的Drawable占用空间小,有利于减小APK安装包的大小. 6.1Drawable ...

  4. Android开发艺术探索学习笔记(一)

    第一章 Activity的生命周期和启动模式 1.1Activity的生命周期全面解析 1.1.1典型情况下的生命周期分析 (1)在两个Activity进行切换时,当前的Activity的onPaus ...

  5. Android开发艺术探索学习笔记(十)

    第十章  Android的消息机制 面试中经常会被问到的一个问题:handler是如何在子线程和主线程中进行消息的传递的,这个问题通过了解Android的消息机制可以得到一个准确的答案. Androi ...

  6. Android开发艺术探索读书笔记——01 Activity的生命周期

    http://www.cnblogs.com/csonezp/p/5121142.html 新买了一本书,<Android开发艺术探索>.这本书算是一本进阶书籍,适合有一定安卓开发基础,做 ...

  7. Android开发艺术探索读书笔记——进程间通信

    1. 多进程使用场景 1) 应用某些模块由于特殊需求须要执行在单独进程中. 如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会由于应用程序进程crash而受影响. 2) 为加大一个应用可 ...

  8. android开发艺术探索学习 之 结合Activity的生命周期了解Activity的LaunchMode

    转载请标明出处: http://blog.csdn.net/lxk_1993/article/details/50749728 本文出自:[lxk_1993的博客]: 首先还是先介绍下Activity ...

  9. android开发艺术探索读书笔记之-------view的事件分发机制

    View的点击事件的分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生后,系统需要把这个事件传递给一个具体的View,而这个过程就是分发过程. 分发过程主要由以下 ...

随机推荐

  1. C语言实现BMP图片生成

    ## #include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned cha ...

  2. NSString copy && strong

    http://www.cocoachina.com/ios/20150512/11805.html 我们在声明一个NSString属性时,对于其内存相关特性,通常有两种选择(基于ARC环境):stro ...

  3. jenkins+maven+svn构建项目,及远程部署war包到tomcat上

    要使用jenkins构建项目,当然要使用jenkins了,我使用的war版本的jenkins jenkins的官网 http://jenkins-ci.org/ 点击latest下载,但是可能因为天朝 ...

  4. 修改apche的端口号为80后,重启不成功,怎么办

    修改为80后提示如下 Job for httpd.service failed. See 'systemctl status httpd.service' and 'journalctl -xn' f ...

  5. [device-orientation] 使用手机设备的方向感应实现图片选择

    <div class="main"> <h2>Device Orientation</h2> <table> <tbody&g ...

  6. Android-Kotlin-函数表达式&String与Int转换$异常处理

    Kotlin的函数表达式: package cn.kotlin.kotlin_base03 /** * 函数第一种写法 */ fun addMethod1(number1: Int, number2: ...

  7. 我最常用的7个Web在线工具

    为什么要用Web在线工具呢?有两个原因,第一,它不受限于物理平台,我既可以在自己的电脑上使用,也可以在公司或亲戚朋友的电脑上使用(不管对方的操作系统是什么,只要能上网):第二,可以解放硬盘,减少PC端 ...

  8. 微信小游戏canvas操作

    这几天在做项目的时候,想在游戏画面之前,在Canvas上面画上一张背景图,代码如下     let ctx = canvas.getContext('2d')    export default cl ...

  9. 1.翻译:EF基础系列--什么是Entity Framework?

    大家好,好久不见,EF系列之前落下了,还是打算重新整理一下. 先说说目前的打算:先简单了解一下EF基础系列-->然后就是EF 6 Code-First系列-->接着就是EF 6 DB-Fi ...

  10. 【译】AI 让科技公司变得更强大吗

    机器学习可能是当今技术中最重要的基本趋势.由于机器学习的基础是数据 - 大量的数据 - 很常见的是,人们越来越担心已经拥有大量数据的公司会变得更强大.这有一定的道理,但是以相当狭窄的方式,同时ML也看 ...