エントリー

Flex/AIR の、モバイル端末用カスタムスキンを作成しているときに、 FXG (Flash XML Graphics) の scale-9 grid (9スライス) にハマった。

そもそも scale-9 grid とは?

定義

scale-9 grid とは、日本語で言うところの 9スライス のこと。あっ、Android界隈では、この 9スライス のことを、 9-patch と呼ぶそうな。むー。

Adobe Open Source で公開されている FXG の仕様書を見ると、scale-9 gridベクター分割slice-9 gridビットマップ分割、というように、英語表記では意味合いが違ってくるので、この記事では、「9スライス」という日本語名称をできるだけ使わずに、scale-9, slice-9 grid を使い分けて表記することにします(どうしても、以下の文章内で、「9スライス」と表記している箇所がありますが、それは画像オーサリングソフトでの表記が"それ"だったり、scale-9とslice-9の総体的な意味合いの"それ"として、表記しています)。

「9スライス」ってなにがおいしいの?

たとえば、次のようなボタンの素材を、Adobe Fireworks で作ったとします。

fw_starButton_mock.png

このとき、ボタンの大きさは、固定だったら楽できて嬉しいんだけど、実際のところは、ボタン内の文字の量とかフォントサイズ等に影響を受けるので、可変。つまり、横長のボタンもあれば、正方形みたいな真四角なボタンもあるかもしれないです。なので、ボタンの大きさが任意に変わっても大丈夫なものをちゃんと用意しなきゃいけないってこと。

先ほどのボタンのベクター画像を、次のように伸縮してみると、下図のように、縦横比を固定しないで拡大/縮小した際に、問題が発生しちゃう。

fw_starButton_scale_nogrid.png

この問題を解決するために、ここで「9スライス」が大活躍!

Fireworks の場合の手順としては、下図のように、まずは「シンボル化 (オブジェクトを選択して、[F8]キー)」を行って・・・

fw_starButton_symbol.png

そんで、そのシンボルを編集して、下図のように、「9スライスのガイド」を設定ッ!(下図のガイドの設定は、あくまで例)

fw_starButton_slice_guide.png

たったのこれだけで、Fireworks での、9スライスの設定が完了ッ!!

9スライスを設定した、このボタンのベクター画像を、先ほどと同様に伸縮させてみると、下図のように、バッチリ拡大/縮小が行えているのが、見て取れるハズ・・・!?

fw_starButton_scale_9grid.png

9スライス、バンザイ!

 

Flex/AIRの、モバイル端末用カスタムボタンスキンの作成

んじゃ、こっから、どこでハマったのか、書いてみる。

ちゃんとうまいこと伸縮しない件について…

まずは、さきほど Fireworks で作成した、ボタンの素材(とりあえず、320 dpi なモバイル端末用のカスタムボタンスキンの up ステートと down ステートの 2 枚だけ)を FXG 形式で書き出しました。

Fireworks PNGExportsFlash XML Graphics

書きだした FXG ファイルのコンパイル済みコードを読みやすいようにちょっとだけ整形して、実際にレンダリングさせてみると、下のような状態です。下のレンダリング画像内の、赤色の補助線は、scale-9 grid の分割線です。

  • Button_up.fxg
  • Button_down.fxg

fxg net.chsmea.mioproject.skins.test.mobile320.assets.Button_up.fxg

fxg_starButton_up.png

