1.obtainStyledAttributes 后两个参数的详细的作用
defStyleAttr:
说明它是一个引用类型的属性,指向一个style,并且在当前的theme中进行设置,
当我们切换不同的主题时,你会发现控件(Button,TextView...)的样式会发生一些变化,就是因为不同的主题,设置了一些不同的style。
defStyleRes:
如果我们不在布局文件中设置任何属性,那么会从在这个style中读取相关属性。
别废话上代码:
defStyleRes
其实是用于指定一个style,如下:
我们在styles.xml里面编写
可以看到我们声明了一个style,然后我们修改我们刚才获取属性的代码,修改为:
ok,目前我们的布局文件是这样的(并未设置任何属性):
运行看下结果:
E/TAG﹕ test1 = false
E/TAG﹕ test2 = 3333
E/TAG﹕ test3 = 278//139dp
运行结果很明显的可以看出,如果我们不在布局文件中设置任何属性,那么会从在这个style中读取相关属性。
首先,我们先声明一个attr,当然还是写在attrs.xml里面:
我们再来看看defStyleAttr
添加了一个新的引用格式的属性`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更高,具体的实验大家自己测试。
2.自定义View中构造方法中调用初始化代码,两种写法的区别
第一种:
第二种:
这两种有什么区别呢?
说实话,区别不大,但是在使用场景上还是需要注意的,下面简单说几个场景。
1. 如果需要设置obtainStyledAttributes的第三个参数,即`defStyleAttr`,一般会使用第一种方式,会在两个参数的构造中,去调用三个参数的构造,同时传入`defStyleAttr `。如果没有此需求,两种写法没有什么区别。
2. 继承系统已有的控件去自定义View,比如你继承Button,去做一些事情
第一种方式会覆盖掉Button默认在theme里面设置的style,相对来说第二种方式更为合适(这条是一定要注意的,切记,切记)。
那么我们简单写个例子:
你在布局文件里面去声明,最后的效果是这样的:
看到这样的效果,你信它是个按钮吗?
当然了,如果你继承系统这个View,完全不在乎它原本的样子,那么就无所谓了。这种情况呢,也是存在的,就是我要的是系统控件的特性,但是它的样子不需要,比如:https://github.com/hongyangAndroid/Android-ProgressBarWidthNumber,你可以看到这个代码,继承了ProgressBar,因为要自定义UI风格,完全不需要系统的默认的样子,于是使用了第一种方式。
(楼主这两天一直纳闷为啥使用2个参数的构造函数使用第二种构造方式构造的ProgressBar的getProgress一直是0,而鸿洋用第一种方式构造就不是0。原来是使用第二种延续的progress的默认样式,就是一直是圈的那种永动型,自然就是0了,解决办法可以指定非永动的样式或者使用第一种构造方式覆盖掉默认样式 style="?android:attr/progressBarStyleHorizontal")
看到这个标题,你是不是很郁闷,我擦,获取自定义属性,也有两种写法?
(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时平时可能会忽略的东西,希望可以帮助到你。