<pre name="code" class="html"><declare-styleable name="MyToggleBtn"> // 声名属性集的名称,即这些属性是属于哪个控件的。 <attr name="current_state" format="boolean"/> // 声名属性 current_state 格式为 boolean 类型 <attr name="slide_button" format="reference"/> // 声名属性 slide_button格式为 reference 类型 </declare-styleable>所有的format类型
<com.example.mytogglebtn.MyToggleButton xmlns:example="http://schemas.android.com/apk/res/com.example.mytogglebtn" android:layout_width="wrap_content" android:layout_height="wrap_content" example:slide_button="@drawable/slide_button" />三、在代码中对属性进行解析,代码如下:
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyToggleBtn);// 由attrs 获得 TypeArray以上是创建自定义属性的大致步骤。下面,我将要创建一个自定义控件的Demo,来学习学习自定义属性的相关知识点。
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- 声明属性级的名称 --> <declare-styleable name="MyView"> <!-- 声明一个属性,整型 --> <attr name="test_id" format="integer" /> <!-- 声明一个属性,字符串 --> <attr name="test_msg" format="string" /> <!-- 声明一个属性,引用,引用资源id --> <attr name="test_bitmap" format="reference" /> </declare-styleable> </resources>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:example="http://schemas.android.com/apk/res/com.example.myattrs" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.myattrs.MyView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" example:test_bitmap="@drawable/ic_launcher" example:test_msg="@string/app_name" /> </RelativeLayout>
package com.example.myattrs; import android.content.Context; import android.view.View; public class MyView extends View { public MyView(Context context) { super(context); // TODO Auto-generated constructor stub } }运行一下工程,那么工程立即崩溃了,报错也很清晰明了:
09-17 06:52:24.389: E/AndroidRuntime(1563): Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]表示没有找到某个带两个参数的构造方法,于是,知道自定义属性必须得复写父类的另外一个构造方法,修改如下:
package com.example.myattrs; import android.content.Context; import android.util.AttributeSet; import android.view.View; public class MyView extends View { public MyView(Context context, AttributeSet attrs) { super(context, attrs); int count = attrs.getAttributeCount(); for (int index = 0; index < count; index++) { String attributeName = attrs.getAttributeName(index); String attributeValue = attrs.getAttributeValue(index); System.out.println("name:" + attributeName + " value:" + attributeValue); } } }打印结果如下:
public View(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public View(Context context, AttributeSet attrs, int defStyle) { this(context); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0);于是,找到一个跟属性很相关的类TypeArray,那么接下来,我在自定义控件的构造方法上也获取一下TypeArray这个类:
package com.example.myattrs; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; public class MyView extends View { public MyView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView); int count = ta.getIndexCount(); for (int i = 0; i < count; i++) { int itemId = ta.getIndex(i); System.out.println("itemId::" + itemId); // 获取属性在R.java文件中的id switch (itemId) { case R.styleable.MyView_test_bitmap: int bitmapId = ta.getResourceId(itemId, 100); System.out.println("bitmapId::" + bitmapId); break; case R.styleable.MyView_test_id: int test_id = ta.getInteger(itemId, 10); System.out.println("test_id" + test_id); break; case R.styleable.MyView_test_msg: String test_msg = ta.getString(itemId); System.out.println("test_msg::" + test_msg); break; default: break; } } } }
以下是TypeArray类里的方法,这里不写注释了,见名知意:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyToggleBtn"> <!-- 滑动按钮背景图片 --> <attr name="switchBG" format="reference" /> <!-- 滑动块图片 --> <attr name="slideBg" format="reference" /> <!-- 设置当前的状态 --> <attr name="currState" format="boolean" /> </declare-styleable> </resources>然后,在引用自定义控件的布局文件acticity_main.xml上设置自定义属性,记住,引用这些属性之前,必须先引用命名空间:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:mytogglebtn="http://schemas.android.com/apk/res/com.example.slidebutton" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.slidebutton.view.SlideButton android:id="@+id/slidebutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" mytogglebtn:currState="false" mytogglebtn:slideBg="@drawable/slide_button_background" mytogglebtn:switchBG="@drawable/switch_background" /> </RelativeLayout>有了上面的步骤,我们就可以自定义组件类的构造方法中,将属性集解析成TypeArray了,从TypeArray中获取相关的属性值,并用于初始化自定义控,以下是主要代码:
public SlideButton(Context context, AttributeSet attrs) { super(context, attrs); // 获得自定义属性 TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyToggleBtn); int count = ta.getIndexCount(); for (int i = 0; i < count; i++) { int itemId = ta.getIndex(i); // 获取某个属性的Id值 switch (itemId) { case R.styleable.MyToggleBtn_currState: // 设置当前按钮的状态 currentState = ta.getBoolean(itemId, false); break; case R.styleable.MyToggleBtn_switchBG: // 设置按钮的背景图 int backgroundId = ta.getResourceId(itemId, -1); if (backgroundId == -1) throw new RuntimeException("资源没有被找到,请设置背景图"); switchBG = BitmapFactory.decodeResource(getResources(), backgroundId); break; case R.styleable.MyToggleBtn_slideBg: // 设置按钮图片 int slideId = ta.getResourceId(itemId, -1); if (slideId == -1) throw new RuntimeException("资源没有找到,请设置按钮图片"); slideButtonBG = BitmapFactory.decodeResource(getResources(), slideId); break; default: break; } } }从上可以看到,自定义属性其实很简单。就是在构造方法中,将获取到的属性集加工成TypeArray对象,通过这个对象取出属性的id,通过id取出每个属性对应的值(毕竟Android下的布局文件XML也是key-value形式的),最后将获取到的属性值(控件用户自定义的数据)初始化到自定义控件上,这样,一个完整的自定义控件就完成。这种完整的自定义控件方式用的并不多见,因为在开发自定义控件时候,需要什么数据就直接在Java代码里设置就好了,方便多了。但是在特定的场合下,如果开发的控件某些数据不确定,或者所开发控件需要提供给其他人进行偏好设置什么的,这种自定义属性就显得非用不可了。