Flexのscale-9 gridはハマるね…

Flexでは埋め込み画像にscale-9 gridによる伸縮を適用することができます。scale-9 gridによる伸縮とは、画像を枠の部分と中央部分にグリッド分割して、画像の枠にあたる部分の拡大率を固定したまま、画像の中央部分のみを拡大縮小する画像伸縮手法です(scale-9 gridについて詳しくはこのへんを見ると概要が分かるかと)。

さて、実際この機能を使ってみると、思い通りに画像が伸縮しないという現象によく見舞われます。
最初のころはFlex Builderやコンパイラのバグを疑っていました。しかし、よくよく調べてみると、いかにもscale-9 gridが効きそうな設定にも関わらず、scale-9 gridが無効になる条件があることが分かりました。(確認したのはFlex Builder 3 + Flex SDK 3.5.0の環境です)

まず、100ピクセル×100ピクセルの画像に対して、次の赤線のようにscale-9 gridを設定することを考えます。

Embed文で書くと次のとおり。

[Embed(source='100x100.png', 
       scaleGridTop='0', 
       scaleGridRight='90', 
       scaleGridBottom='90', 
       scaleGridLeft='8')]


このEmbed文のコンパイルは問題なく通ります。しかし、scale-9 gridは全く適用されません。なぜ?

結論から言ってしまうと、画像のEmbed時に、次のいずれかの条件を満たすと、その画像に対するscale-9 gridは無効になります。

  • scaleGridTopに0を設定する
  • scaleGridLeftに0を設定する。
  • scaleGridBottomに、画像の縦幅(ピクセル数)以上の値を設定する。
  • scaleGridRightに、画像の横幅(ピクセル数)以上の値を設定する。

別のいい方をすると、上下左右の周辺領域のうち、1つでも面積ゼロの領域ができると、scale-9 grid全体が無効になります。

scaleGridTop,Left,Right,Bottomの指定値と、scale-9 gridの周辺領域の大きさの関係はとても重要です。調べたところ、次のようになっていました。

  • scaleGridTopの値=上側周辺領域の高さピクセル
  • scaleGridLeftの値=左側周辺領域の幅ピクセル
  • scaleGridRightの値=画像の幅 - 右側周辺領域の幅ピクセル
  • scaleGridBottomの値=画像の高さ - 下側周辺領域の高さピクセル

上の一覧を見て何を当り前な…と思った方は要注意!たとえばscaleGridTop='4'と指定したときに、上側周辺領域のサイズが3ピクセルになるか、4ピクセルになるか確信を持って言い切れましたか?

というわけで、scaleGridTopやscaleGridLeftに0を指定すると、上側・左側領域の面積がゼロになります。一方、scaleGridRightやscaleGridBottomに、それぞれ画像の幅・高さを指定すると、右側・下側領域の面積がゼロとなるわけです。

scale-9 gridが無効になる条件については、ヘルプなどに記載が見当たりませんが、はたしてこれが仕様なのでしょうかねぇ…。

ところでscale-9 gridが無効になったことにより、画像が思い通りに伸縮しないだけならまだ良いのですが、こいつのおかげでうっかりアプリがクラッシュすることもあります。注意すべきポイントは、scale-9 gridが有効な画像はSpriteAssetクラスのオブジェクトになるのに対し、scale-9 gridが無効になった画像はBitmapAssetオブジェクトになる点です。

scale-9 gridを適用した画像をnewするコードを考えてみます。

[Embed (source='hoge/moge.png',
        scaleGridTop='1',
        scaleGridLeft='1',
        scaleGridRight='8',
        scaleGridBottom='8')]
var imageClass:Class;
var img:SpriteAsset = new imageClass(); //※

上記のコードは問題なく動きます。
しかし、ここでもしscale-9 gridの設定を少し変更しようと思って、scaleGridTop='0'と書いてしまうと、※の行で例外が出ます。imageClassのscale-9 girdが無効になって、new imageClass()の結果がBitmapAssetになるためです。
SpriteAssetとBitmapAssetは継承関係にないので、上記コードのまま実行すると型変換できずにランタイムエラーとなります。