对于这个问题并不是一件容易的事,但是如果你掌握了基本的原理其实很简单。依我的习惯还是先复习一些相关的知识,这样可以保证你在阅读过程中没有阻碍。
【复习或者预习部分 Begin 】
先看看官方文档,其中Dev Guide -->User Interface -->How Android Draws Views. 
具体文档内容如下(当然你可以直接在官方上看,刚刚网上找链接的时候发现改版了,自己找找吧,我用的是离线的):
When an Activity receives focus, it will be requested to draw its layout. The 
Android framework will handle the procedure for drawing, but the Activity must 
provide the root node of its layout hierarchy.
Drawing begins with the root node of the layout. It is requested to measure 
and draw the layout tree. Drawing is handled by walking the tree and rendering 
each View that intersects the invalid region. In turn, each View group is 
responsible for requesting each of its children to be drawn (with 
the draw() method) 
and each View is responsible for drawing itself. Because the tree is traversed 
in-order, this means that parents will be drawn before (i.e., behind) their 
children, with siblings drawn in the order they appear in the tree.
The framework will not draw Views that are not in the invalid region, and 
also will take care of drawing the Views background for you.
You can force a View to draw, by callinginvalidate().
Drawing the layout is a two pass process: a measure pass and a layout pass. 
The measuring pass is implemented in measure(int, 
int) and is a top-down traversal of the View tree. Each View 
pushes dimension specifications down the tree during the recursion. At the end 
of the measure pass, every View has stored its measurements. The second pass 
happens in layout(int, 
int, int, int) and is also top-down. During this pass each 
parent is responsible for positioning all of its children using the sizes 
computed in the measure pass.
When a View's measure() method returns, its getMeasuredWidth() and getMeasuredHeight() values 
must be set, along with those for all of that View's descendants. A View's 
measured width and measured height values must respect the constraints imposed 
by the View's parents. This guarantees that at the end of the measure pass, all 
parents accept all of their children's measurements. A parent View may 
call measure() more than once on its children. For example, the 
parent may measure each child once with unspecified dimensions to find out how 
big they want to be, then call measure() on them again with actual 
numbers if the sum of all the children's unconstrained sizes is too big or too 
small (i.e., if the children don't agree among themselves as to how much space 
they each get, the parent will intervene and set the rules on the second 
pass).
To initiate a layout, call requestLayout(). 
This method is typically called by a View on itself when it believes that is can 
no longer fit within its current bounds.
The measure pass uses two classes to communicate dimensions. The View.MeasureSpec class 
is used by Views to tell their parents how they want to be measured and 
positioned. The base LayoutParams class just describes how big the View wants to 
be for both width and height. 
【复习或者预习部分 End 】
 
这一部分网上有翻译过来的,不过这也比较简单,在此就自己翻译或者google一下。下面开始切入主题,正式讲解。
 
关键部分:Drawing the layout is a two pass process: 
a measure pass and a layout pass. 
所以一个view执行OnDraw时最关键的是measure和layout。其实这很好理解的,一个view需要绘制出来,那么必须知道他要占多大的空间也就是measure,还得知道在哪里绘制,也就是把view放在哪里即layout。把这两部分掌握好也就可以随意自定义view了。至于viewGroup中如何绘制就参考上面官方文档,其实就是一个分发绘制,直到child是一个view自己进行绘制。
 
说了这么多看着挺累的,举两个例子给大家。
 
1、重写一个View。
引用一下网上的一个例子http://labs.ywlx.net/?p=284 ,代码如下:
public  class  RotateTextView  extends  TextView {private  static  final  String  NAMESPACE = “http://www.ywlx.net/apk/res/easymobi”;private  static  final  String  ATTR_ROTATE = “rotate”;private  static  final  int  DEFAULTVALUE_DEGREES = 0;private  int  degrees ;public  RotateTextView(Context context, AttributeSet attrs) {super(context, attrs);
degrees = attrs.getAttributeIntValue(NAMESPACE, ATTR_ROTATE, DEFAULTVALUE_DEGREES);
}
@Overrideprotected  void  onDraw(Canvas canvas) {
canvas.rotate(degrees,getMeasuredWidth()/2,getMeasuredHeight()/2);super.onDraw(canvas);
}
}
使用自定义RotateTextView如下:
<cn.easymobi.application.memorytest.RotateTextViewandroid:layout_width=”wrap_content”android:layout_height=”wrap_content”android:padding=”8dip”android:gravity=”center”android:id=”@+id/tvBottom_color”android:textSize=”15dip”android:textColor=”@color/black”easymobi:rotate=”10″android:layout_marginTop=”468dip”/>
个人觉得这个例子挺不错的,简单易懂,而且涉及的内容比较单一。需要更详细的讲解看文章http://labs.ywlx.net/?p=284 。
 
2、重写一个ViewGroup。
再次引用网上的一个例子http://blog.csdn.net/arui319/article/details/5868466 ,代码如下:
public class MyViewGroup extends ViewGroup {  
    public MyViewGroup(Context context) {  
        super(context);  
        this.initOtherComponent(context);  
    }  
    private void initOtherComponent(Context context) {  
        Button aBtn = new Button(context);  
        // set id 1   
        aBtn.setId(1);  
        aBtn.setText("a btn");  
        this.addView(aBtn);  
        Button bBtn = new Button(context);  
        // set id 2   
        bBtn.setId(2);  
        bBtn.setText("b btn");  
        this.addView(bBtn);  
    }  
    @Override  
    protected void onLayout(boolean changed, int l, int t, int r, int b) {  
        int childCount = getChildCount();  
        for (int i = 0; i < childCount; i++) {  
            View child = getChildAt(i);  
            switch (child.getId()) {  
            case 1:  
                // 1 is aBtn   
                Log.d("MyViewGroup", "btn1 setting");  
                child.setVisibility(View.VISIBLE);  
                child.measure(r - l, b - t);  
                child.layout(0, 0, child.getMeasuredWidth(), child  
                        .getMeasuredHeight());  
                break;  
            case 2:  
                // 2 is bBtn   
                Log.d("MyViewGroup", "btn2 setting");  
                child.setVisibility(View.VISIBLE);  
                child.measure(r - l, b - t);  
               child.layout(0, 50, child.getMeasuredWidth(), child  
                        .getMeasuredHeight() + 50);  
                break;  
            default:  
                //   
            }  
        }  
    }  
}  
这个例子主要是说明layout 和Measure的使用。
 
3、两个例子的总结。
重写一个view一般情况下只需要重写OnDraw方法。那么什么时候需要重写OnMeasure、OnLayout、OnDraw方法呢,这个问题只要把这几个方法的功能弄清楚你就应该知道怎么做了。在此我也简单的讲一下(描述不正确请拍砖,欢迎交流)。
①如果需要改变View绘制的图像,那么需要重写OnDraw方法。(这也是最常用的重写方式。)
②如果需要改变view的大小,那么需要重写OnMeasure方法。
③如果需要改变View的(在父控件的)位置,那么需要重写OnLayout方法。
④根据上面三种不同的需要你可以组合出多种重写方案,你懂的。
 
说了这么多如果有没有讲清楚的,请再看看那文档。希望对你理解view的绘制原理和重写view有所帮助。欢迎留言交流。


转自http://www.cnblogs.com/vanezkw/archive/2012/06/27/2565378.html