Android自定义属性解析
2015-10-08 14:40
591 查看
一般情况下,我们自定义一个View的时候往往会重载它的三个构造函数,如下:
另外,我们定义一个属性文件attrs.xml
下面针对这三个构造函数,我们来一一讲解:
1、public CustomView(Context context)
这个构造函数一般在代码中来定义这个CustomView对象的时候会被调用,例如:
2、public CustomView(Context context, AttributeSet attrs)
这个构造函数一般是在布局文件中使用这个CustomView的时候会被调用,AttributeSet对应的就是设置的属性值集合,例如:
3、public CustomView(Context context, AttributeSet attrs, int defStyleAttr)
这个构造函数一般是被第一个或者第二个构造函数来调用,它的作用是当没有为自定义的属性赋值的时候,就可以使用defStyleAttr里面定义的默认属性值。
下面先来说说构造函数里面的两个参数:
1、AttributeSet attrs
这个参数里面存放的就是上面布局文件中CustomView所定义的属性。例如:android:layout_width、android:layout_height、app:text、app:num。
所以在布局文件中使用CustomView的时候,会调用第二个构造函数,并且将CustomView里面所赋值的属性封装在AttributeSet里面传递给第二个构造函数。
2、int defStyleAttr
我们自定义的CustomView的属性值也可以在Theme中进行指定,我们知道,我们在AndroidManifest文件中会为整个应用设置一个主题,在这个主题里面就可以为自定义的View定义默认的属性值,如下图:
![](https://img-blog.csdn.net/20151008143356768)
我们以CheckBox为例:
从上面可以看到它调用三个参数的构造函数的时候,传递了一个com.android.internal.R.attr.checkboxStyle值。
在frameworks/base/core/res/res/values/themes.xml文件中指定了它的值,这个值就是在Theme中设置的:
在frameworks/base/core/res/res/values/styles.xml文件里面有它的具体值:
一般情况下,我们的操作会在第三个构造函数中处理,第一个构造函数会调用第二个构造函数,第二个构造函数会调用第三个构造函数,具体如上面代码所示,那么第三个构造函数中AttributeSet和defStyle都有自定义属性的赋值怎么办?这个也不难,既然defStyle里面对应的是默认的属性值,就相当于如果我们在布局文件为自定义属性赋值了,那么AttributeSet不为空,肯定就是使用给定的属性值,如果AttributeSet为空的话,就使用默认属性,它们之间有一定的先后顺序。
另外,在构造函数中,其实还有第四个构造函数:
这个构造函数里面又多了一个参数defStyleRes,它是一个样式,我们可以为它指定一个默认的样式文件。
还是举例子:
它们三个参数直接的优先级别为:
attrs > defStyleAttr > defStyleRes
下面我们来验证一下AttributeSet是否如上面所说,存放的是布局文件里面CustomView的属性值。
运行结果如下:
![](https://img-blog.csdn.net/20151008143639168)
但是通常情况下,我们使用TypedArray来获取里面的属性值,原因是因为TypedArray其实是用来简化我们的工作的,比如,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。
下面我们来看看怎样使用TypedArray,还是举个例子,看看CustomView的构造函数。
它使用了obtainStyledAttributes函数来进行处理。
下面来看看现在来看看这个函数的声明吧:
obtainAttributes(AttributeSet set, int[] attrs)
obtainStyledAttributes(int[] attrs)
obtainStyledAttributes(int resId,int[] attrs)
obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
这四个声明跟上面的四个构造函数是一一对应的,里面的参数也是基本对应的,唯一不同的就是它里面多了一个int[] attrs,它是自定义属性数组。
下面来分别对这几个函数进行一下说明:
1、obtainAttributes(AttributeSet set, int[] attrs)
AttributeSet就是局文件中得到的CustomView的属性集合,attrs是自定义的属性数组,因为AttributeSet得到的是CustomView里面所有设置过的属性,例如上面布局文件中android:layout_width、android:layout_height、app:text、app:num,通过attrs这个自定义的属性数组,我们就可以从所有设置的属性中筛选出我们自定义的属性值。
总结一句就是:从layout设置的属性集中获取attrs中的属性
2、obtainStyledAttributes(int[] attrs)
从系统主题中获取attrs中的属性
3、obtainStyledAttributes(int resId,int[] attrs)
从资源文件定义的style中读取attrs中的属性
4、obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
按照上面的优先级来获取来获取attrs中的属性值
从上面的CustomView的构造函数中,我们可以看到int[] attrs数组为R.styleable.CustomView。
下面我们来看看项目中的R文件,把有用的内容整理如下:
从这里我们可以看到系统帮我们做的一系列工作。
现在我们就知道上面使用declare-styleable的原因就是因为它把不同类别的属性进行分组,其实没有其他作用,每一个declare-styleable对应一个数组,数组里面存放的就是对应的属性id,在获取对应属性值的时候是根据属性的索引来得到的,所以上面的CustomView_text、CustomView_num对应的就是索引值。
参考文章:
深入理解Android 自定义attr Style styleable以及其应用
Android 深入理解Android中的自定义属性
public class CustomView extends View { public CustomView(Context context) { this(context, null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
另外,我们定义一个属性文件attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomView"> <attr name="text" format="string" /> <attr name="num" format="integer"/> </declare-styleable> </resources>
下面针对这三个构造函数,我们来一一讲解:
1、public CustomView(Context context)
这个构造函数一般在代码中来定义这个CustomView对象的时候会被调用,例如:
CustomView customView = new CustomView(context);
2、public CustomView(Context context, AttributeSet attrs)
这个构造函数一般是在布局文件中使用这个CustomView的时候会被调用,AttributeSet对应的就是设置的属性值集合,例如:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.xxx.cn.customattr.CustomView android:layout_width="wrap_content" android:layout_height="wrap_content" app:text="Mirhunana" app:num="20"/> </RelativeLayout>
3、public CustomView(Context context, AttributeSet attrs, int defStyleAttr)
这个构造函数一般是被第一个或者第二个构造函数来调用,它的作用是当没有为自定义的属性赋值的时候,就可以使用defStyleAttr里面定义的默认属性值。
下面先来说说构造函数里面的两个参数:
1、AttributeSet attrs
这个参数里面存放的就是上面布局文件中CustomView所定义的属性。例如:android:layout_width、android:layout_height、app:text、app:num。
所以在布局文件中使用CustomView的时候,会调用第二个构造函数,并且将CustomView里面所赋值的属性封装在AttributeSet里面传递给第二个构造函数。
2、int defStyleAttr
我们自定义的CustomView的属性值也可以在Theme中进行指定,我们知道,我们在AndroidManifest文件中会为整个应用设置一个主题,在这个主题里面就可以为自定义的View定义默认的属性值,如下图:
我们以CheckBox为例:
public CheckBox(Context context,) { this(context, null); } public CheckBox(Context context, AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.checkboxStyle); } public CheckBox(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }
从上面可以看到它调用三个参数的构造函数的时候,传递了一个com.android.internal.R.attr.checkboxStyle值。
在frameworks/base/core/res/res/values/themes.xml文件中指定了它的值,这个值就是在Theme中设置的:
<item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox</item>
在frameworks/base/core/res/res/values/styles.xml文件里面有它的具体值:
<style name="Widget.CompoundButton.CheckBox"> <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item> </style>
一般情况下,我们的操作会在第三个构造函数中处理,第一个构造函数会调用第二个构造函数,第二个构造函数会调用第三个构造函数,具体如上面代码所示,那么第三个构造函数中AttributeSet和defStyle都有自定义属性的赋值怎么办?这个也不难,既然defStyle里面对应的是默认的属性值,就相当于如果我们在布局文件为自定义属性赋值了,那么AttributeSet不为空,肯定就是使用给定的属性值,如果AttributeSet为空的话,就使用默认属性,它们之间有一定的先后顺序。
另外,在构造函数中,其实还有第四个构造函数:
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
这个构造函数里面又多了一个参数defStyleRes,它是一个样式,我们可以为它指定一个默认的样式文件。
还是举例子:
<style name="default_style"> <item name="text">Mirhunana/item> <item name="num">20</item> </style>
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, R.style.default_style);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
它们三个参数直接的优先级别为:
attrs > defStyleAttr > defStyleRes
下面我们来验证一下AttributeSet是否如上面所说,存放的是布局文件里面CustomView的属性值。
package com.xxx.cn.customattr; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.View; public class CustomView extends View { private static final String TAG = "CustomView"; public CustomView(Context context) { this(context, null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); int count = attrs.getAttributeCount(); for (int i = 0; i < count; i++) { String attrName = attrs.getAttributeName(i); String attrVal = attrs.getAttributeValue(i); Log.e(TAG, "attrName = " + attrName + " , attrVal = " + attrVal); } } }
运行结果如下:
但是通常情况下,我们使用TypedArray来获取里面的属性值,原因是因为TypedArray其实是用来简化我们的工作的,比如,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。
下面我们来看看怎样使用TypedArray,还是举个例子,看看CustomView的构造函数。
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, 0); String text = a.getString(R.styleable.CustomView_text); int num = a.getInt(R.styleable.CustomView_num, 0); Log.e(TAG, "text = " + text +", num = " + num); }
它使用了obtainStyledAttributes函数来进行处理。
下面来看看现在来看看这个函数的声明吧:
obtainAttributes(AttributeSet set, int[] attrs)
obtainStyledAttributes(int[] attrs)
obtainStyledAttributes(int resId,int[] attrs)
obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
这四个声明跟上面的四个构造函数是一一对应的,里面的参数也是基本对应的,唯一不同的就是它里面多了一个int[] attrs,它是自定义属性数组。
下面来分别对这几个函数进行一下说明:
1、obtainAttributes(AttributeSet set, int[] attrs)
AttributeSet就是局文件中得到的CustomView的属性集合,attrs是自定义的属性数组,因为AttributeSet得到的是CustomView里面所有设置过的属性,例如上面布局文件中android:layout_width、android:layout_height、app:text、app:num,通过attrs这个自定义的属性数组,我们就可以从所有设置的属性中筛选出我们自定义的属性值。
总结一句就是:从layout设置的属性集中获取attrs中的属性
2、obtainStyledAttributes(int[] attrs)
从系统主题中获取attrs中的属性
3、obtainStyledAttributes(int resId,int[] attrs)
从资源文件定义的style中读取attrs中的属性
4、obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
按照上面的优先级来获取来获取attrs中的属性值
从上面的CustomView的构造函数中,我们可以看到int[] attrs数组为R.styleable.CustomView。
下面我们来看看项目中的R文件,把有用的内容整理如下:
public final class R { public static final class styleable { public static final int[] CustomView = { 0x7f010025, 0x7f010026 }; public static final int CustomView_text = 0; public static final int CustomView_num = 1; } public static final class attr { public static final int text=0x7f010025; public static final int num=0x7f010026; } }
从这里我们可以看到系统帮我们做的一系列工作。
<declare-styleable name="CustomView"> <attr name="text" format="string" /> <attr name="num" format="integer"/> </declare-styleable>
现在我们就知道上面使用declare-styleable的原因就是因为它把不同类别的属性进行分组,其实没有其他作用,每一个declare-styleable对应一个数组,数组里面存放的就是对应的属性id,在获取对应属性值的时候是根据属性的索引来得到的,所以上面的CustomView_text、CustomView_num对应的就是索引值。
参考文章:
深入理解Android 自定义attr Style styleable以及其应用
Android 深入理解Android中的自定义属性
相关文章推荐
- Android布局优化之过度绘制
- Android布局优化之过度绘制
- Android notification ticker text 详解
- android获得当前view在屏幕中坐标的方法
- android缓存框架ASimpleCache
- Android Studio 导入问题总结-IT蓝豹
- 【转】Android开发中Handler的使用
- Android AlarmManager 锁屏显示闹钟
- Android布尔型配置存储优化
- Android基础之android截屏学习
- Android实现定制桌面的方法
- Android Studio 导入问题总结-IT蓝豹
- Android rotation 转屏过程分析
- Android Studio项目目录结构介绍
- 如何导入support_V4包中源码
- Android 中的DisplayMetrics类的用法
- android studio 常用快捷键集合
- Android Please ensure that adb is correctly located at问题解决
- Android 之 下拉框(Spinner)的使用
- Android6.0 运行时权限(runtime permission)