一、引言

有时候Android传统的页面布局不足以满足我们的需求,常常需要自己定义view,通常继承View,然后重写构造方法以及onDraw等函数,再具体实现自己定义的复杂view。我们知道在给控件赋属性时,通常使用的是android系统自带的属性,比如 android:layout_height="wrap_content",除此之外,我们亦可以自己定义属性,这样在使用的时候我们就可以使用形如 testapp:testSize="15sp"的方式了

二、使用步骤

1.在项目文件res/value下面创建一个attrs.xml文件即自定义属性的声明文件,该文件中包含若干个attr集合,例如:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="test">
      <attr name="textSize" format="dimension"/>
      <attr name="texsColor" format="color"/>
      <attr name="type" format="integer"/>
      <attr name="background" format="reference"/>
  </declare-styleable>
</resources>

其中resource是根标签,可以在里面定义若干个declare-styleable,<declare-styleable name="MyView">中name定义了属性集合的名称,下面可以再自定义多个属性,针对<attr name="testSize" format="dimension "/>来说,其属性的名称为"testSize",format指定了该属性类型为dimension,表示尺寸值。

format还可以指定其他的类型比如;

reference   表示引用,参考某一资源ID

string   表示字符串

color   表示颜色值

dimension   表示尺寸值

boolean   表示布尔值

integer   表示整型值

float   表示浮点值

fraction   表示百分数

enum   表示枚举值

flag   表示位运算

2.在使用到该自定义view的布局文件中键入一行代码,为该自定义属性添加命名空间,名称可以自己定义,在这我定义为testapp,并不要求一定是自定义view的名字,同时使用Eclipse和AS的在此有点区别:

Ecplise:其中com.cx.test为应用程序包名:

xmlns:testapp="http://schemas.android.com/apk/res/com.cx.test"

AS:以前写应用程序包名的方法被AS废弃掉了,直接使用res-auto:

xmlns:testapp="http://schemas.android.com/apk/res-auto"

接下来就可以在布局文件中使用自定义属性啦:

<com.cx.test.MyView
        android:layout_width="100dp"
        android:layout_height="100dp"
        testapp:textColor="#000000"
        testapp:textSize="15sp"
        testapp:type="1"
        />

3.在自定义view的代码中引入自定义属性,修改构造函数:

AttributeSet与TypedArray:

构造方法中的有个参数叫做AttributeSet(eg: MyView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的集合,那么我能不能通过它去获取我的自定义属性呢?首先AttributeSet中的确保存的是该View声明的所有的属性,并且外面的确可以通过它去获取(自定义的)属性,怎么做呢? 

其实看下AttributeSet的方法就明白了,下面看代码:

public class MyView extends View {
    private static String TAG=MyView.class.getSimpleName();
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //使用AttributeSet获取属性
        for (int i=0;i<attrs.getAttributeCount();i++){
            Log.e(TAG, "MyView: =name=" + attrs.getAttributeName(i) + "=value=" + attrs.getAttributeValue(i));
        }
    }
}

输出:

E/MyView: MyView: =name=layout_width=value=100.0dip
 E/MyView: MyView: =name=layout_height=value=100.0dip
 E/MyView: MyView: =name=textSize=value=15.0sp
 E/MyView: MyView: =name=textColor=value=#ff000000
 E/MyView: MyView: =name=type=value=1

结果果然可以输出自定义view的所有属性,也就是通过AttributeSet可以获得布局文件中定义的所有属性的key和value(还有一些方法,自己去尝试)。

现在关注下一个问题:TypedArray是什么鬼?

我们简单修改下,布局文件中的View的属性:



<com.cx.test.MyView
        android:layout_width="100dp"
        android:layout_height="100dp"
        testapp:textColor="#000000"
        testapp:textSize="@dimen/mysize"
        testapp:type="1"
        testapp:bg="@drawable/aa"
        />


此时输出:


E/MyView: MyView: =name=layout_width=value=100.0dip
 E/MyView: MyView: =name=layout_height=value=100.0dip
 E/MyView: MyView: =name=textSize=value=@2131230794
 E/MyView: MyView: =name=textColor=value=#ff000000
 E/MyView: MyView: =name=type=value=1
 E/MyView: MyView: =name=bg=value=@2130837504

通过AttributeSet获取的值,如果是引用都变成了@+数字的字符串。你说,这玩意你能看懂么?那么你看看最后一行使用TypedArray获取的值,是不是瞬间明白了什么。TypedArray其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/myszie),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。

下面使用TypedArray:

public class MyView extends View {
    private static String TAG=MyView.class.getSimpleName();
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //使用AttributeSet获取属性
//        for (int i=0;i<attrs.getAttributeCount();i++){
//            Log.e(TAG, "MyView: =name=" + attrs.getAttributeName(i) + "=value=" + attrs.getAttributeValue(i));
//        }
        //使用TypedArray获取属性
        TypedArray ta=context.obtainStyledAttributes(attrs,R.styleable.test);
        String textSize=ta.getString(R.styleable.test_textSize);
        int type=ta.getInteger(R.styleable.test_type,0);
        int color=ta.getColor(R.styleable.test_textColor, 0);
        Drawable drawable = ta.getDrawable(R.styleable.test_bg);
        Log.e(TAG, "MyView: =textSize=" + textSize + "=type=" + type+"=color="+color+"=drawable="+drawable);
        ta.recycle();

    }
}


输出结果:

E/MyView: MyView: =textSize=20.0sp=type=1=color=-16777216=drawable=android.graphics.drawable.BitmapDrawable@428d6258


可以看出使用TypedArray也可以获取布局文件中使用引用的属性。 通过比较可以看出我们在View的构造方法中,可以通过AttributeSet去获得自定义属性的值,但是比较麻烦,而TypedArray可以很方便的便于我们去获取。