在上一篇文章中,不仅熟悉了动态设定布局的方法,而且也对view的绘制流程有所了解。于是我继续做了下面几个实验,发现了一个问题。如果对view的绘制流程不是很明白,可以看看我的上一篇文章的介绍,点击下面的链接:

http://www.cnblogs.com/fuly550871915/p/4872865.html

 

         由于本人水平有限,如果有理解的不对或者不足的地方,劳请大家批评指正,共同进步。

 

        好了,直接看代码吧。首先新建一个项目“View实验",然后新建类MyLinearLayout,继承自RelativeLayout。在这里我想实现一个派生的RelativeLayout,手动添加一个按钮进去。观察一下添加按钮的代码写在哪个方法中合适。是写在onMeasure方法中呢,还是写在onLayout或者onDraw方法中呢?下面分几个步骤来做实验。

实验一、写在onMeasure方法中

        类MyLinearLayout的代码如下:

 1 package com.fuly.view;
 2 
 3 import android.content.Context;
 4 import android.util.AttributeSet;
 5 import android.widget.Button;
 6 import android.widget.RelativeLayout;
 7 
 8 
 9 public class MyLinearLayout extends RelativeLayout{
10     
11     private boolean once = false;
12 
13     public MyLinearLayout(Context context, AttributeSet attrs) {
14         super(context, attrs);
15     
16     }
17     
18     
19     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
20         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
21         if(!once){
22             this.setPadding(3, 3, 3, 3);//首先设定四个方向的内边距
23         //首先设定按钮
24         Button btn = new Button(getContext());
25         btn.setText("按钮");
26         btn.setId(1);//设定id,
27         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
28         lp.leftMargin = 5;//设定左边距
29         lp.addRule(ALIGN_PARENT_LEFT);//设定位于最左边
30         addView(btn,lp);
31 
32         once = true;
33         }
34         
35         setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
36     }
37 
38     
39 }

       然后更改activity_main.xml中的代码,如下:

 1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5        >
 6 
 7  <com.fuly.view.MyLinearLayout 
 8      android:layout_width="match_parent"
 9      android:layout_height="wrap_content"
10      >
11   
12  </com.fuly.view.MyLinearLayout>
13 
14 </RelativeLayout>

      现在来运行一下,效果如下:

 

由此见在onMeasure方法中执行添加子view的代码是可以的。

 

实验二、写在onLayout方法中

      只需要修改MyLinearLayout方法中的代码如下:

 1 package com.fuly.view;
 2 
 3 import android.content.Context;
 4 import android.util.AttributeSet;
 5 import android.widget.Button;
 6 import android.widget.RelativeLayout;
 7 
 8 
 9 public class MyLinearLayout extends RelativeLayout{
10     
11     private boolean once = false;
12 
13     public MyLinearLayout(Context context, AttributeSet attrs) {
14         super(context, attrs);
15     
16     }
17     
18     
19 
20     protected void onLayout(boolean changed, int l, int t, int r, int b) {
21         // TODO Auto-generated method stub
22         super.onLayout(changed, l, t, r, b);
23         if(!once){
24             this.setPadding(3, 3, 3, 3);//首先设定四个方向的内边距
25         //首先设定按钮
26         Button btn = new Button(getContext());
27         btn.setText("按钮");
28         btn.setId(1);//设定id,
29         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
30         lp.leftMargin = 5;//设定左边距
31         lp.addRule(ALIGN_PARENT_LEFT);//设定位于最左边
32         addView(btn,lp);
33 
34         once = true;
35         }
36         
37     }
38 }

      运行效果我就不贴图了,跟上面的一样。说明写在onLayout方法中也可以。

 

实验三、写在onDraw方法中

      依旧只需要修改MyLinearLayout方法中的代码,如下:

 1 package com.fuly.view;
 2 
 3 import android.content.Context;
 4 import android.graphics.Canvas;
 5 import android.util.AttributeSet;
 6 import android.widget.Button;
 7 import android.widget.RelativeLayout;
 8 
 9 
10 public class MyLinearLayout extends RelativeLayout{
11     
12     private boolean once = false;
13 
14     public MyLinearLayout(Context context, AttributeSet attrs) {
15         super(context, attrs);
16     
17     }
18     
19 
20     protected void onDraw(Canvas canvas) {
21         
22         super.onDraw(canvas);
23         if(!once){
24             this.setPadding(3, 3, 3, 3);//首先设定四个方向的内边距
25         //首先设定按钮
26         Button btn = new Button(getContext());
27         btn.setText("按钮");
28         btn.setId(1);//设定id,
29         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
30         lp.leftMargin = 5;//设定左边距
31         lp.addRule(ALIGN_PARENT_LEFT);//设定位于最左边
32         addView(btn,lp);
33 
34         once = true;
35         }
36         
37     }
38 }

      运行程序,效果图如下:

      我们发现什么效果也没有。

 

实验四、ViewGroup重写onDraw注意事项

 

      然后我上网查找了下原因。才知道很可能是因为没有触发onDraw方法导致的。因为RelativeLayout是一个容器,可以翻看源码发现它继承自ViewGroup。而容器类的view默认不会触发onDraw方法,即ViewGruop默认不执行onDraw方法。原因很简单直白,因为一个容器如果里面没有装任何东西,那就不需要触发绘制功能。所以如果想让一个ViewGruup执行它的onDraw方法,必须让它的里面放点东西。大概有三种解决办法:

(1)给这个ViewGroup添加背景颜色或者背景图片。