001<?xml version="1.0" encoding="UTF-8"?>
002 
003<!---
004  星が付いた、ポップなボタン (up).
005   
006  <p>モバイル端末 (320 dpi) 用に最適化された,
007  コンパイル済みFXG.</p>
008   
009  @author mio
010-->
011<Graphic version="2.0"
012         xmlns="http://ns.adobe.com/fxg/2008"
013         xmlns:d="http://ns.adobe.com/fxg/2008/dt"
014         xmlns:fc="http://ns.adobe.com/flashcatalyst/2009"
015         viewHeight="106"
016         viewWidth="292"
017         scaleGridLeft="48"
018         scaleGridRight="244"
019         scaleGridTop="50"
020         scaleGridBottom="56">
021     
022    <Library>
023    </Library>
024     
025    <!-- ボタンの角丸矩形部分 -->
026    <Path data="M 22 42 C 22 31 31 22 42 22 C 42 22 250 22 250 22 C 261 22 270 31 270 42 C 270 42 270 64 270 64 C 270 75 261 84 250 84 C 250 84 42 84 42 84 C 31 84 22 75 22 64 C 22 64 22 42 22 42 Z ">
027        <fill>
028            <SolidColor color="#febf01" />
029        </fill>
030        <filters>
031            <DropShadowFilter angle="-90"
032                              color="#f0a82f"
033                              distance="5"
034                              quality="2"
035                              blurX="0"
036                              blurY="0"
037                              inner="true" />
038            <DropShadowFilter angle="90"
039                              color="#ffffff"
040                              distance="1"
041                              alpha="0.5"
042                              quality="2"
043                              blurX="0"
044                              blurY="0" />
045            <DropShadowFilter angle="90"
046                              color="#000000"
047                              distance="3"
048                              alpha="0.12"
049                              quality="2"
050                              blurX="0"
051                              blurY="0" />
052        </filters>
053    </Path>
054     
055    <!-- ボタンの左上の「☆」 -->
056    <Path data="M 35 41 C 35 41 35 41 35 41 C 35 41 35 41 35 41 C 35 41 36 41 36 41 C 36 41 36 40 36 40 C 36 39 36 38 35 37 C 35 36 35 36 35 35 C 34 34 34 33 34 32 C 33 31 33 31 33 30 C 33 30 33 29 33 29 L 34 29 C 34 29 37 25 37 25 C 37 25 37 25 37 25 C 37 24 38 24 38 24 C 38 24 38 23 39 23 C 39 23 39 23 39 22 C 39 22 40 21 40 21 L 41 20 C 41 20 41 20 41 20 C 41 20 41 19 41 19 C 41 19 41 19 41 19 C 41 19 41 19 40 18 C 40 18 39 18 38 18 C 37 17 36 17 35 17 C 34 17 33 16 32 16 C 31 16 30 16 29 15 C 29 15 28 14 28 13 C 28 12 27 11 27 10 C 27 9 26 9 26 8 C 25 7 25 6 24 5 C 24 5 23 5 23 5 C 23 5 23 5 23 5 C 22 5 22 5 22 5 C 22 5 22 5 22 5 C 21 5 21 5 21 5 C 21 5 21 6 21 6 C 20 6 20 7 20 8 C 19 9 19 10 18 10 C 18 11 17 12 17 13 C 17 14 16 15 16 15 C 15 16 14 16 13 16 C 12 17 12 17 11 17 C 10 17 9 18 8 18 C 7 18 6 19 5 19 C 5 19 5 20 5 20 C 5 20 5 21 5 21 C 6 22 6 22 7 23 C 7 23 8 24 9 24 C 9 25 10 26 10 26 C 11 27 11 27 12 28 C 12 29 11 30 11 31 C 11 31 11 32 11 33 C 10 34 10 35 10 36 C 10 37 10 37 9 38 C 9 38 9 39 9 39 C 9 39 9 39 10 39 C 10 40 10 40 10 40 C 10 40 10 40 10 40 C 10 40 11 41 11 41 C 12 41 13 40 14 40 C 15 40 16 39 17 39 C 18 38 19 38 20 38 C 20 38 23 36 23 36 C 24 36 25 37 26 37 C 27 38 28 38 29 39 C 30 39 31 40 32 40 C 33 40 33 41 34 41 C 34 41 35 41 35 41 C 35 41 35 41 35 41 Z ">
057        <fill>
058            <LinearGradient x="23" y="5"
059                            scaleX="36"
060                            rotation="90">
061                <GradientEntry color="#f2dc5e"
062                               ratio="0"
063                               alpha="1" />
064                <GradientEntry color="#e7ca12"
065                               ratio="0.99"
066                               alpha="1" />
067            </LinearGradient>
068        </fill>
069        <filters>
070            <DropShadowFilter angle="270"
071                              color="#e0bb0c"
072                              distance="5"
073                              quality="2"
074                              blurX="0"
075                              blurY="0"
076                              inner="true" />
077            <DropShadowFilter angle="90"
078                              color="#000000"
079                              distance="7"
080                              alpha="0.11"
081                              quality="2"
082                              blurX="0"
083                              blurY="0" />
084        </filters>
085    </Path>
086     
087    <!-- ボタンの右下の「☆」 -->
088    <Path data="M 276 91 C 276 91 276 91 276 91 C 276 91 277 91 277 91 C 277 91 277 91 277 91 C 277 91 277 90 277 90 C 277 89 277 89 277 88 C 276 87 276 87 276 86 C 276 85 276 85 275 84 C 275 84 275 83 275 82 C 275 82 275 82 275 82 L 275 81 C 275 81 278 79 278 79 C 278 79 278 78 278 78 C 278 78 278 78 279 78 C 279 78 279 77 279 77 C 279 77 280 77 280 77 C 280 77 280 76 280 76 L 281 75 C 281 75 281 74 281 74 C 281 74 281 74 281 74 C 281 74 281 74 281 74 C 281 74 281 74 281 73 C 280 73 279 73 278 73 C 278 73 277 72 276 72 C 276 72 275 72 274 72 C 273 72 273 71 272 71 C 272 70 271 70 271 69 C 271 68 270 68 270 67 C 270 66 269 66 269 65 C 269 64 268 64 268 63 C 268 63 267 63 267 63 C 267 63 267 63 267 63 C 266 63 266 63 266 63 C 266 63 266 63 266 63 C 266 63 266 63 266 63 C 266 63 265 64 265 64 C 265 64 265 65 264 65 C 264 66 264 67 263 67 C 263 68 263 68 262 69 C 262 70 262 70 261 71 C 261 71 260 72 259 72 C 259 72 258 72 257 73 C 257 73 256 73 255 73 C 255 73 254 74 253 74 C 253 74 253 75 253 75 C 253 75 253 75 253 75 C 253 76 254 76 254 77 C 255 77 255 78 256 78 C 256 79 257 79 257 80 C 257 80 258 80 258 81 C 258 82 258 82 258 83 C 258 84 257 84 257 85 C 257 86 257 86 257 87 C 257 88 257 88 256 89 C 256 89 256 89 256 89 C 256 89 256 90 257 90 C 257 90 257 90 257 90 C 257 90 257 90 257 90 C 257 90 257 91 257 91 C 258 91 259 91 260 90 C 261 90 262 90 262 89 C 263 89 264 89 265 88 C 265 88 267 87 267 87 C 268 87 269 88 269 88 C 270 89 271 89 271 89 C 272 90 273 90 274 90 C 274 90 275 91 276 91 C 276 91 276 91 276 91 C 276 91 276 91 276 91 Z ">
089        <fill>
090            <LinearGradient x="267" y="63"
091                            scaleX="28"
092                            rotation="90">
093                <GradientEntry color="#f2dc5e"
094                               ratio="0"
095                               alpha="1" />
096                <GradientEntry color="#e7ca12"
097                               ratio="0.99"
098                               alpha="1" />
099            </LinearGradient>
100        </fill>
101        <filters>
102            <DropShadowFilter angle="270"
103                              color="#e0bb0c"
104                              distance="5"
105                              quality="2"
106                              blurX="0"
107                              blurY="0"
108                              inner="true" />
109            <DropShadowFilter angle="90"
110                              color="#000000"
111                              distance="7"
112                              alpha="0.11"
113                              quality="2"
114                              blurX="0"
115                              blurY="0" />
116        </filters>
117    </Path>
118     
119</Graphic>

 

