咱们在自定义Android组件的时候,除了用Java构建出组件的样子外,有时候还须要去申明一些“属性”提供给项目使用,那么什么是组件的属性呢?html
例如在清单文件中,建立一个TextView的时候,这是须要制定TextView的 android:layout_width="wrap_content" android:layout_height="wrap_content"等等这些都是组件的属性,TextView是android系统为咱们提供好 的组件,它的属性亦是android系统为咱们提供了。详情查看android的源码,我这里举例android2.3的源码,路径是
/frameworks/base/core/res/res/values/attrs.xml,这个attrs.xml定义了全部android系统组件的属性。
当咱们自定义组件时,除了可使用android系统为咱们提供好的属性以外,还能够自定义属性。自定义属性主要步骤以下:
1、在attrs.xml文件中声明属性,如:java
[html] view plain copyandroid
print?ios
- <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类型
reference 引用
color 颜色
boolean 布尔值
dimension 尺寸值
float 浮点值
integer 整型值
string 字符串
enum 枚举值
2、在布局文件中使用:在使用以前必须声名命名空间,xmlns:example="http://schemas.android.com/apk/res/com.example.mytogglebtn"
说明:xmlns 是XML name space 的缩写;
example 可为任意写符
http://schemas.android.com/apk/res/ 此为android固定格式; 浏览器
com.example.mytogglebtn 此应用的包名,如manifest配置文件中一致。app
布局文件:ide
[html] view plain copy工具
print?布局
- <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" />
3、在代码中对属性进行解析,代码以下:学习
[java] view plain copy
print?
- TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyToggleBtn);// 由attrs 得到 TypeArray
以上是建立自定义属性的大体步骤。下面,我将要建立一个自定义控件的Demo,来学习学习自定义属性的相关知识点。
首先,须要建立一个自定义控件出来,而且继承View。在工程的res/values文件夹下建立attrs.xml文件:
[html] view plain copy
print?
- <?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>
而后在布局文件中,引用这个自定义控件MyView
[html] view plain copy
print?
- <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>
因为建立出来的自定义组件MyView是继承于View的,因此必须得复写View的构造方法,View中有三个构造方法,先来看看复写带一个参数的构造方法:
[java] view plain copy
print?
- 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]
表示没有找到某个带两个参数的构造方法,因而,知道自定义属性必须得复写父类的另一个构造方法,修改以下:
[java] view plain copy
print?
- 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);
- }
- }
-
- }
打印结果以下:

AttributeSet:对布局文件XML解析后的结果,封装为AttributeSet对象。存储的都是原始数据,可是对数据进行了简单的加工。
由此构造器帮咱们返回了布局文件XML的解析结果,拿到这个结果,咱们该怎么作呢?接下来,咱们来看看View类对于这个是怎么处理的:
[java] view plain copy
print?
- public View(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
[java] view plain copy
print?
- 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这个类:
翻看一下TypeArray的源码会发现,TypeArray是不继承任何类(除了Object)的,也就是说,TypeArray至关于一个工具 类,经过context.obtainStyledAttributes方法,将AttributeSet和属性的类型传递进去,好比 AttributeSet至关于原材料,属性类型至关于图纸,context.obtainStyledAttributes至关于加工厂加工成所对象的 属性,封装到TypeArray这个类里。
[java] view plain copy
print?
- 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类里的方法,这里不写注释了,见名知意:

当在构造方法中获取到这些设置好的属性值时,取出其值,就能够在代码中进行处理了。
上篇博客提到了Android自定义控件——仿ios的滑动开关按钮,接下来,就要为这个滑动开关按钮条件自定义的属性,不熟悉上篇博客Demo的,能够先去浏览器一下个人上篇博客,点这里Android自定义控件——仿ios滑动开关按钮
首先,按照上面介绍的步骤,先在res/values目录下建立一个属性文件attrs.xml:
[html] view plain copy
print?
- <?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上设置自定义属性,记住,引用这些属性以前,必须先引用命名空间:
xmlns:mytogglebtn="http://schemas.android.com/apk/res/com.example.slidebutton"
其中:mytogglebtn 是任意取名,没有强制要求,可是在控件中引用属性的时候,要保持一致,不要写错了
com.example.slidebutton 是工程的包名,千万不要弄错了,否则找不到属性文件
[html] view plain copy
print?
- <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中获取相关的属性值,并用于初始化自定义控,如下是主要代码:
[java] view plain copy
print?
- 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代码里设置就好 了,方便多了。可是在特定的场合下,若是开发的控件某些数据不肯定,或者所开发控件须要提供给其余人进行偏好设置什么的,这种自定义属性就显得非用不可 了。