(2)在其构造方法中添加一句setWillNotDraw(false)。这句的意思就是设定需要绘制,即执行onDraw方法。

     那我们这样子,使用第二种方法,修改MyLinearLayout中的代码如下:

 1 package com.fuly.view;
 2 
 3 import android.content.Context;
 4 import android.graphics.Canvas;
 5 import android.util.AttributeSet;
 6 import android.widget.Button;
 7 import android.widget.RelativeLayout;
 8 
 9 
10 public class MyLinearLayout extends RelativeLayout{
11     
12     private boolean once = false;
13     
14 
15     public MyLinearLayout(Context context, AttributeSet attrs) {
16         super(context, attrs);
17         setWillNotDraw(false);
18     
19     }
20 
21     protected void onDraw(Canvas canvas) {
22         
23         super.onDraw(canvas);
24         if(!once){
25             this.setPadding(3, 3, 3, 3);//首先设定四个方向的内边距
26         //首先设定按钮
27         Button btn = new Button(getContext());
28         btn.setText("按钮");
29         btn.setId(1);//设定id,
30         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
31         lp.leftMargin = 5;//设定左边距
32         lp.addRule(ALIGN_PARENT_LEFT);//设定位于最左边
33         addView(btn,lp);
34 
35         once = true;
36         }
37         
38     }
39 }

 

      然后运行程序,效果呢,很遗憾的告诉你,依然没有显示出按钮。效果图依旧如下:

              

       

       奇怪按照逻辑,应该执行onDraw方法,应该会画出按钮啊。上面的实验都是我在小米手机2S上做的,反复实验我却发现一个问题。上段代码,一字不差的运行在eclipse的模拟器上,就会有效果,绘制出按钮。很奇怪。在模拟器上的效果图如下:

   

       这真是个奇怪的事情!!注意到我的模拟器使用的是android4.4版本,而小米2S手机是android4.1.1,我猜想是因为版本的问题。于是我又用了华为荣耀7实验,android版本是兼容5.0,结果还是没有画出按钮来。具体原因真是很奇怪。不明白为什么,总之由这个现象知道最好不要将添加view的代码放在ViewGroup的onDraw方法中。

 

实验五、继续实验

      onDraw方法中,不要执行添加子view的操作,那么我们来执行普通的绘制呢?比如画一个圆。我们来看看。修改MyLinearLayout方法如下:

 1 package com.fuly.view;
 2 
 3 import android.content.Context;
 4 import android.graphics.Canvas;
 5 import android.graphics.Color;
 6 import android.graphics.Paint;
 7 import android.util.AttributeSet;
 8 import android.widget.Button;
 9 import android.widget.RelativeLayout;
10 
11 
12 public class MyLinearLayout extends RelativeLayout{
13     
14     private boolean once = false;
15     private Paint mPaint;
16     
17 
18     public MyLinearLayout(Context context, AttributeSet attrs) {
19         super(context, attrs);
20         setWillNotDraw(false);
21         mPaint = new Paint();
22         mPaint.setColor(Color.RED);
23     
24     }
25     
26     
27     protected void onDraw(Canvas canvas) {
28         
29         super.onDraw(canvas);
30         
31         canvas.drawText("我是画出来的", 30, 30, mPaint);
32         canvas.drawCircle(50, 50, 20, mPaint);
33         
34     }
35 }

      这次我手机和模拟器都进行了实验,发现都是什么都没有画什么都没有画,效果如下:

        

 

     我又继续加上了画按钮的代码,结果更让我吃惊了。代码如下:

 1 package com.fuly.view;
 2 
 3 import android.content.Context;
 4 import android.graphics.Canvas;
 5 import android.graphics.Color;
 6 import android.graphics.Paint;
 7 import android.util.AttributeSet;
 8 import android.widget.Button;
 9 import android.widget.RelativeLayout;
10 
11 
12 public class MyLinearLayout extends RelativeLayout{
13     
14     private boolean once = false;
15     private Paint mPaint;
16     
17 
18     public MyLinearLayout(Context context, AttributeSet attrs) {
19         super(context, attrs);
20         setWillNotDraw(false);
21         mPaint = new Paint();
22         mPaint.setColor(Color.RED);
23     
24     }
25     
26     
27 
28     protected void onDraw(Canvas canvas) {
29         
30         super.onDraw(canvas);
31         if(!once){
32             this.setPadding(3, 3, 3, 3);//首先设定四个方向的内边距
33         //首先设定按钮
34         Button btn = new Button(getContext());
35         btn.setText("按钮");
36         btn.setId(1);//设定id,
37         LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
38         lp.leftMargin = 5;//设定左边距
39         lp.addRule(ALIGN_PARENT_LEFT);//设定位于最左边
40         addView(btn,lp);
41 
42         once = true;
43         }
44         canvas.drawText("我是画出来的", 30, 30, mPaint);
45         canvas.drawCircle(50, 50, 20, mPaint);
46         
47     }
48 }

       分别在手机和模拟器上运行程序,效果图分别如下:

        

 

      发现手机还是没法响应onDraw方法,而模拟器却可以。看到这个结果,我彻底乱了。更加困惑。不再实验了,做下总结吧。不知道还有没有人遇到这样子的情况。真心希望有大牛能解释下原因。

 

六、总结

      做了好几个实验,虽然随着清楚原因到看到实验结果却十分迷乱,但是还是总结一下吧。

(1)自定义ViewGroup添加子view的代码尽量放在onMeasure方法中,不要放在onDraw方法中。

(2)自定义ViewGruop默认不执行onDraw,如果想执行重写的onDraw方法,需要在其构造方法中加上一句

setWillNotDraw(false)

 

(3)如果自定义的view,则默认是执行onDraw方法,因为只需要重写就可以自动执行了。

 

posted on 2015-10-13 13:53  fuly  阅读(1100)  评论(0编辑  收藏  举报