リッチインターネットアプリケーションと
 Macromedia Flex の技術情報集
 
    HOME   |  
 
 
 Flex FAQ コーディング関連

質問と回答

質問

PrintJob で印刷を行いますが、disable エリアが印刷されません

回答

disable で透過されている領域なども含め、画面の見たままの状態で印刷をするには PringJob の addPage ファンクションの第3引数で印刷オプション printAsBitmap:true を指定します。

例:

pj.addPage(datagrid1,null,{printAsBitmap:true}); 

詳しくは製品ドキュメントを確認してください:

http://livedocs.macromedia.com/flash/mx2004/main_7_2/00001640.html

 

質問

ポップアップウィンドウを複数開いたとき、フォーカスが正しく移動しません。

回答

この現象はどうやら Flex フレームワークの不具合のようです。回避策として ポップアップを開いた後に

mx.managers.SystemManager.activate() 

というファンクションを呼び出し、明示的に新しく開いたポップアップに制御を移すようにします。

例:

	function openWin() {
var mc = mx.managers.PopUpManager.createPopUp(this, SimplePopup,true);
mx.managers.SystemManager.activate(mc);
mc.setFocus();
}

 

 

質問

getURL ファンクションを使って別ページを開き、ここに POST データを送りたい。

回答

Flex アプリケーションから、同一、または別ブラウザーで新しい URL のページを開くには getURL ファンクションを使います。 新しいページが、サーブレットや JSP ページなどの Webアプリケーションで、ここに GETやPOSTデータを渡したいときは、getURL の第3引数に、データを渡す形式に従い "GET" もしくは "POST" を指定します。渡すデータは getURL を呼び出すオブジェクトに対してセットします。 getURL は MovieClip クラスのファンクションです。mx:Application は MovieClip を継承していることから、Flex アプリケーションは明示的にオブジェクトを指定することなく getURL ファンクションを利用することができます(つまり this.getURL(....) )。 しかしこのとき第3引数で GET または POST と指定すると、mx:Application が持つプロパティーをすべて送るよう処理が作動し、期待した結果となりません。

GET や POST 形式で getURL で開くページにデータを渡したいときはブランクの MovieClip オブジェクトを生成し、ここにプロパティーを設定、このオブジェクトを使って getURL を行うようにします。

