最近修改一个python写的小工具,用作移除Android项目中没用到的资源如string, color等等。在做的过程中,style和styleable一直困扰不清,后面查了一下stackoverflow http://stackoverflow.com/questions/4585808/difference-between-declare-styleable-and-style ,总结一下这个问题。
一、style的作用
首先需要了解style的作用是什么!Android developer http://developer.android.com/intl/zh-CN/guide/topics/ui/themes.html 给出了一个例子我觉得很说明问题。
如果我们需要定义很多个这样的TextView:
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="#00FF00" android:typeface="monospace" android:text="@string/hello" />
为了便于修改,我们可以修改成这样:
<TextView style="@style/CodeFont" android:text="@string/hello" />
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="CodeFont" parent="@android:style/TextAppearance.Medium"> <item name="android:layout_width">fill_parent</item> <item name="android:layout_height">wrap_content</item> <item name="android:textColor">#00FF00</item> <item name="android:typeface">monospace</item> </style> </resources>
这样,我们只要采用CodeFont的style的view都会有一样的外观,而且很容易修改。所以,style可以理解成自定义外观,重复利用代码的作用。
至于styleable,我们注意到它是在attrs.xml中定义的,所以我们可以想象,它可能是用于定义attr在style里面用。具体的区别马上就讲。
二、declare-styleable 和 style 的区别
为什么在attrs.xml里面会有styleable的定义呢?
我们注意到上文中的CodeFont的定义,有没有发现item里面的name都是android开头?因为这些属性都是在android中预先设定好的,所以我们可以随意用。但是如果我们想用自己定义的属性名呢?这时候styleable的作用就出现了。我们只需要把attr的定义包围在styleable里面,这样定义的属性名就可以在style里面用。示例如下(示例来自上文给出的stackoverflow链接):
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="attrib1" format="string" /> <declare-styleable name="blahblah"> <attr name="attrib2" format="string" /> </declare-styleable>
在attrs.xml里面定义了两个attr,attrib1是普通的,attrib2包围在declare-styleable标签中;
<com.custom.ViewClass attrib1="xyz" attrib2="abc"/>
我们可以在layout/someactivity.xml里直接使用这些attr;
<style name="customstyle" parent="@android:style/Widget.TextView"> <item name="attrib2">text value</item> <!-- customize other, standard attributes too: --> <item name="android:textColor">@color/white</item> </style>
在styles.xml中,我们就能用attrib2。(原网站这里写成了attrib1,怀疑是笔误。)
后来我验证过attrib1也能使用在style里面(我真的不确定,逻辑上应该不能才对,但是编译就是通过了。。。),那么这里就必须说明attr包不包含在styleable里面的另一个主要区别了,stackoverflow中是这么说的:
The constructor of the custom class needs to read the styled and the non-styled attributes in a different way: the styled attributes with
obtainStyledAttributes()
, and the non-styled attributes withattr.getAttributeValue()
or similar.
也就是说,获取两者的方式不一样。获取attrib1用attr.getAttributeValue(),而获取attrib2用obtainStyledAttributes()。也就是说,styleable属性的获取方式具体可以参见android developer http://developer.android.com/intl/zh-CN/training/custom-views/create-view.html 这里我截取一段代码加以说明它的一般流程:
public PieChart(Context ctx, AttributeSet attrs) { super(ctx, attrs); //得到styleable的全部值,返回是一个TypedArray。注意R.styleable.PieChart TypedArray a = context.getTheme().obtainStyledAttributes( attrs, R.styleable.PieChart, 0, 0); try { //得到styleable里面的属性值。R.styleable.PieChart_showText mShowText = a.getBoolean(R.styleable.PieChart_showText, false); mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0); } finally { a.recycle(); } }
为了保证a.recycle()被调用,把它用finally包起来。
三、怎么移除与declare-styleable相关attr无用资源
最后简单介绍一下我的工作,怎么移除这里面的无用资源呢?
注意到,decleare-styleable中定义的attr在R.java中出现在两个地方:1)在class attr 中又有对应的变量;2)在class styleable中也有对应的变量。举个例子(截取的项目里面的一小部分,有改动):
我们在分析哪些attr没用到的时候是这么做的:
- 解析R.java,将class attr和class styleable中的变量分别存在两个集合里面,比如UnusedAttrSet和UnusedStyleableSet;
- 解析src下的代码和res下的代码,得到两个集合UsedAttrSet和UsedStyleableSet;
- 将UsedSet合并。因为用到的attr的可能是在styleable里面定义的,所以attr用了表示styleable也用了;用到styleable的肯定表示attr对应的量也用到了。
- 最后分别打印出attr和styleable中没有用到的量。
这里有些同学可能会问,styleable集合肯定包含在attr里面吧?为什么还要找styleable呢?是的,可以直接判断attr有没有用到,这里我把它们独立主要是为了比如有个变量在attr和styleable中都没有用到,我就只需要去查找styleable就行了。这在调试代码的时候有用。
处理的时候需要关注一下class attr和class styleable中的变量名略有不同。具体的实现如果有机会我专门开帖子来说明。