​

最近在模仿​​今日头条​​,发现它的很多属性都是通过自定义控件并设定相关的配置属性进行配置,于是便查询了解了下declare-styleable,下面我把自己的使用感受和如何使用进行说明下。


declare-styleable:declare-styleable是给自定义控件添加自定义属性用的。

官方的相关内部控件的配置属性文档:​​http://developer.android.com/reference/android/R.styleable.html​


如果不知道如何查看源码:​​点击这里​


起初,在自定义控件的时候,会要求构造3个方法中的一个或多个,好比我自定义的控件PersonView,

  ​


  1. public PersonView(Context context) {
  2. super(context);
  3. // TODO Auto-generated constructor stub
  4. }

  5. public PersonView(Context context, AttributeSet attrs, int defStyle) {
  6. super(context, attrs, defStyle);
  7. // TODO Auto-generated constructor stub
  8. }

  9. public PersonView(Context context, AttributeSet attrs) {
  10. super(context, attrs);
  11. }


public PersonView(Context context) {   super(context);   // TODO Auto-generated constructor stub  }   public PersonView(Context context, AttributeSet attrs, int defStyle) {   super(context, attrs, defStyle);   // TODO Auto-generated constructor stub  }   public PersonView(Context context, AttributeSet attrs) {   super(context, attrs); }

其中的AttributeSet attrs一般都没给它配置和使用,所以不知道这个东西到底怎么用,后来查看源码发现,这个配置在默认情况下使用的是系统自己的默认配置,一旦你直接设定了它的属性,默认属性就会被你的赋值所替代。


下面我们拿TextView的源码看看AttributeSet是如何进行操作的。

初始化时候,在布局文件中写android:text="拉拉";

android  自定义控件 使用declare-styleable进行配置属性(源码角度)_android


初始化TextView的时候,它的类中的属性都会初始化;


android  自定义控件 使用declare-styleable进行配置属性(源码角度)_android_02


接着往下看,你可以看到以下代码:



 


  1. TypedArray a = theme.obtainStyledAttributes(
  2. attrs, com.android.internal.R.styleable.TextViewAppearance, defStyle, 0);
  3. TypedArray appearance = null;
  4. int ap = a.getResourceId(
  5. com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
  6. a.recycle();
  7. if (ap != -1) {
  8. appearance = theme.obtainStyledAttributes(
  9. ap, com.android.internal.R.styleable.TextAppearance);


TypedArray a = theme.obtainStyledAttributes(                     attrs, com.android.internal.R.styleable.TextViewAppearance, defStyle, 0);         TypedArray appearance = null;         int ap = a.getResourceId(                 com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);         a.recycle();         if (ap != -1) {             appearance = theme.obtainStyledAttributes(                     ap, com.android.internal.R.styleable.TextAppearance);

这个就是系统在默认的资源文件R.styleable中去获取相关的配置。


如果appearance不为空,它就会去寻找获取相关属性,接着往下看。

android  自定义控件 使用declare-styleable进行配置属性(源码角度)_自定义_03

此时的text = ""; 就是准备输出的字符串初始化。

之后它便会查找你布局文件XML中是否设定给了它text属性值

android  自定义控件 使用declare-styleable进行配置属性(源码角度)_自定义_04

之前我们设定过android:text="拉拉"; 所以它便会得到相关的赋值,之后调用


 ​


  1. <span style="font-size:18px;"> setText(text, bufferType);
  2. if (hint != null) setHint(hint);
  3. </span>


<span style="font-size:18px;"> setText(text, bufferType);  if (hint != null) setHint(hint); </span>

输出该字符串。当资源检查赋值完毕后,调用a.recycle();释放。 同理也可以发现,像hint,textcolor这类属性都是这么初始化赋值的。



思路:

自定义控件并且自定义属性的情况下,你可以通过这样去获取判断是否配置了相关的属性,并进行赋值操作。


从源码那边我们大体知道了一个控件的属性配置和初始化流程,下面就让我们按照这个思路去自己学习下如何自定义配置。



下面我要写一个继承了TextView的PersonView类,给它设定属性配置,之后实现属性的显示。


1.首先,先写attrs.xml

在res-vlaues文件夹下创建资源文件attrs.xml或则自定义一个资源文件xx.xml,都可以。

 


  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="PersonAttr">
  4. <attr name="name" format="reference" />
  5. <attr name="sex" format="reference" />
  6. <attr name="age" format="integer" />
  7. <attr name="weight">
  8. <flag name="fat" value="2" />
  9. <flag name="mid" value="1" />
  10. <flag name="thin" value="0" />
  11. </attr>
  12. <attr name="adult" format="boolean" />
  13. <attr name="textSize" format="dimension" />
  14. </declare-styleable>
  15. </resources>


<?xml version="1.0" encoding="utf-8"?> <resources>     <declare-styleable name="PersonAttr">         <attr name="name" format="reference" />         <attr name="sex" format="reference" />         <attr name="age" format="integer" />         <attr name="weight">             <flag name="fat" value="2" />             <flag name="mid" value="1" />             <flag name="thin" value="0" />         </attr>         <attr name="adult" format="boolean" />         <attr name="textSize" format="dimension" />     </declare-styleable> </resources>

我这里设置了姓名name,性别sex,年龄age,以及特征属性weight(fat,mid,thin内部的3个属性及对应的属性值),还有是否成年adult,和TextView的字体大小textView。


可能这里有人会问,format是什么,里面的单词代表的又是什么意思。

format就是格式,里面的就是这个属性对应的格式,下面列出来大致的格式有:


1. reference:参考某一资源ID,以此类推

(1)属性定义:

<declare-styleable name = "名称">

<attr name = "background" format = "reference" />

</declare-styleable>

(2)属性使用:

<ImageView

android:layout_width = "42dip"

android:layout_height = "42dip"

android:background = "@drawable/图片ID"

/>

2. color:颜色值

<declare-styleable name = "名称">

<attr name = "textColor" format = "color" />

</declare-styleable>

3. boolean:布尔值

<declare-styleable name = "名称">

<attr name = "focusable" format = "boolean" />

</declare-styleable>

4. dimension:尺寸值。注意,这里如果是dp那就会做像素转换

<declare-styleable name = "名称">

<attr name = "layout_width" format = "dimension" />

</declare-styleable>

5. float:浮点值。

6. integer:整型值。

7. string:字符串

8. fraction:百分数。

9. enum:枚举值

10. flag:是自己定义的,类似于 android:gravity="top",就是里面对应了自己的属性值。

11. reference|color:颜色的资源文件。 12.reference|boolean:布尔值的资源文件

注意://由于reference是从资源文件中获取:所以在XML文件中写这个属性的时候必须 personattr:name="@string/app_name"这种格式,否则会出错


2.设置好属性文件后,在使用的布局中写相关配置:


 


  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:personattr="http://schemas.android.com/apk/res/com.example.declare_styleable"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >

  5. <com.example.declare_styleable.PersonView
  6. android:layout_width="wrap_content"
  7. android:layout_height="wrap_content"
  8. personattr:name="@string/person_name"
  9. personattr:weight ="fat"
  10. personattr:adult ="false"
  11. personattr:textSize="@dimen/text_size"/>

  12. </RelativeLayout>


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:personattr="http://schemas.android.com/apk/res/com.example.declare_styleable"     android:layout_width="match_parent"     android:layout_height="match_parent" >      <com.example.declare_styleable.PersonView         android:layout_width="wrap_content"         android:layout_height="wrap_content"         personattr:name="@string/person_name"          personattr:weight ="fat"         personattr:adult ="false"         personattr:textSize="@dimen/text_size"/>  </RelativeLayout>

这里要先应用这个attr: [html]​view plain​​​​copy​​​​print​​​​?​​​
 ​
​​​ ​


  1. xmlns:personattr="http://schemas.android.com/apk/res/com.example.declare_styleable"


xmlns:personattr="http://schemas.android.com/apk/res/com.example.declare_styleable"


对应结构是:


 


  1. xmlns:你自己定义的名称="http://schemas.android.com/apk/res/你程序的package包名" (我这是com.example.declare_styleable)


xmlns:你自己定义的名称="http://schemas.android.com/apk/res/你程序的package包名"    (我这是com.example.declare_styleable)



包名是配置文件中 package="com.example.declare_styleable" 这样格式的

之后在布局中自定义的类中设相关属性:

你自己定义的名称:你设的属性 ="属性值";


3.最后在自定义控件的构造方法中获取你配置的属性值:

  1. public class PersonView extends TextView {
  2. public PersonView(Context context) {
  3. super(context);
  4. // TODO Auto-generated constructor stub
  5. }

  6. public PersonView(Context context, AttributeSet attrs, int defStyle) {
  7. super(context, attrs, defStyle);
  8. // TODO Auto-generated constructor stub
  9. }

  10. public PersonView(Context context, AttributeSet attrs) {
  11. super(context, attrs);
  12. // TODO Auto-generated constructor stub
  13. TypedArray tArray = context.obtainStyledAttributes(attrs,R.styleable.PersonAttr);//获取配置属性
  14. String name = tArray.getString(R.styleable.PersonAttr_name);<span style="font-family: Arial, Helvetica, sans-serif;">//得到属性name</span>
  15. int age = tArray.getInt(R.styleable.PersonAttr_age, 15);
  16. Boolean adult = tArray.getBoolean(R.styleable.PersonAttr_adult, false);
  17. String str_adult = getAdultStatus(adult);
  18. int weight = tArray.getInt(R.styleable.PersonAttr_weight, 1);// 默认是中等身材,属性为:1
  19. String str_weight = getWeightStatus(weight);//获得肥胖属性
  20. float textSize = tArray.getDimension(R.styleable.PersonAttr_textSize,R.dimen.default_text_size);// 如果你设置为DP等单位,会做像素转换
  21. tArray.recycle();//回收资源
  22. // setTextSize(textSize);//设置字体大小
  23. setText("姓名:" + name + "\n" + "年龄:" + age + "\n" + "是否成年:" + str_adult
  24. + "\n" + "体形:" + str_weight);//给自定义的控件赋值
  25. }

  26. /** 根据传入的值判断是否成年 */
  27. public String getAdultStatus(Boolean adult ){
  28. String str_adult = "未成年";
  29. if (adult) {
  30. str_adult = "成年";
  31. }
  32. return str_adult;
  33. }

  34. /** 根据传入的值判断肥胖状态 */
  35. public String getWeightStatus(int weight){
  36. String str_weight = "中等";
  37. switch (weight) {
  38. case 0:
  39. str_weight = "瘦";
  40. break;
  41. case 1:
  42. str_weight = "中等";
  43. break;
  44. case 2:
  45. str_weight = "肥胖";
  46. break;
  47. default:
  48. break;
  49. }
  50. return str_weight;
  51. }
  52. }


public class PersonView extends TextView {  public PersonView(Context context) {   super(context);   // TODO Auto-generated constructor stub  }   public PersonView(Context context, AttributeSet attrs, int defStyle) {   super(context, attrs, defStyle);   // TODO Auto-generated constructor stub  }   public PersonView(Context context, AttributeSet attrs) {   super(context, attrs);   // TODO Auto-generated constructor stub   TypedArray tArray = context.obtainStyledAttributes(attrs,R.styleable.PersonAttr);//获取配置属性   String name = tArray.getString(R.styleable.PersonAttr_name);<span style="font-family: Arial, Helvetica, sans-serif;">//得到属性name</span>   int age = tArray.getInt(R.styleable.PersonAttr_age, 15);   Boolean adult = tArray.getBoolean(R.styleable.PersonAttr_adult, false);   String str_adult = getAdultStatus(adult);   int weight = tArray.getInt(R.styleable.PersonAttr_weight, 1);// 默认是中等身材,属性为:1   String str_weight = getWeightStatus(weight);//获得肥胖属性   float textSize = tArray.getDimension(R.styleable.PersonAttr_textSize,R.dimen.default_text_size);// 如果你设置为DP等单位,会做像素转换   tArray.recycle();//回收资源 //  setTextSize(textSize);//设置字体大小   setText("姓名:" + name + "\n" + "年龄:" + age + "\n" + "是否成年:" + str_adult     + "\n" + "体形:" + str_weight);//给自定义的控件赋值  }    /** 根据传入的值判断是否成年 */  public String getAdultStatus(Boolean adult ){   String str_adult = "未成年";   if (adult) {    str_adult = "成年";   }   return str_adult;  }    /** 根据传入的值判断肥胖状态 */  public String getWeightStatus(int weight){   String str_weight = "中等";   switch (weight) {   case 0:    str_weight = "瘦";    break;   case 1:    str_weight = "中等";    break;   case 2:    str_weight = "肥胖";    break;   default:    break;   }   return str_weight;  } }

运行后就是:


android  自定义控件 使用declare-styleable进行配置属性(源码角度)_自定义_05

这样,以后我们就可以根据这个方法,去自定义控件并自定义配置属性了,大大提高了自定义布局的使用效率。


对应的源码下载地址:​​下载地址​