在上一篇文章中,不仅熟悉了动态设定布局的方法,而且也对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方法,因为只需要重写就可以自动执行了。