View绘制体系(三)——AttributeSet与TypedArray详解


前言

上篇博客中讲了LayoutInflater.inflate机制,其中提到了AttributeSetXmlPullParser两个接口,这里我们来详细的了解一下Android中提供的AttributeSet接口和它与XmlPullParser的区别,以及如何使用TypedArray获取AttributeSet中对应的属性。


AttributeSet

AttributeSet是xml文件中元素属性的一个集合。其中提供了各种Api,供我们从已编译好的xml文件获取属性值,如getAttributeIntValuegetAttributeBooleanValuegetAttributeFloatValue等,会返回对应类型的属性值,传入的参数一般有两种形式,如下:

  • getAttributeXXXValue(int index, XXX defaultValue):根据对应属性的索引获取对应的属性值,index取值范围在0~count-1之间,找不到返回defaultValue
  • getAttributeXXXValue(String namespace, String attribute, XXX defaultValue):根据指定命名空间的属性名获取对应的属性值,找不到返回defaultValue

AttributeSet与XmlPullParser

我们现在知道了AttributeSet也是获取xml文件中属性值用的接口,那么它和XmlPullParser有什么关联和区别呢?

我们先看下下面这张类图:

XmlPullParser和AttributeSet都能从Xml文件中获取数据,它们的相同点为:

  • 它们有一些重复的方法,如getAttributeNamegetAttributeValue等。

区别在于:

  • AttributeSet提供了额外的一系列getAttributeXXXValue的方法(如getAttributeIntValue),这些方法能返回对应XXX的类型值(如int值),而XmlPullParser获取属性值只能通过getAttributeValue方法,返回值只能是String类型
  • AttributeSet接口从XmlPullParser接口中只保留了必要的方法,去除了next()等方法,并且新增了一些Android独有的方法,可以说AttributeSet是Android独有的用来获取xml文件属性的一个接口

需要注意的是,在Android中,对于AttributeSet接口和XmlPullParser接口,其实现都是结合已有的编译好的xml资源,这些资源是编译时经过aapt生成的高度优化的资源,而不是通过pull方式解析原有的Xml字符串。这与我们常见的一般的XmlPullParser接口的实现机制(kXml,WbXml等)不同。

我们从类图中可以看到实现类有两个,分别是ParserXmlPullAttributes,我们可以看下XmlPullAttributes的具体实现:

class XmlPullAttributes implements AttributeSet {XmlPullParser mParser;public XmlPullAttributes(XmlPullParser parser) {mParser = parser;}public int getAttributeCount() {return mParser.getAttributeCount();}public String getAttributeNamespace (int index) {return mParser.getAttributeNamespace(index);}public String getAttributeName(int index) {return mParser.getAttributeName(index);}public String getAttributeValue(int index) {return mParser.getAttributeValue(index);}public String getAttributeValue(String namespace, String name) {return mParser.getAttributeValue(namespace, name);}public String getPositionDescription() {return mParser.getPositionDescription();}public int getAttributeNameResource(int index) {return 0;}public int getAttributeListValue(String namespace, String attribute,String[] options, int defaultValue) {return XmlUtils.convertValueToList(getAttributeValue(namespace, attribute), options, defaultValue);}//...其余AttributeSet中的接口都是类似getAttributeListValue通过XmlUtils转换类型来实现的}

可以看到XmlPullAttributes对于AttributeSet中和XmlPullParser相同的接口都是通过内部的XmlPullParser变量来实现的。

XmlPullParser还可以通过以下方式生成对应的AttributeSet:

public static AttributeSet asAttributeSet(XmlPullParser parser) {return (parser instanceof AttributeSet)? (AttributeSet) parser: new XmlPullAttributes(parser);
}

TypedArray

然而,我们很少用到AttributeSet类提供的方法来获取它的属性,常常都是通过Theme.obtainStyledAttributes方法来获取其中的属性的,方法如下:

public TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)

获取到TypedArray之后,再去调用TypedArray的Api来处理。

为什么不直接用AttributeSet来解析属性值呢?

