2012年12月6日木曜日

グラデーション画像からAndroidのXMLを自動生成する

このエントリーをはてなブックマークに追加
Androidでは、画像ファイルをそのままアプリにはめこむのではなく、XMLでスタイルを定義することで、画像を作成することができます。

ここで、デザイナが下記のような画像ファイルでボタンを作成したとします。

 
が、エンジニアは、ここで困ります。XMLでグラデーションを描画するためには、グラデーションの始まりと終わりの色の16進数を入力する必要があるからです。エンジニアがカラーピッカーを立ち上げ、RGBを取得し、16進数に変換する・・・なんて非常に手間がかかります。

というわけで、画像を引数として与えると、XMLを生成してくれるPythonスクリプトを書きました。画像ライブラリとしてPILが必要です。
GitHub

python generateDrawableXML.py [img_path]
#! /usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from PIL import Image

def getCenterList(_list, order=1):
    if order > 0:
        return [i for (i, x) in enumerate(_list) if x == max(_list)]
    else:
        return [i for (i, x) in enumerate(_list) if x == min(_list)]

def getHexColor(rgb):
    return '#FF' + getHexStr(rgb[0]) + getHexStr(rgb[1]) + getHexStr(rgb[2])

def getHexStr(color):
    hexStr = hex(color)[2:].upper()
    return hexStr if len(hexStr) == 2 else '0' + hexStr

def main(path):
    img = Image.open(path)
    img = img.resize((1, img.size[1]))
    img = img.convert("RGB")

    rgbs        = list(img.getdata())
    height      = len(rgbs)
    startRGB    = rgbs[0]
    endRGB      = rgbs[len(rgbs) - 1]
    centerRGB   = None
    centerIndex = 0

    rs, gs, bs = [], [], []
    for rgb in rgbs:
        rs.append(rgb[0])
        gs.append(rgb[1])
        bs.append(rgb[2])

    for i in [-1, 1]:
        centerSet = set(getCenterList(rs, i)) & set(getCenterList(gs, i)) & set(getCenterList(bs, i))
        if len(centerSet) == 0: continue
        index = list(centerSet)[0]
        if index != 0 and index != (height - 1):
            centerRGB   = rgbs[index]
            centerIndex = index

    centerStr = ""
    if centerRGB:
        centerStr = """
        android:centerColor="%s"
        android:centerY="%s"
        """ % (getHexColor(centerRGB), round(float(centerIndex) / float(height - 1), 1))


    print """
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="270"
        android:type="linear"
        android:startColor="%s"
        android:endColor="%s"%s
        />
</shape>""" % (getHexColor(startRGB), getHexColor(endRGB), centerStr)

if __name__ == '__main__':
    argvs = sys.argv
    if len(argvs) != 2:
        print("python " + argvs[0] + " [img_path]")
    else:
        main(argvs[1])

実行すると下記XMLが生成されます。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="270"
        android:type="linear"
        android:startColor="#FF330F55"
        android:endColor="#FFC45D19"
        />
</shape>

また、center要素がある画像でも大丈夫。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="270"
        android:type="linear"
        android:startColor="#FFFFC081"
        android:endColor="#FFFFA349"
        android:centerColor="#FFFF8101"
        android:centerY="0.6"
        />
</shape>

※横方向のグラデーションや、円型のグラデーションには対応していません。