この FXG 画像を用いた、Flex/AIRのモバイル端末用カスタムボタンスキンクラスを、Flex/AIRのモバイル端末用デフォルトボタンコンポーネントクラスを拡張して、テスト用に実装してみたのが、下のコードです(今回の下のコードは、あくまでテスト用。実際の本番の実装では、アプリケーションDPI毎に画像を用意する必要がありますし、ほかにもいろいろ調整が必要で、とっても手数の多い、面倒な作業になります)。

mxml_skin net.chsmea.mioproject.skins.test.mobile.ButtonSkin.as

01package net.chsmea.mioproject.skins.test.mobile {
02     
03    import net.chsmea.mioproject.skins.test.mobile320.assets.Button_down;
04    import net.chsmea.mioproject.skins.test.mobile320.assets.Button_up;
05     
06    import spark.skins.mobile.ButtonSkin;
07     
08    /**
09     * 星が付いた、ポップなボタンのスキン for mobile app. (未完成版).
10     *
11     * @langversion 3.0
12     * @playerversion AIR 2.5
13     * @productversion Flex 4.5
14     *
15     * @author mio
16     */
17    public class ButtonSkin extends spark.skins.mobile.ButtonSkin {
18         
19        /**
20         * コンストラクタ.
21         */
22        public function ButtonSkin() {
23             
24            super();
25             
26            switch (applicationDPI) {
27                 
28                default : { // (with 320 dpi skin)
29                     
30                    upBorderSkin = Button_up;
31                    downBorderSkin = Button_down;
32                     
33                    layoutGap = 50;
34                    layoutCornerEllipseSize = 20;
35                    layoutPaddingLeft = 48;
36                    layoutPaddingRight = 48;
37                    layoutPaddingTop = 50;
38                    layoutPaddingBottom = 50;
39                    layoutBorderSize = 50;
40                    measuredDefaultWidth = 164;
41                    measuredDefaultHeight = 106;
42                     
43                    break;
44                }
45                 
46            }
47        }
48         
49        /**
50         * 背景描画.
51         */
52        override protected function drawBackground(unscaledWidth : Number, unscaledHeight : Number) : void {
53             
54            // don't any.
55             
56        }
57         
58    }
59}