我们可以看下面这个xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="@dimen/w_50dp"android:layout_height="@dimen/w_50dp"android:orientation="vertical"/>

我们通过AttributeSet直接去获取属性的话:

final XmlResourceParser parser = getResources().getLayout(R.layout.ll);
int type;
while ((type = parser.getEventType()) != XmlPullParser.START_TAG&& type != XmlPullParser.END_DOCUMENT) {parser.next();
}
int count = parser.getAttributeCount();
Log.d(TAG, "count: " + parser.getAttributeCount());AttributeSet attributeSet = Xml.asAttributeSet(parser);for (int i = 0; i < count; i++) {String name = attributeSet.getAttributeName(i);String value = attributeSet.getAttributeValue(i);Log.d(TAG, "attribute: " + name + "   value: " + value);
}int resourceId = attributeSet.getAttributeResourceValue("http://schemas.android.com/apk/res/android", "layout_height", 0);
float dimension = getResources().getDimension(resourceId);
Log.d(TAG, "layout_height: " + dimension);

得到的结果如下:

count: 3
attribute: orientation   value: 1
attribute: layout_width   value: @2131034226
attribute: layout_height   value: @2131034226
layout_height: 150.0  #得到的是px

可以看到如果直接使用AttributeSet来获取属性值有以下缺点:

  • 通过索引index获取元素属性颇为麻烦,而且对于属性的顺序不熟悉的人很难将将index和属性值一一对应;而通过命名空间来获取,又要必须填入android的命名空间"http://schemas.android.com/apk/res/android",编写起来都是不太方便的
  • 对于"@String/hello_world"这一类通过索引资源文件获取属性值的,直接通过getAttributeValue获得的只是资源的id,虽然可以通过getAttributeResourceValue获取到id后,通过Resources类来获取对应的属性值,但是这样会有多步的操作。

我们来看看使用TypedArray是如何获取属性值的。TypedArray是一个存放属性值数组的一个容器,通常通过如下方式来获得TypedArray的:

Resources.Theme.obtainStyledAttributes(AttributeSet set,@StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)

我们来分别看下每个参数的含义:

  • AttributeSet set:表示从AttributeSet中挑选属性,可以为空
  • int[] attrs:表示你想挑选的属性,你想得到哪些属性,你就可以将其写到这个int数组中
  • int defStyleAttr:表示从defStyleAttr中挑选属性,可以为空
  • int defStyleRes:表示从defStyleRes中挑选属性,可以为空

这里我们先来演示下从AttributeSet中挑选属性:

TypedArray a = obtainStyledAttributes(attributeSet,new int[] { android.R.attr.layout_width, android.R.attr.layout_height});
float width = a.getDimension(0, 0f); //第一个参数表示在int数组中的索引,第二个参数为默认值
Log.d(TAG, "typedarray layout_width: " + width);
float height = a.getDimension(1, 0f); //第一个参数表示在int数组中的索引,第二个参数为默认值
Log.d(TAG, "typedarray layout_width: " + height);

可以得到对应的值:

typedarray layout_width: 150.0
typedarray layout_width: 150.0

TypedArray提供了各种Api,如getIntegergetStringgetDimension等方法来获取属性值,这些方法都需要传入对应属性名在obtainStyledAttributes中的int数组的位置索引。

对于TypedArray的分析,这里就只讲从AttributeSet中挑选属性的用法,从defStyleAttr和defStyleRes中挑选属性的用法详见[View绘制体系(四)——TypedArray与自定义属性]。

ps:对于AttributeSet与TypedArray的分析就到这里,后续将会介绍TypedArray与自定义属性的相关内容,敬请关注!

