View总结(1)---onMeasure(),onLayout(),onDraw()方法

来源:互联网 发布:淘宝个人店铺 注册商标 编辑:程序博客网 时间:2024/05/17 07:21

View总结

本文是在阅读http://blog.csdn.net/qinjuning 和
http://www.idtkm.com/2016/08/10/10%E3%80%81RequestLayout/的一些总结思考

View绘制过程

项目 价格 描述 布局 onMeasure 测量View和ChildView的大小 - onLayout 确定Child View的位置 - onSizeChanged 确定View的大小 绘制 onDraw 实际绘制View的内容 重绘 invalidate 调用onDraw方法,重绘View中变化的部分

附上一张流程图(图片来源https://plus.google.com/photos/photo/115089607132986274709/5801812726909061186)

onMeasure()方法
作用:测量View与ChildView的大小
onMeasure()的函数模型

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 

widthMeasureSpec和heightMeasureSpec共32bit,前2bit记录了测量模式,后30bit记录了View尺寸信息,可以通过下面方法获得相应的值。测量模式有三种:UNSPECIFIED,EXACTLY,AL_MOST。

int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);
测量模式 意义 对应布局 UNSPECIFIED 父容器没有对View做任何的限制,View可取任意尺寸 EXACTLY WindthSize就是当前View应取的尺寸 match_parent和固定尺寸(eg:100dp) AT_MOST windthSize是当前View能取的最大尺寸 wrap_content

onMeasure()源码

//回调View视图里的onMeasure过程  private void onMeasure(int height , int width){   //设置该view的实际宽(mMeasuredWidth)高(mMeasuredHeight)   //1、该方法必须在onMeasure调用,否者报异常。   setMeasuredDimension(h , l) ;   //2、如果该View是ViewGroup类型,则对它的每个子View进行measure()过程   int childCount = getChildCount() ;   for(int i=0 ;i<childCount ;i++){    //2.1、获得每个子View对象引用    View child = getChildAt(i) ;    //整个measure()过程就是个递归过程    //该方法只是一个过滤器,最后会调用measure()过程 ;或者 measureChild(child , h, i)方法都    measureChildWithMargins(child , h, i) ;     //其实,对于我们自己写的应用来说,最好的办法是去掉框架里的该方法,直接调用view.measure(),如下:    //child.measure(h, l)   }  }  //该方法具体实现在ViewGroup.java里 。  protected  void measureChildWithMargins(View v, int height , int width){   v.measure(h,l)     }  

重写onMeasure()方法—实现效果:View的宽高相等,默认为100像素。

private int getSize(int defaultSize, int measureSpec){        int mySize = defaultSize;        int mode = MeasureSpec.getMode(measureSpec);        int size = MeasureSpec.getSize(measureSpec);        switch (mode){            case MeasureSpec.UNSPECIFIED:{      //如果没有指定大小,就设置为默认大小                mySize = defaultSize;                break;            }            case MeasureSpec.EXACTLY:{                mySize = size;                break;            }            case MeasureSpec.AT_MOST:{                mySize = size;                break;            }        }        return mySize;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        width = getSize(100, widthMeasureSpec);        height = getSize(100, heightMeasureSpec);        if(width > height){            width = height;        }else{            height = width;        }        //传入的是View最终的视图大小        setMeasuredDimension(width, height);        Log.d("onMeasure", "width = "+width+"; height = "+height);    }

onLayout()方法
作用:确定View的位置,设置View试图位于父视图的坐标轴,即mLeft, mRight, mTop, mBottom(调用setFrame()方法实现)。

// layout()过程  ViewRoot.java  // 发起layout()的"发号者"在ViewRoot.java里的performTraversals()方法, mView.layout()  private void  performTraversals(){      //...      View mView  ;         mView.layout(left,top,right,bottom) ;      //....  }  //回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现  private void onLayout(int left , int top , right , bottom){   //如果该View不是ViewGroup类型   //调用setFrame()方法设置该控件的在父视图上的坐标轴   setFrame(l ,t , r ,b) ;   //--------------------------   //如果该View是ViewGroup类型,则对它的每个子View进行layout()过程   int childCount = getChildCount() ;   for(int i=0 ;i<childCount ;i++){    //2.1、获得每个子View对象引用    View child = getChildAt(i) ;    //整个layout()过程就是个递归过程    child.layout(l, t, r, b) ;   }  }  

onDraw()方法
作用:由ViewRoot对象的performTraversals()方法调用draw()方法绘制View树,但只会绘制View内部包含标志位DRAWN的树,即”需要重绘”的树。

onDraw()源码

// draw()过程     ViewRoot.java  // 发起draw()的"发号者"在ViewRoot.java里的performTraversals()方法, 该方法会继续调用draw()方法开始绘图  private void  draw(){      //...   View mView  ;      mView.draw(canvas) ;        //....  }  //回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现  private void draw(Canvas canvas){   //该方法会做如下事情   //1 、绘制该View的背景   //2、为绘制渐变框做一些准备操作   //3、调用onDraw()方法绘制视图本身   //4、调用dispatchDraw()方法绘制每个子视图,dispatchDraw()已经在Android框架中实现了,在ViewGroup方法中。        // 应用程序程序一般不需要重写该方法,但可以捕获该方法的发生,做一些特别的事情。   //5、绘制渐变框    }  //ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法  @Override  protected void dispatchDraw(Canvas canvas) {   //    //其实现方法类似如下:   int childCount = getChildCount() ;   for(int i=0 ;i<childCount ;i++){    View child = getChildAt(i) ;    //调用drawChild完成    drawChild(child,canvas) ;   }       }  //ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法  protected void drawChild(View child,Canvas canvas) {   // ....   //简单的回调View对象的draw()方法,递归就这么产生了。   child.draw(canvas) ;   //.........  }  

重写onDraw的几点说明:
1. 自定义布局属性
在res/values/styles.xml中创建自己的自定义属性:

<declare-styleable name="DoubanLoadingView">    <attr name="my_size" format="dimension" /></declare-styleable>

接着即可在布局文件中声明自定义属性:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.hc.douban.DoubanLoadingView        android:layout_width="match_parent"        android:layout_height="100dp"        app:my_size="100dp" /></LinearLayout>

PS: 注意添加”http://schemas.android.com/apk/res-auto”
最后可以在自定义的View中将自定义的属性取出来:

private int defalutSize;  public MyView(Context context, AttributeSet attrs) {      super(context, attrs);      //第二个参数就是我们在styles.xml文件中的<declare-styleable>标签        //即属性集合的标签,在R文件中名称为R.styleable+name        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DoubanLoadingView);        //第一个参数为属性集合里面的属性,R文件名称:R.styleable+属性集合名称+下划线+属性名称        //第二个参数为,如果没有设置这个属性,则设置的默认的值        defalutSize = a.getDimensionPixelSize(R.styleable.DoubanLoadingView_my_size, 100);        //最后记得将TypedArray对象回收        a.recycle();   }
阅读全文
0 0
原创粉丝点击