kagamihogeの日記

kagamihogeの日記です。

Flex の右クリックあるいはコンテキストメニューの出し方

こんなようなもの。

flash.ui.ContextMenu - ActionScript 3.0 言語およびコンポーネントリファレンス なるものを使用する。使い方はこんな感じ。といってもリファレンスの例ほぼそのままだけど。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            private function init():void {
                var rmenu:ContextMenu;
                var rmenuitem:ContextMenuItem;

                rmenu = new ContextMenu();
                rmenu.hideBuiltInItems();

                rmenuitem = new ContextMenuItem("hoge 1");
                rmenu.customItems.push(rmenuitem);
                rmenuitem = new ContextMenuItem("separator 2",true);
                rmenu.customItems.push(rmenuitem);
                
                for (var i:int=3; i<40; i++){
                    rmenuitem = new ContextMenuItem("hoge" + i);
                    rmenu.customItems.push(rmenuitem);
                }            
                this.contextMenu = rmenu;
            }
        ]]>
    </mx:Script>
</mx:Application>

ContextMenu#hideBuiltInItems() で拡大だとかプリントだとかのビルトインコンテキストメニューが消せる。ただし、設定と Adobe Flash Player x については消せない。また、メニューアイテムは最大 15 個までという制限がある。↑のソースだとタクサン入れてますが見事にスルーされる。サブメニューも作れないようだ。あと、ContextMenuItem のコンストラクタの第二引数を true にすると、その ContextMenuItem の上部にセパレータを表示する。

mx.controls.Menu と使い勝手余りにも違うんでなんじゃこりゃーと思ったけど、コンテキストメニューは特殊扱いみたいだねぇ。ContextMenu のリファレンス見ると Adobe AIR でしか使えない API がいっぱいあるし、ドキュメントにも「ただし AIR では」って記述がたくさんある。AIR だと 15 個制限もないし、サブメニューも作れるし、設定とバージョン情報のメニューもないし、メニューアイテムに flash.display.NativeMenuItem ってのが使えたり、と色々違う。

Flex と AIR では出来ることが違うの当たり前ってのはわかってたけど、細かいとこが結構違うんだなぁ……

気を取り直して続き。コンテキストメニューを表示する場所は、Application 単位だけじゃなくてコンポーネント単位に設定できる。とりあえずこんな感じにすると、swf の領域全体、ボタン1、ボタン2それぞれに異なるコンテキストメニューが表示できる。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" applicationComplete="init()">
    <mx:Script>
        <![CDATA[
            import mx.core.UIComponent;
            private function init():void {
                initComponentContextMenu(this, "hoge");
                initComponentContextMenu(button1, "button1");
                initComponentContextMenu(button2, "button2");
            }
            
            private function initComponentContextMenu(component:UIComponent, label:String):void {
                var rmenu:ContextMenu = new ContextMenu();
                var rmenuitem:ContextMenuItem = new ContextMenuItem(label);
                rmenuitem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function():void{trace(label);});
                
                rmenu.hideBuiltInItems();
                rmenu.customItems.push(rmenuitem);
                component.contextMenu = rmenu;    
            }
        ]]>
    </mx:Script>
    <mx:Button id="button1" label="ボタン1"/>
    <mx:Button id="button2" label="ボタン2"/>
</mx:Application>

メニューアイテムが ContextMenuItem クラスとして独立してるおかげか、イベントリスナーが ContextMenuItem 単位に設定できるのも Menu との違いかねぇ。


次に、15 個制限の壁を突破するか右クリックメニューを消滅させる方法について調べてみた。とりあえず 右クリックメニューを出さない方法はありますか? - フォーラム - Flex User Group がひっかかった。そこに Custom Right-click in Flash | polyGeek.com という URL が紹介されていた。これによると、ExternalInterface を使用して javascript で右クリックのイベントを補足し、ActionScript のメソッドを呼ぶ、ということをやっている。ややこしいなー。

で、これを試してみようと思ったら ExternalInterface の動かし方がよくわからず、しばらく唸っていた。まず最初の原因はコイツ。

IEで戻り値がnullの件、allowScriptAccessはalwaysにしたのに、FFでは正常なのにと...

SWF貼り付けのobjectタグのID名に "external"を入れると動作する。
external、だけじゃだめで、*external or external* or *external* ってこと(*は任意の文字列)

ExternalInterface.call Internet explorerでnullが返ってくる件 より抜粋

わかんないってそんなの……w

今はとりあえず IE でだけ動作確認してるんだけど 【解決!】ExternalInterfaceにハマる (Nega Diary) とか見ると色々イヤな予感が。こういうのがあるから Web のフロントエンド部分の開発はイヤらしいんだよなぁw

ここで疲れたので ExternalInterface 使って右クリック操作するのを試すのは止めてしまった。