例:引数で指定された URL にアクセスし、POST 形式で firstName=Taro&lastName=Yamada を送りページを開くファンクション openURL :

	var loader_mc:MovieClip;
		
	function openURL(theURL) {
		this.createEmptyMovieClip("loader_mc", this.getNextHighestDepth());
		loader_mc.firstName = "Taro";
		loader_mc.lastName  = "Yamada";
		loader_mc.getURL( "httheURL, "_blank", "POST");
	}

参:http://livedocs.macromedia.com/flex/15/flex_docs_en/00001360.htm

 

 

質問

validator が入力エラーを検出したときに表示するふきだしの色を変えるにはどうしますか?

回答

スタイルのタイプセレクターで ErrorTip という型についてスタイルを設定することでふきだしの色やフォントなどスタイルをカスタマイズすることができます。

<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" >

<mx:Style>
ErrorTip {
	border-color:#00FFCC;
}
</mx:Style>

   <mx:Model id="membership">
     <username>{userNameInput.text}</username>
     <fullname>{fullNameInput.text}</fullname>
   </mx:Model>

   <mx:Form id="membershipForm">
     <mx:FormItem id="fullNameItem" label="Full Name">
       <mx:TextInput id="fullNameInput"/>
     </mx:FormItem>
     <mx:FormItem id="userNameItem" label="Username">
       <mx:TextInput id="userNameInput" />
     </mx:FormItem>
   </mx:Form>
   <mx:StringValidator field="membership.username" listener="userNameInput" minLength="1" maxLength="6" />
</mx:Application>

実行例

 

 

質問

HTTPService と連携して DataGrid にデータを表示していますが、データが1件の時データが表示されません。

回答

HTTPService の結果を DataGrid などのリスト系コンポーネントに結びつけ、表示を行うアプリケーションにおいて、サーバーから取得した結果が1件だけのとき、表示が正しく行われない場合があります。これは HTTPSerivce の XML データ解析処理の特性によるものです。

次にサンプルを示します。

テストアプリケーション xmltest1.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">

<mx:HTTPService id="srv1" url="data.xml" />
 
<mx:Button label="Get data" click="srv1.send()"   />

<mx:DataGrid dataProvider="{srv1.result.record.data}">
  <mx:columns>
    <mx:Array>
	<mx:DataGridColumn columnName="name"/>
	<mx:DataGridColumn columnName="age"/>
    </mx:Array>
  </mx:columns>
</mx:DataGrid>
</mx:Application>

テストデータ - data.xml (xmltest1.mxml と同じディレクトリに保存)

<?xml version="1.0" encoding="utf-8"?>
<record>
	<data>
		<name>Hanako Tanaka</name>
		<age>23</age>
	</data>
	<data>
		<name>Ichiro Yamada</name>
		<age>28</age>
	</data>
</record>

アプリケーションを実行すると一つのボタンと、一つの DataGrid が表示されます。ボタンをクリックするとサーバーからデータが取得され、サーバーから取得した結果が DataGrid に表示されます。

(もし 「HTTPService Fault」 というポップアップエラーメッセージが表示して問題が解決できない場合、まずこちらを参照してください)

次に data.xml を編集し、後半の <data> ..... </data> の部分を削除します。これで data.xml に保存されている、name, age の情報は1件だけになりました。

再び [Get Data] ボタンをクリックします。データは表示されなくなります。

これは HTTPService がデフォルトの resultFormat="Object" でデータを扱うときの特性に依存しています。 HTTPService はサーバーからデータを取得し、その結果を XML とみなし XML -> ActionScript オブジェクトの変換を行います。このとき、同じ要素名で繰り返しのデータがあったとき、この繰り返しを Array 型のオブジェクトとして展開します。今回の data.xml の例ですと data 要素がこれにあたります。

問題はデータが1件しかなかったときです。このとき HTTPService は data 要素の繰り返しを検出しません。そのため data 要素を Array 型で展開することがないのです。 DataGrid などのリスト系コンポーネントは dataProvider に渡された配列要素をもとにデータを表示します。このためデータが配列で渡ってこなければ DataGrid はデータを正しく表示することができないのです。

回避策は2つ考えられます。

回避策1.データを強制的に Array に変換する

最もシンプルな回避策です。データが配列でなくとも強制的に要素が1つしかない配列として変換します。 Flexライブラリーには mx.utils.ArrayUtil.toArray というこのための便利なファンクションが用意されています。この回避策を組み込んだ例を次に示します。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">

<mx:HTTPService id="srv1" url="data.xml" />
 
<mx:Button label="Get data" click="srv1.send()"   />

<mx:DataGrid dataProvider="{mx.utils.ArrayUtil.toArray(srv1.result.record.data)}">
  <mx:columns>
    <mx:Array>
	<mx:DataGridColumn columnName="name"/>
	<mx:DataGridColumn columnName="age"/>
    </mx:Array>
  </mx:columns>
</mx:DataGrid>
</mx:Application>

回避策2.変換ロジックを明示的に実装する

回避策1、はほとんどの場合において有効でかつ組み込まなければならない処理も最小限ですみます。回避策2では XML -> ActionScript オブジェクトの変換を自動判定に任せるのではなく、明示的に変換ルールを実装する方法を示します。この方法は変換処理を自ら組み込まなければならないため開発者の作業はより多くなりますが、データの形式にあわせた均一な変換方法を実装できることから、今回の配列変換の問題のみならず、「各要素ごとに明示的に型を指定することができる」、「変換ルールが均一なので自動判別にかかるオーバーヘッドを抑止できる」などの利点も享受することができます。 XML -> ActionScript オブジェクトの変換処理は HTTPService の xmlDecode プロパティーでそのファンクションを指定することでカスタムのものに置き換えることができます。次に例を示します。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">
<mx:Script>
<![CDATA[
function xmlDecoder ( myXML:XML ) : Object {
   // Simplified decoding logic.
   var myResult:Array = new Array();
   
	//       aNode                 record    .data      
	for (var aNode:XMLNode = myXML.firstChild.firstChild; aNode != null; aNode = aNode.nextSibling) {
		var dataNode = aNode.firstChild;
		var data:Object = new Object();
		data.name = String(dataNode.firstChild.nodeValue);
		dataNode = dataNode.nextSibling
		data.age = Number(dataNode.firstChild.nodeValue);
		myResult.push(data);
	}	
	return myResult;
}

   
]]>
</mx:Script>

<mx:HTTPService id="srv1" url="data.xml" xmlDecode="xmlDecoder"  />
 
<mx:Button label="Get data" click="srv1.send()"   />

<mx:DataGrid dataProvider="{srv1.result}">
  <mx:columns>
    <mx:Array>
	<mx:DataGridColumn columnName="name"/>
	<mx:DataGridColumn columnName="age"/>
    </mx:Array>
  </mx:columns>
</mx:DataGrid>
</mx:Application>

HTTPService はまず ActionScript の XML オブジェクトとしてサーバーからの結果を受け取ります。これを適当な変換ルールに基づいて ActionScript オブジェクトに変換します。xmlDecode ファンクションの一連の実装には XMLクラスの扱い方を知っている必要があります。 XML クラスについては次を参照してください。

http://livedocs.macromedia.com/flex/15/flex_docs_en/00001882.htm

 

 

 

質問

DataGrid の内部データを入れ替えたとき、ソートの状態が維持されません。

回答

DataGrid コンポーネントにはカラム単位でソートを行う機能があります。データグリッドの列タイトルをクリックすることでこの機能が働き、クリックしたカラムの列でソートが実行されます。またこのソート操作が行われると、特定の列でソートされたということを視覚的に表現するために、ソートが行われた列の列タイトルの領域に矢印のアイコンが表示されます。

これは非常に便利な機能ですが、ソートされたことを表す矢印のアイコンと、内部のデータとは様々な理由で同期しなくなります。矢印のアイコンはソート操作が行われたタイミングで表示されますが、その後のデータの更新には関与しないからです。例えば、ソート操作を行った後、dataProvider プロパティーを更新し内部データを入れ替えた場合、ソートアイコンの表示と実際の内部データの並び順はまったく一致しなくなります。

この問題に対する最も簡単な回避方法の一つは DataGridの持つ sortIndex、sortDirection という2つのプロパティー、そして placeSortArrow メソッドを利用して追加の機能を実装するということです。 sortIndex, sortDirection プロパティーは API ドキュメントには出てきませんが、比較的知られた DataGrid のプロパティーです。この2つのプロパティーを利用することなしにこの問題に対する回避コードを実装しようとした場合、相当な手間がかかりますので、この2つのプロパティーを有効に利用することがいいと思います。

次に実装例を示します。 DataGrid コンポーネントを継承して新しく AutoSortDataGrid というカスタムコンポーネントを作成しました。 AutoSortDataGrid は DataGrid の機能を引き継ぎつつ、次の2つの機能を加えました。

追加機能1. updateDataProvider(dg, resetSortIndex)
現在のソート順序を保ちつつ、第一引数で与えられたデータで dataProvider を更新します。第二引数で true を指定した場合は現在設定されているソート順序をキャンセルし、dataProvider 第一引数で与えられたデータで更新します。
追加機能2.sortColumn(sortIndex, sortDirection)
ActionScript からデータ のソートを 行います。ソートの状態に従って矢印アイコンも正しく表示されます。第一引数でソートを行う列の番号(0〜)、第二引数は、降順か、昇順かを文字列 "ASC" / "DESC" で指定します。

カスタムコンポーネント AutoSortDataGrid.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:DataGrid xmlns:mx="http://www.macromedia.com/2003/mxml" >
<mx:Script>
<![CDATA[

	function sortColumn(aSortIndex, direction) {
		sortIndex = aSortIndex;
		sortDirection = direction;
		doSort();
	}
		
	function updateDataProvider(dt:Object, resetSortIndex:Boolean) {
		if (resetSortIndex) {
			sortIndex = undefined;
		}
		dataProvider = dt;		
		doSort();
	}
	
	private function doSort() {
		if (sortIndex != undefined) {
			var col = columns[sortIndex].columnName;
			sortItemsBy(col,sortDirection);
			placeSortArrow();
		}
	}
]]>
</mx:Script>
</mx:DataGrid> 

AutoSortdataGrid.mxml を利用したサンプルアプリケーション

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" xmlns="*" >
<mx:Script>
<![CDATA[
	var items0 = [{num:'111', name:'NAME1', price:123},
		{num:'222', name:'NAME2', price:456}];

	var items1 = [{num:'001', name:'Data1', price:100},
		{num:'002', name:'Data2', price:150},
		{num:'003', name:'Data3', price:100},
		{num:'004', name:'Data4', price:1000},
		{num:'005', name:'Data5', price:1001},
		{num:'010', name:'Data10', price:10}				
		];
	
	var items2 = [{num:'501', name:'A Data1', price:5100},
		{num:'002', name:'Data2', price:4150},
		{num:'403', name:'Data3', price:3100},
		{num:'004', name:'B Data4', price:21000},
		{num:'305', name:'Data5', price:11001},
		{num:'010', name:'C Data10', price:010}				
		];
	
]]>
</mx:Script>

    <mx:HBox>
      <mx:Button label="load items1" click="datagrid1.updateDataProvider(items1);" />
      <mx:Button label="load items2" click="datagrid1.updateDataProvider(items2);" />
      <mx:Button label="load items1(reset sortIndex)" click="datagrid1.updateDataProvider(items1,true);" />
      <mx:Button label="load items2(reset sortIndex)" click="datagrid1.updateDataProvider(items2,true);" />
    </mx:HBox>
    <mx:HBox>
      <mx:Button label="sort by col1 (ASC)" click="datagrid1.sortColumn(0, 'ASC')" />
      <mx:Button label="sort by col1 (DESC)" click="datagrid1.sortColumn(0, 'DESC')" />
    </mx:HBox>
	<AutoSortDataGrid id="datagrid1" width="100%" height="100%" dataProvider="{items0}" >
        <columns>
          <mx:Array>
            <mx:DataGridColumn headerText="カラムA" columnName="num" />
            <mx:DataGridColumn headerText="カラムB" columnName="name" />
            <mx:DataGridColumn headerText="カラムC" columnName="price" />
          </mx:Array>
        </columns>
    </AutoSortDataGrid>
</mx:Application>

実行例:

 

 

 

質問

タブキーでの入力フィールドの移動をプログラムで制御することはできますか? または TextInput で改行キーでフォーカスを移動させるようにすることは可能ですか?

回答

FocusManager クラスの tabHandler メソッドを呼び出すといいでしょう。次は TextInput で改行キーが押されたというイベントを受け、タブ移動を行わせるように実装した例です。

					<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">
<mx:Script>
<![CDATA[
	function goNext(event) {
		getFocusManager().tabHandler();
	}

]]>
</mx:Script>

<mx:TextInput enter="goNext(event)" />
<mx:TextInput enter="goNext(event)" />
<mx:TextInput enter="goNext(event)" />
<mx:TextInput enter="goNext(event)" />
</mx:Application>
					

tabHandler メソッドは正確には非公開のメソッドです。 そのためアップデータを適用する、または新しいバージョンに移行した際には同じように動かない可能性があります。この条件を受け入れられるのであれば使って問題ないと思いますが、メインテナンス性を考えて上記のようにそれを使う部分はひとつのファンクションにまとめておいたほうがいいでしょう。

 

 

質問

Treeコンポーネントで表示されるアイコンの表示を変えるには?

回答

Tree コンポーネントのアイコン表示を変える方法はいくつかあります。それぞれの方法でできること、できないことがありますので、必要とするユーザーインターフェースデザインにあわせてどの方法を選択するか検討する必要があります。

アイコン表示を変更する方法1.
folderClosedIcon、folderOpenIcon、defaultLeafIconスタイルを使ってフォルダーのオープン/クローズ、リーフアイコンを指定する。

欠点: 設定したスタイルはすべてのノードに適用される、各ノードごとに別々のアイコンを指定することはできない。
 
アイコン表示を変更する方法2.
iconFunction を定義する。iconFunction は引数にノードの情報を持つ形で、各ノードごとに呼び出される。戻り値でアイコンシンボルを返すことで、これをそのノードのアイコンとして表示されることができるので、条件にしたがって各ノードごとに別々のアイコンを表示させることが可能。

欠点:各ノードごと指定できるアイコンはひとつだけ、つまりフォルダーノードである場合、フォルダーの開く/閉じるで表示アイコンを切り替えることができない。
 
アイコン表示を変更する方法3.
setIcon メソッドを使って、各ノードごとに個別にアイコンを指定する。setIcon メソッドは第一引数にノード、第二、第三引数に、フォルダー閉じる(リーフ要素の場合はリーフ)アイコン、フォルダー開くアイコンのシンボルを指定する。各ノードごとに別々のアイコンを指定でき、かつフォルダーノードについては、フォルダーを閉じたときと、開いたときで別々のアイコンを指定できる。

欠点:各ノードごとに setIcon を行わなければならない。つまりたとえば、10個のノードがあった場合、それらに対して個別に setIcon を行う処理ロジックを書かなければならない。
 

アイコン表示を変更する方法2の例 (各ノードにある levelid の番号によってアイコンを切り替えます):

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" >

<mx:Script>
<![CDATA[
  [Embed(source="help.png")]
  var helpIcon : String;

  [Embed(source="home.png")]
  var homeIcon : String;

  [Embed(source="lock_color.png")]
  var lockIcon : String;

  [Embed(source="lock_gray.png")]
  var defaultIcon : String;
 

  function myTreeIcon(item) {
    switch(Number(item.attributes.levelid)) {
    case 1:
      return helpIcon;
    case 2:
      return homeIcon;
    case 3:
      return lockIcon;
    default:
      return defaultIcon;
    }
  }

]]>
</mx:Script>

<mx:Tree iconFunction="myTreeIcon"  >
  <mx:dataProvider>
    <mx:XML>
      <node label="folder1" levelid="1" >
        <node label="folder2" levelid="2" >
          <node label="aaaaa" levelid="3" />
          <node label="bbbbb" levelid="3" />
          <node label="cccc" levelid="3" />
        </node>
        <node label="text4" levelid="3" />
        <node label="text5" />
      </node>
    </mx:XML>
  </mx:dataProvider>
</mx:Tree>

</mx:Application>

アイコン表示を変更する方法3の例 
(各ノードにある levelid の番号によってアイコンを切り替えます。アイコンの切り替えは Tree コンポーネントの生成が終了した後に initializeTreeIcons が呼び出されることで行われています。このファンクションは表示されているデータのツリー構造を順番にたどり、levelid の番号にしたがって、フォルダーの場合は開く、閉じるアイコン、リーフの場合は、リーフアイコンを設定しています):

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" >


<mx:Script>
<![CDATA[
  [Embed(source="level1CloseIcon.png")]
  var level1CloseIcon : String;

  [Embed(source="level1OpenIcon.png")]
  var level1OpenIcon : String;

  [Embed(source="level1LeafIcon.png")]
  var level1LeafIcon : String;

  [Embed(source="level2CloseIcon.png")]
  var level2CloseIcon : String;

  [Embed(source="level2OpenIcon.png")]
  var level2OpenIcon : String;

  [Embed(source="level2LeafIcon.png")]
  var level2LeafIcon : String;

 

  function initializeTreeIcons(tree) {
	
    // you also can change disclosure icons if it needed.
    // tree.setStyle("disclosureOpenIcon", yourIcon1);
    // tree.setStyle("disclosureClosedIcon", yourIcon2);

    iterateChildNodes(tree.dataProvider,tree);
  }

  // look into the tree nodes and set apropriate icons to them.
  function iterateChildNodes(node,tree) {
    var childNodes:Array = node.childNodes;
    for ( var i = 0 ; i < childNodes.length; i += 1 ) {
        iterateChildNodes(childNodes[i],tree);
    }
    var icon1, icon2;
	
    switch( Number(node.attributes.levelid)) {
	
    case 1:
      if (node.hasChildNodes()) {
      // branch node
        icon1 = level1CloseIcon;
        icon2 = level1OpenIcon;
      } else {
      // leaf node
        icon1 = level1LeafIcon;
        icon2 = level1LeafIcon;
      }			
      break;

    case 2:
      if (node.hasChildNodes()) {
      // branch node
        icon1 = level2CloseIcon;
        icon2 = level2OpenIcon;
      } else {
      // leaf node
        icon1 = level2LeafIcon;
        icon2 = level2LeafIcon;
      }			
      break;

    default:
      // if you have default icon. Define here.
		
    }		

    tree.setIcon(node, icon1, icon2);
  }
 ]]>
</mx:Script>

<mx:Tree id="t1" creationComplete="initializeTreeIcons(event.target)"  >
  <mx:dataProvider>
    <mx:XML>
      <node label="folder1" levelid="2" >
        <node label="folder2" levelid="2" >
          <node label="aaaaa" levelid="2" />
          <node label="bbbbb" levelid="2" />
          <node label="cccc" levelid="2" />
        </node>
        <node label="text4" levelid="2" />
        <node label="text5" levelid="2" />
      </node>
      <node label="folder3" levelid="1" >
        <node label="text6" levelid="1" />
      </node>
    </mx:XML>
  </mx:dataProvider>
</mx:Tree>

</mx:Application>

 

 

 

 

<前のページ