Android 自定义View属性相关细节

 

自定义View过程中,还有这样的细节值得你去发现。...



1

概述

在自定义View中去定义属性、获取属性,相关大家肯定都不陌生,不过在整个过程中还是会涉及到一些可能被大家忽略的细节问题。

今天的文章主要讨论下面几个细节:

  1. obtainStyledAttributes 四个参数的详细的作用
  2. 自定义View中构造方法中调用初始化代码,两种写法的区别
  3. 自定义View中获取自定义属性,两种写法的区别
是不是现在在想两种写法是什么玩意?恩,看完就知道了。

2

obtainStyledAttributes详解

(1)基本用法

ok,首先看一个非常常见的写法,假设我有一个自定义View叫做:AttrView,我们随便给它定义几个属性:

res/values/attrs.xml



对于在自定义View里面获取以上的属性,那么一般我们的代码是这样的:



相信大家都很熟悉了~~

obtainStyledAttributes这个方法实际上最终调用的是:



可以看到实际上有4个参数,那么对于后两个参数到底是干嘛的呢,怎么使用呢?

我们先看第4个参数,再看第3个参数。

(2)defStyleRes

接下来,我们先看最后一个参数,最后一个参数叫`defStyleRes `,其实是用于指定一个style,如下:

我们在styles.xml里面编写


可以看到我们声明了一个style,然后我们修改我们刚才获取属性的代码,修改为:

ok,目前我们的布局文件是这样的(并未设置任何属性):



运行看下结果:

 E/TAG﹕ test1 = false

E/TAG﹕ test2 = 3333

E/TAG﹕ test3 = 278//139dp
运行结果很明显的可以看出,如果我们不在布局文件中设置任何属性,那么会从在这个style中读取相关属性。

下面接续看第三个参数

(3)defStyleAttr

注释上是这么描述的:

An attribute in the current theme that contains a

reference to a style resource



说明它是一个引用类型的属性,指向一个style,并且在当前的theme中进行设置,如下:

那么首先,我们先声明一个attr,当然还是写在attrs.xml里面:



添加了一个新的引用格式的属性`attrViewStyleRef `。

然后去styles.xml里面,找到我们所使用的theme,添加一条item:



然后修改我们关键的一行代码:



再次运行的结果为:

E/TAG﹕ test1 = true
E/TAG﹕ test2 = 10086
E/TAG﹕ test3 = 1776//888dp


那么对于第三个参数呢,实际上用的还是比较多的,比如看系统的Button,EditText,它们都会在构造里面指定第三个参数:



提供一些参数的样式,比如background,textAppearance,textColor等,所以当我们切换不同的主题时,你会发现控件的样式会发生一些变化,就是因为不同的主题,设置了一些不同的style。

那么推演到我们自定义的View,如果你的属性非常多,你也可以去提供默认的style,然后让用户去设置到theme里面即可。

那么这样的话,大家对于obtainStyledAttributes应该有了进一步的认识,不过,如果同时设置defStyleAttr与defStyleRes,那么哪个优先级更高呢?

defStyleRes的注释是这样介绍的:

A resource identifier of a style resource that supplies default values for the TypedArray, used only if defStyleAttr is 0 or can not be found in the theme.

那么可以看出,只有defStyleAttr设置为0或者在当前的theme中没有找到相关属性时,才会去defStyleRes中读取,那么很明显优先级是defStyleAttr更高,具体的实验大家自己测试。

ok,那么解决了今天的第一个问题,接下来下一个。

3

构造方法中调用初始化代码

大家可能会困惑,哪两种写法呢?

(1)第一种





(2)第二种



对于两种写法,大家都不陌生吧,肯定的,谁都见过,但是很少有人会去问:这两种有什么区别呢?

说实话,区别不大,但是在使用场景上还是需要注意的,下面简单说几个场景。

1. 如果需要设置obtainStyledAttributes的第三个参数,即`defStyleAttr`,一般会使用第一种方式,会在两个参数的构造中,去调用三个参数的构造,同时传入`defStyleAttr `。如果没有此需求,两种写法没有什么区别。

2. 继承系统已有的控件去自定义View,比如你继承Button,去做一些事情

第一种方式会覆盖掉Button默认在theme里面设置的style,相对来说第二种方式更为合适(这条是一定要注意的,切记,切记)。

那么我们简单写个例子:



你在布局文件里面去声明,最后的效果是这样的:



看到这样的效果,你信它是个按钮吗?

当然了,如果你继承系统这个View,完全不在乎它原本的样子,那么就无所谓了。这种情况呢,也是存在的,就是我要的是系统控件的特性,但是它的样子不需要,比如:https://github.com/hongyangAndroid/Android-ProgressBarWidthNumber,你可以看到这个代码,继承了ProgressBar,因为要自定义UI风格,完全不需要系统的默认的样子,于是使用了第一种方式。

4

获取自定义属性两种写法的区别

看到这个标题,你是不是很郁闷,我擦,获取自定义属性,也有两种写法?

(1) 第一种方式

略(就是我们上面的写法)。

(2)第二种方式

等你细细的看完,你可能会认为,我擦,这种写法好傻逼,直接指定index不就行了,这个for循环根本没鸟用。其实并不是这样的,这种写法有它考虑到的地方,而且系统中很多类中获取属性的方式都是这样的方式(例如:TextView)。

注:如果switch报错,可以改为if-else

不过,这种写法和第一种写法还是有一定的区别的:

  •  第一种写法,不管你有没有在布局文件中使用该属性,都会去执行getXXX方法赋值,
  • 第二种写法,只有在你布局文件中设置了该属性后,才会去调用getXXX方法赋值。
那么,有什么影响呢?

假设这么个场景:

private int attr_mode = 1;//默认为1
//然后你在写getXXX方法的时候,是这么写的:
attr_mode = array.getInt(i, 0);
可能你的自定义属性有个默认的值,然后你在写赋值的时候,一看是整形,就默默的第二个参数就给了个0,然而用户根本没有在布局文件里面设置这个属性,你却在运行时将其变为了0(而不是默认值),而第二种就不存在这个问题。当然这个场景可以由规范的书写代码的方式来避免,(把默认值提取出来,都设置对就好了)。

其实还有个场景,假设你是继承自某个View,父类的View已经对该成员变量进行赋值了,然后你这边需要根据用户的设置情况,去更新这个值,第一种写法,如果用户根本没有设置,你可能就将父类的赋值给覆盖了。

在日常编码中时候,这些细节经常被忽视,而且很难发现!

ok,文本都是一些自定义View时平时可能会忽略的东西,希望可以帮助到你。



你还遇到过哪些值得注意的地方?欢迎留言

--欢迎长按或者扫码关注--
-本公众号支持投稿,直接投递md文件或者链接至我邮箱-


    关注 android时空


微信扫一扫关注公众号

0 个评论

要回复文章请先登录注册