このとき、Flexライブラリビルドパスに、「(Adobe Flex SDK のインストール先)/frameworks/themes/Mobile.swc」を通しておかないと、Flex/AIRのモバイル端末用のデフォルトスキンのクラスを拡張できないので、忘れずに!

fb_libpath_mobile_swc.png

 

そして、テスト用に用意した、このFlex/AIRのモバイル端末用カスタムボタンスキンクラスを利用する、Flex/AIRモバイルアプリケーションを次のように用意したとします。

  • Main.mxml
  • View.mxml
  • style.css

mxml_main_exec (Project)/src/Main.mxml

1<?xml version="1.0" encoding="utf-8"?>
2 
3<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
4                            xmlns:s="library://ns.adobe.com/flex/spark"
5                            firstView="views.View">
6     
7    <fx:Style source="./assets/style.css" />
8     
9</s:ViewNavigatorApplication>

 

よし、そんじゃ、このアプリケーションを、デスクトップ上のモバイル端末シミュレータで実行・・・と。

adb_starButton_exec_failure.png

ダメです、うまく伸縮してくれません。

なぜか、FXG 画像の scale-9 grid が機能してくれないのです。

 

なぜ、機能してくれなかったのか?

先に答えを言ってしまうと、FXG の ベクター (Path) に、ビットマップフィルター (filters) を適用してしまっていたから でした。

・・・。

えっ。・・・あれれ!?

Flashって、scale-9 grid も、slice-9 grid も両方とも、ちゃんと機能してたはずでは?

そんな疑問だらけでしたが、 FXG version 2.0 の仕様書を読んだところ、slice-9 grid (ビットマップスライシング) 機能は FXG 2.0 の仕様として定まっていないので、表示オブジェクト内にビットマップが含まれていると、scale-9 grid も含めて、9スライスつまりはスライシング機能が適用されなくなる、とかいう制限があるとのこと。

そんなわけで、先ほどのボタンのコンパイル済みFXG画像のコードから、filtersを取っ払ったところ、下図のようにちゃんとうまく伸縮してくれるようになりました。

adb_starButton_exec_success.png

☆がいっぱいで、ぷちしあわせです。

はふーーー。

じゃぁ、影付けとかどうすんの!?

