事情起源
最近做的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)
效果自然是完美替換:
對比