View绘制体系(三)——AttributeSet与TypedArray详解相关推荐

  1. android view使用方法,Android View构造方法第三参数使用方法详解

    我们都知道,在Android中要使用一个View,一般会有两种方式: 在XML文件中配置: 直接在代码中new一个View的对象. 我们今天讨论的内容就是围绕着View的构造方法的. 1.实例 首先我 ...

  2. View 绘制体系知识梳理(7) getMeasuredWidth 和 getWidth 的区别

    前言 前几天被问到了getMeasuredWidth和getWidth之间的区别,因此回来看了一下源码,又顺便复习了一遍measure/layout/draw的过程,有兴趣的同学可以看前面的几篇文章 ...

  3. 对tcp三次握手的详解之 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)

    重要 !!!!!!!!!       转载自[怀揣梦想,努力前行] 对tcp三次握手的详解之 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number) ...

  4. android 说话水波动画,Android实用View——水波动画效果多种实现方式详解

    原标题:Android实用View--水波动画效果多种实现方式详解 这次给大家带来的是一篇关于自定义View实现水波动画效果的文章,其实在去年项目中使用过类似的动画,当时就自定义View也实现了预期的 ...

  5. matlab三次样条曲线的绘制(spline和csape函数详解)

    matlab三次样条函数的绘制(spline和csape函数详解) 前言 1.spline函数详解 1.一维非节点边界 2.第二边界条件 3.高维无约束 4.高维第二边界 5.利用第二边界条件绘制圆 ...

  6. Spring Cloud Eureka 入门 (三)服务消费者详解

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! "真正的进步 ...

  7. 【Tools】VMware虚拟机三种网络模式详解和操作

    00. 目录 文章目录 00. 目录 01. VMware虚拟机三种网络模式 02. Bridged(桥接模式) 03. NAT(地址转换模式) 04. Host-Only(仅主机模式) 05. 参考 ...

  8. JavaWeb --第三章 HTTP协议详解

    JavaWeb --第三章 HTTP协议详解 Http 什么是HTTP HTTP: 超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常 ...

  9. C语言中三个数比较大小详解——三种方法

    ​ C语言中三个数比较大小详解--三种方法 方法一:if-else法 方法二:函数法 方法三:三目运算符法 C语言中比较三个数的大小有很多方法,以下是我总结的三种方法: 首先我定义 int a = 1 ...

最新文章

  1. 柯南君:看大数据时代下的IT架构(5)消息队列之RabbitMQ--案例(Work Queues起航)...
  2. 始于TensorFlow ,终于PyTorch
  3. 人脸识别有风险,美国全面禁止,可为什么中国却全面推广?
  4. BAP存储属性的思想
  5. C/C++中函数参数传递
  6. C++ 判断类是否有某变量
  7. 【云服务】浅析XaaS
  8. “C# 未在本地计算机上注册microsoft.Jet.OLEDB.12.0”的解决方案
  9. linux命令、vi编辑器常用命令
  10. 机器人控制器编程课程-教案02-基础
  11. web嵌入flowplayer流媒体播放器
  12. 带你深入剖析TCP/IP协议、TCP协议和UDP协议、IP协议
  13. TS中的方法重载,函数重载,构造器重载
  14. ssrs报表服务器数据库配置文件,ReportingServicesService 配置文件
  15. 直方图都看不懂,怎么可能拍出好照片!
  16. 《信贷的逻辑与常识》笔记
  17. 单线激光雷达(Lidar)学习一:使用单线lidar进行测距
  18. 这所北京计算机类大学,改考408了!北京信息科技大学
  19. you belong with me(你属于我)
  20. 2021苹果cms模板集合新增苹果cms首涂模板

热门文章

  1. 彩虹云六月第二版秒赞引导页模板
  2. Arcobjects for java:Arcgis二次开发入门,开发一个基本地图组件
  3. 新人学习笔记-SQL的ANSI标准
  4. 我的网站出现css控制错误,360调试出现问题
  5. Bootstrap(六)表单样式
  6. 扑克牌排序脚本 扑克牌添加花色脚本(java)
  7. 《万万没想到——用理工科的思维看世界》读书笔记(四)匹夫怎样逆袭?
  8. 人气最高的缓存中间件:Redis
  9. https://zhuanlan.zhihu.com/p/33841176
  10. 使用python绘制笛卡尔心形线