事情起源
最近做的Android项目中出现了一个这样的需求:不同宽度的设备上,控件的尺寸按比例缩放。也就是说,假如在360dp宽的设计稿上,某个Button的宽度为120dp,那么到了480dp宽的设备上,这个Button的宽度应该拉成160dp。一开始我想到了几种思路:
- 全部用layout_weight,但是这样太麻烦了
- 百分比布局,还是一样,需要计算百分比,太麻烦
- 鸿洋_推出的AutoLayout,但我一看Github上的issues,似乎很多坑的样子,不敢试
- 布局里面的宽高全部写到dimens.xml中,然后为不同宽度的设备写多套dimens.xml,这样做比较麻烦,但现在看来是最有效的办法
也就是说,我会把一个Button的布局代码写成这样(所有长度单位全部写到dimens.xml里):
<Button
android:layout_marginTop="@dimen/login_button_margin_top"
android:id="@+id/id_button_login"
android:layout_width="@dimen/large_button_width"
android:layout_height="@dimen/standard_widget_height"
android:layout_gravity="center"
android:textSize="@dimen/sp15"
android:text="登录"
android:textColor="@color/white"
android:gravity="center"
/>
然后在values/dimens.xml里面,定义这些单位:
<resources>
<!-- Widget Width && Height -->
<dimen name="toolbar_height">@dimen/standard_widget_height</dimen>
<dimen name="large_button_width">270dp</dimen>
<dimen name="standard_widget_height">42dp</dimen>
<!--Margin && Padding-->
<dimen name="login_button_margin_top">30dp</dimen>
</resources>
创建values-w480dp目录,在里面的dimens.xml定义(如果设计稿的屏幕宽度是360dp,那么这里乘以一个三分之四):
<resources>
<!-- Widget Width && Height -->
<dimen name="toolbar_height">@dimen/standard_widget_height</dimen>
<dimen name="large_button_width">360dp</dimen>
<dimen name="standard_widget_height">56dp</dimen>
<!--Margin && Padding-->
<dimen name="login_button_margin_top">40dp</dimen>
</resources>
但是随着项目布局文件的增多,dimens里面的标签数量也会水涨船高,到时候难道为每一个宽度的布局都去算一遍宽高吗?这显然太费人力了。
思路
于是我就想写个脚本来自动化地做这件事,人生苦短,首选武器必然是Python。一下子思路就很简单:找出所有xxdp的文本,把数值解析出来,乘以一个倍数,再写回去。
所以问题就变成了怎么把xxdp解析出来,一开始想到的是XML解析库,网上看了一下,想用xml.dom.minidom这个包,后来发现解析出来后不知道咋把数值写回去(肯定可以的,但我太懒了不想去看文档),所以干脆就用正则表达式了。我用这样一个表达式来把dimens.xml里面的单位给匹配出来:
> *(\w+)[sd]p *<
这里我假设单位都是整数,为了防止里面有空格加了个' *',这样(\w+)所匹配出来的就是dp或sp的数值了。写回去好办,把匹配出来的数值改一改再替换回去就是了。
代码
最后的代码长这样:
import re
def auto_transfrom_reg(res_path, new_res_path, t):
value_file_360p_path = res_path + '//values//dimens.xml'
new_value_file_path = res_path + new_res_path
file_handler = open(value_file_360p_path)
new_file_handler = open(new_value_file_path, 'w')
line = file_handler.readline()
while line:
match_obj = re.search(r'> *(\w+)[sd]p *<', line)
if match_obj:
dp_str = match_obj.group(0)
new_dp_str = ''
if dp_str.find('d') >= 0:
dp_value = int(dp_str[1:dp_str.find('d')])
new_dp_value = dp_value * t
new_dp_str = '>%.1fdp<' % new_dp_value
else:
dp_value = int(dp_str[1:dp_str.find('s')])
new_dp_value = dp_value * t
new_dp_str = '>%.1fsp<' % new_dp_value
print new_dp_str
new_line = re.sub(r'> *(\w+)[sd]p *<', new_dp_str, line)
new_file_handler.write(new_line)
print new_line
else:
new_file_handler.write(line)
line = file_handler.readline()
pass
if __name__ == '__main__':
auto_transfrom_reg('E://XXX//YYYY//app//src//main//res',
'//values-w480dp//dimens.xml', 480.0/360)
效果自然是完美替换: