zoukankan      html  css  js  c++  java
  • 对于view的深入理解,续篇(一)重写ViewGroup的onDraw

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

  • 相关阅读:
    B
    K
    kuangbin专题总结一 简单搜索
    5-33 地下迷宫探索 (30分) __写错了。。
    AC日记——Count on a tree bzoj 2588
    AC日记——线段树练习5 codevs 4927
    AC日记——王室联邦 bzoj 1086
    AC日记——狼抓兔子 bzoj 1001
    AC日记——联合权值 洛谷 P1351
    AC日记——软件包管理器 洛谷 P2416
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4874418.html
Copyright © 2011-2022 走看看