前言:
最近在自定义ViewGroup类型的控件时,通过复写View的OnDraw()方法来修改控件的背景,发现不论在onDraw()方法中怎么修改背景,都是不能成功,除非指定android:background的颜色属性后才能有颜色显示。网上多番查证找到原因:viewgroup的绘制严格来说是在dispatchDraw中完成,作为一个容器,绘制自己的孩子该通过dispatchDraw(canvas)实现。但是你非要在ondraw中也不会出错。

但自定义ViewGroup ondraw方法不会被调用的根本原因是你的ViewGroup中还没有可绘制的内容。

下面是详细解释:

一,现象

<com.elvis.tool.MyRelativeView    xmlns:android="http://schemas.android.com/apk/res/android"   android:id="@+id/ll_absolute"   android:orientation="vertical"   android:layout_width="fill_parent"   android:layout_height="fill_parent"android:background="#FF000000">com.elvis.tool.MyRelativeView> 

大概的架构是,MyLinearLayout从LinearLayout派生出来,然后在程序中重载OnDraw(Canvas canvas)。但是,onDraw不会被调用。我们可能会遇到这个问题:如果不给LinearLayout设置一个背景,系统是不会调用onDraw时,也就是说,我们重写的onDraw是不会调用的。当设置一个背景后,onDraw就会被调用。

二,原因
造成这种现象的原因是继承自LinearLayout,而LinearLayout这是一个容器,ViewGroup嘛,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上面有东西可画了,因此会调用onDraw方法。

我们可以仔细分析View的源码,它有一个方法View#draw(Canvas)方法,这里面有两个地方调用onDraw,它的条件都是:

if (!dirtyOpaque) onDraw(canvas);

也就是说,如果dirtyOpaque是true的话,onDraw就不会调用,而dirtyOpaque的值的计算代码如下:

final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&(mAttachInfo == null|| !mAttachInfo.mIgnoreDirtyState);

当View初始他时,它会调用一个私有方法:computeOpaqueFlags,这里面列出了不透明的三个条件:

   // Opaque if:   //   - Has a background   //   - Background is opaque   //   - Doesn't have scrollbars or scrollbars are inside overlay

View还提供了一个重要的方法:setWillNotDraw,我们看一看它的实现:

/**
* If this view doesn’t do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw} you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
从这个方法的注释,我们可以看出,如果你想重写onDraw的话,你应该调用这个方法来清除flag,所以如果我们想要重写LinearLayout的onDraw的话,我们也可以在其构造方法中调用setWillNotDraw方法。 在ViewGroup初始他时,它调用了一个私有方法:initViewGroup,它里面会有一句setFlags(WILL_NOT_DRAW, DRAW_MASK); 相当于调用了setWillNotDraw(true),所以说,对于ViewGroup,它就认为是透明的了。如果我们想要重写onDraw,就需要调用setWillNotDraw(false)

三,总结一下:
1)ViewGroup默认情况下,会被设置成WILL_NOT_DRAW,这是从性能考虑,这样一来,onDraw就不会被调用了。

2)如果我们要重要一个ViweGroup的onDraw方法,有两种方法:

   1,在构造函数里面,给其设置一个颜色,如#00000000。   2,在构造函数里面,调用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。

更多相关文章

  1. Android使用TextView实现无下划线超链接的方法
  2. [置顶] 我的Android进阶之旅------>Android中解析XML 技术详解--
  3. [置顶] android面试大宝典
  4. 【Android(安卓)界面效果26】listview android:cacheColorHint,a
  5. Android系统设计中的设计模式分析之一
  6. Android中的音频播放(MediaPlayer和SoundPool)
  7. Your Android(安卓)SDK is missing, out of date or corrupted
  8. Android(安卓)GLSurfaceView
  9. OkHttp源码深度解析

随机推荐

  1. 图片加载和处理的又一个神器 Fresco 的使
  2. Google Android开发精华教程【转】
  3. Android(安卓)数独游戏 记录
  4. Android学习笔记(8) - GridView与ImageVi
  5. android 之View 一
  6. android中Menu介绍
  7. 6.22 android计算字符高度宽度,红蓝3D图片
  8. 由 Sense 4.0 说开去: Android 第三方定制
  9. Android程序开发初级教程(一)
  10. Android下使用dlopen函数动态调用.so链接