FXG 画像に、ドロップシャドウフィルター効果など(フィルター効果は残念ながらいまのところ全部ビットマップフィルター)をかけると、scale-9 grid が適用されなくなってしまうので、FXG 画像にたとえば影付けの効果を出したいときには、その影に当たる部分を、一つひとつパスを引いて、アルファ値を付けたブラックやグレーでフィルしていくしかなさそうです。他のフィルター効果の場合も、フィルター効果を追加した後のレンダリング結果(つまりは、見た目のこと)のように、ひたすらパスを引いたり、塗りつぶしの色や、アルファ値などをいじって、ベクターだけでそれっぽく作成していく必要があるようですね。

 

FXGでの9スライスにおける制限

FXG 2.0 画像の9スライスを行うとき、FXG 2.0 のそもそもの仕様や、Flash Player 側の仕様により、scale-9 grid, slice-9 grid の適用・レンダリングに関して、いくらかの制限があるのは、次のようなときのようです。
(出典元: FXG 2.0 Specification # Scale-9 / [ScaleGrid] Implementation Limitations - Flex SDK - Adobe Open Source *英語)

  1. Flash Player 側の仕様で、表示オブジェクト内の9スライスが設定された全ての継承について、scale-9 grid がちゃんと適用されないことがあるとのこと。具体的には、scale-9 を設定した子の表示オブジェクトを表示オブジェクトに吊るして、その親から子の表示リストを伸縮させた時なんかに、子の表示オブジェクトに対して scale-9 grid が適用されなくなるみたい。
  2. 歪み (skew) や 回転 (rotation) を設定した表示オブジェクトにも、scale-9 grid の伸縮は正しく適用されないとのこと。これも、おそらくは Flash Player 側の仕様かと。
  3. Flash Player 側の仕様で、ネストされた scale-9 grid はサポートされていないとのこと。具体的には、scale-9 を設定した子の表示オブジェクトを、これまた scale-9 を設定した表示オブジェクトに吊るして、その親や子の表示オブジェクトを伸縮させた時なんかに、それらの表示オブジェクトに対して scale-9 grid が適用されなくなるみたい。
  4. ビットマップが含まれた表示オブジェクトに対する、ビットマップスライシング (slice-9 grid) は適用されないとのこと。そもそも、FXG 2.0 の仕様に、slice-9 grid を定義していない。
  5. 表示オブジェクトへの scale-9 grid の設定値 (scaleGrid) が、その視覚的コンポーネントの子の表示オブジェクトのバウンディングボックスの外側へ裁ち切られてしまうような値だった場合、scale-9 grid は適用されなくなるとのこと。
  6. scale-9 grid の設定値 (scaleGrid) に、不正な値を設定した際、当然ながら scale-9 grid は適用されないとのこと。
    ここでいう不正な値とは、次の通り:
    • scaleGridLeft 及び scaleGridRight の値が、横断してしまってはならない(つまり、画像の大きさ内で鏡面にすることなく設定しよう、ってこと)。
    • scaleGridTop 及び scaleGridBottom の値が、横断してしまってはならない(つまり、画像の大きさ内で鏡面にすることなく設定しよう、ってこと)。
    • scaleGridLeft の値が scaleGridRight の値を超えてしまってはならない(つまり、左<右)。
    • scaleGridTop の値が scaleGridBottom の値を超えてしまってはならない(つまり、上<下)。
    • scaleGridLeft, scaleGridRight, scaleGridTop 及び scaleGridBottom は、すべて画像の内側の値を取らなければならない(つまり、左・下・右・上のすべてに 1 px 以上の値を設定しよう、ってこと。0 px とか マイナスの値は設定しちゃダメ)。

.

ページ移動

トラックバック

  • トラックバックはまだありません。

トラックバックURL

http://chsmea.net/~mioproject/freo/trackback/174

コメント

ittun55

2012/08/2714:02

Flex4からscale-9 Gridが効かず、困っていました。とっても参考になります。ありがとうございました!!!

コメント登録

  • コメントを入力してください。
登録フォーム
名前 *
URL
コメント *
閲覧制限 *
【スパム対策】 2 × 2 × 2 = ? (半角数字で)

ユーティリティ

user profile

calendar

052025/0607
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 - - - - -

tag cloud