View的子类,与View一样可以有三个constructor:

   public void CustomView(Context context) {}
   public void CustomView(Context context, AttributeSet attrs) {}
   public void CustomView(Context context, AttributeSet attrs, int defStyle) {}
   

为了方便,我们分别命名为C1,C2,C3。 C1是最简单的一个,如果你只打算用code动态创建一个view而不使用布局文件xml inflate,那么实现C1就可以了。

C2多了一个AttributeSet类型的参数,在通过布局文件xml创建一个view时,这个参数会将xml里设定的属性传递给构造函数。 如果你采用xml inflate的方法却没有在code里实现C2,那么运行时就会报错。但是由于编译能顺利通过,这个错误有时不太容易被发现。

C3多了一个defStyle的int参数,关于这个参数doc里是这样描述的: The default style to apply to this view. If 0, no style will be applied (beyond what is included in the theme). This may either be an attribute resource, whose value will be retrieved from the current theme, or an explicit style resource.

1.那么C3到底什么时候被调用呢?答案是当你显式调用它的时候。通常是在C1或者C2里

  public void CustomView(Context context, AttributeSet attrs) {
     this(context, attrs, resid);
  }
   

用以上的方式将真正构造函数的实现转移到C3里,并由resid指定defStyle,作为默认style。

2.defStyle接受什么样的值?

需要传入一个引用style资源的属性(类似R.attr.buttonStyle)。这先要创建一个res/values/attrs.xml的文件, 这个文件用来定义某个view里可以出现的属性:

 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <declare-styleable name="CustomView">
         <attr name="ourstyle" format="reference" />
         <attr name="atext" format="string" />
     </declare-styleable>
 </resources>
   

现在我们为CustomView增加了两个可以出现的自定义属性,ourstyle和atext,前者就是我们打算用来引用一个style资源的属性,后者是一个没什么用的字符串属性,放在这里只是为了后面做测试。 现在我们就可以在程序里引用这个属性并把这个参数传给defStyle。

当然,在这之前我们先要把purple这个style赋值给ourstyle。给一个view的属性赋值,就和给android:layout_width赋值一样, 除了命名空间不同(layout/main.xml的LinearLayout标签里有命名空间的声明)

 <com.your.test.CustomView android:layout_width="100px"
                           android:layout_height="100px"
                           myxmlns:ourstyle="@style/purple"
                           myxmlns:atext="test string"/>
     

调用方法如下:

 public CustomView(Context context, AttributeSet attrs) {
     this(context, attrs, R.attr.ourstyle );
 }
   

这样就成功地让defStyle生效了。