<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 xmlns="http://purl.org/rss/1.0/"
 xmlns:content="http://purl.org/rss/1.0/modules/content/"
 xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:syn="http://purl.org/rss/1.0/modules/syndication/"
 xmlns:image="http://pirl.org/rss/1.0/modules/image/"
 xmlns:admin="http://webns.net/mvcb/"
>
<channel rdf:about="http://blog.banana-systems.com/">
<title>バナナ研究所</title>
<link>http://blog.banana-systems.com/</link>
<description>
</description>
<dc:language>ja</dc:language>
<admin:generatorAgent rdf:resource="http://blog.livedoor.com/?v=2.0" />
<image rdf:resource="http://image.profile.livedoor.jp/icon/banana_systems_60.gif"/>
<items>
 <rdf:Seq>
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/1151326.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/1112559.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/1102807.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/1037174.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/879007.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/812058.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/783299.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/758255.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/331379.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/238811.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/228744.html" />
  <rdf:li rdf:resource="http://blog.banana-systems.com/archives/221512.html" />
 </rdf:Seq>
</items>
</channel>
<image rdf:about="http://image.profile.livedoor.jp/icon/banana_systems_60.gif">
 <title>バナナ研究所</title>
 <link>http://blog.banana-systems.com/</link>
 <url>http://image.profile.livedoor.jp/icon/banana_systems_60.gif</url>
</image>
<item rdf:about="http://blog.banana-systems.com/archives/1151326.html">
<title>mod_auth_opensocial をリリースしました</title>
<link>http://blog.banana-systems.com/archives/1151326.html</link>
<description>






mod_auth_opensocial 0.1.0 をリリースしました。mod_auth_opensocial は、OpenSocial アプリからサーバに届いた署名付きリクエストの検証(Verification)を行うための Apache 2.0, 2.2 用のモジュールです。

署名付きリクエストとは、OpenSocial の makeReq...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2009-02-10T08:20:20+09:00</dc:date>
<dc:subject>OpenSocial</dc:subject>
<content:encoded><![CDATA[






<h3>mod_auth_opensocial 0.1.0 をリリースしました。</h3><br>mod_auth_opensocial は、OpenSocial アプリからサーバに届いた署名付きリクエストの検証(Verification)を行うための Apache 2.0, 2.2 用のモジュールです。<br><br>

署名付きリクエストとは、OpenSocial の makeRequest 関数 (AUTHORIZATIONパラメータにSIGNEDを指定) により生成され、コンテナにより署名されたリクエストです。<br><br>

mod_auth_opensocial が署名の検証に成功すると、リクエストは通常通り(HTMLの取得や、PHP、Servlet Containerへのアクセスなど)に処理されます。 検証に失敗すると、HTTPエラーをリクエストの送信者へ返します。<br>
バックエンドが受け取るリクエストは全て署名の検証に成功したものなので、バックエンドはリクエストを再度検証する必要が無くなります。
リクエストに署名が付加されているかどうかすらバックエンドは気にする必要がありません。<br><br>尚、本プロジェクトは Google Code で管理しています。<br><a href="http://code.google.com/p/mod-auth-opensocial/" target="_top">http://code.google.com/p/mod-auth-opensocial/</a><br>

<h3><br></h3><h3><br></h3><h3>■ インストール</h3>
<br>
mod_auth_opensocial は openssl の機能を使用していますので、Apache に mod_ssl が組み込まれている必要があります。<br><br>

○ <b>Apache のインストール</b>
<br><br>

※ configure コマンドへのオプションの --prefix と --enable-mods-shared の値は適宜変更してください。<br>
またシステムによっては、 --with-ssl オプションで openssl へのパスを指定する必要があるかもしれません。 (例: --with-ssl=/usr/local/openssl )

<pre>#./configure <br>    --prefix=/usr/local/apache \ 
    --enable-mods-shared=most \  
    --enable-so \ 
    --with-included-apr \  
    --enable-ssl

# make
# make install
</pre>
<br>

○ <b>mod_auth_opensocial のインストール</b>
<br>

<pre># tar xvzf mod_auth_opensocial-0.1.0tar.gz
# cd mod-auth-opensocial-0.1.0<br># vi Makefile
APACHE_BASE=PATH_TO_APACHE
の行の PATH_TO_APACHE を Apache を↑でインストールしたディレクトリに変更してください。
APACHE_BASE=/usr/local/apache
# make
# make install
</pre>
<br>

○ <b>httpd.conf の変更</b>
<br><br>

httpd.conf 内の LoadModule ディレクティブ が記述されている箇所に↓の行を追加してください。<br>

<pre>LoadModule auth_opensocial_module modules/mod_auth_opensocial.so
</pre>

Apache をリスタートすると mod_auth_opensocial がロードされます。
ただし、この段階では設定を行っていないので、まだ何も起こりません。
<br><br>

<h3>■ mod_auth_opensocial の設定</h3>
<br>

Apache が http://www.example.com という URL で稼働しているとします。<br>
http://www.example.com/secure/ の以下全てのディレクトリ(パス) へのアクセスを検証するには、以下のように設定します。<br>

<pre>&lt;Location "/secure/"&gt;
   AuthType OpenSocial
   Require valid-user

   AuthOpenSocialEnabled On

   AuthOpenSocialAcceptSignatureMethods hmac-sha1 rsa-sha1

   AuthOpenSocialConsumerKey 4345983491233
   AuthOpenSocialConsumerSecret wa/nx35kdmerLwQZp8AfmoX

   AuthOpenSocialRSAPublicKeyFile ./conf/pub.1199819524.-1556113204990931254.cer
&lt;/Location&gt;
</pre>

※ consumer secret の値を記述しているので、httpd.conf のパーミッションには注意を払ってください。<br><br>

<span style="font-weight: bold;">AuthType </span>と <span style="font-weight: bold;">Require </span>ディレクティブは、必ず必要なので忘れずに記述してください。<br><br>それでは mod_auth_opensocial 個別のディレクティブを見ていきましょう。<br><br><div style="margin-left: 40px;">

<span style="font-weight: bold;">AuthOpenSocialEnabled </span>: <br><div style="margin-left: 40px;">引数の数 : 1<br></div></div><div style="margin-left: 80px;">説明 : このモジュールの有効、無効を指定します。  指定の方法は、有効の場合には On を、無効にする場合には Off を指定します。<br><br></div><div style="margin-left: 40px;"><span style="font-weight: bold;">AuthOpenSocialAcceptSignatureMethods </span>: <br><div style="margin-left: 40px;">引数の数 : 1<br>説明 : 受け付ける署名方式です。  hmac-sha1, rsa-sha1 のどちらか、もしくは両方(スペース区切り)を指定します。

<br><br></div><span style="font-weight: bold;">AuthOpenSocialConsumerKey </span>: <br><div style="margin-left: 40px;">引数の数 : 1<br>説明 : OpenSocial サービスから提供される Consumer Key です。 AuthOpenSocialAcceptSignatureMethods ディレクティブに hmac-sha1 を指定した場合に必須となります。
<br><br></div><span style="font-weight: bold;">AuthOpenSocialConsumerSecret </span>: <br><div style="margin-left: 40px;">引数の数 : 1<br>説明 : OpenSocial サービスから提供される Consumer　Secret です。AuthOpenSocialAcceptSignatureMethods ディレクティブに hmac-sha1 を指定した場合に必須となります。

<br><br></div><span style="font-weight: bold;">AuthOpenSocialRSAPublicKeyFile </span>: <br><div style="margin-left: 40px;">引数の数 : 1<br>説明 : OpenSocial サービスから提供される RSA PublicKey を保存したファイルへのパスを指定します。 AuthOpenSocialAcceptSignatureMethods ディレクティブに rsa-sha1 を指定した場合に必須となります。

<br><br></div>上の設定例では用いていませんが、以下のディレクティブもオプションで指定可能です。

<br><br><span style="font-weight: bold;">AuthOpenSocialRequiredKey </span>: <br><div style="margin-left: 40px;">引数の数 : 1<br>説明 : 必須HTTPパラメータ (query string 又は post data) の key を指定します。リクエストのパラメータにこの値が存在しなければ、HTTPエラーをリクエスト送信者へ返します。

<br><br></div><span style="font-weight: bold;">AuthOpenSocialRequiredKeyAndValue </span>: <br><div style="margin-left: 40px;">引数の数 :2<br>説明 : 必須HTTPパラメータ (query string 又は post data) の key と値のペアをスペース区切りで指定します。リクエストのパラメータに、このディレクティブで指定したkeyと値のペアが含まれていなければ、HTTPエラーをリクエスト送信者へ返します。

<br><br></div></div>今回リリースした mod_auth_opensocial の バージョン 0.1 では、ある一つの URL に対して１つの consumer key や rsa publickey しか設定できないため、複数のOpenSocial サービスからの署名リクエストを１つのLocationディレクティブでさばくことができません。 <br><br>例えば、http://www.example/myapp/ に対して mixi アプリとOrkut の両方からの署名リクエストの検証は行えないということです。 Locationタグには、１つだけOpenSocial サービスの設定を行ってください。 <br>この制限や、他にも課題が多数ありますが、今後のバージョンで対応していきます。<br><br>このモジュールの使いどころやOpenSocialについて、このブログに順次アップしていきます。<br>この機会に是非 RSSの購読をお願いいたします。<br><br><br><br><br>

]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/1112559.html">
<title>速攻で作る OpenSocialアプリ ( RSA-SHA1 )</title>
<link>http://blog.banana-systems.com/archives/1112559.html</link>
<description>前回のブログにて OpenSocial Client Library Ruby版に付属している Gifts アプリを動作させるまで説明しました。

makeRequestの署名方式に RSA-SHA1 を用いる


OpenSocialコンテナ(Orkut) -&amp;gt; 外部サーバへのリクエストの署名方式には HMAC-SHA1 を用いていました...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2009-01-10T06:05:01+09:00</dc:date>
<dc:subject>OpenSocial</dc:subject>
<content:encoded><![CDATA[<a href="http://blog.banana-systems.com/archives/1102807.html">前回のブログ</a>にて OpenSocial Client Library Ruby版に付属している Gifts アプリを動作させるまで説明しました。<br /><br />

<h2>makeRequestの署名方式に RSA-SHA1 を用いる</h2>

<br />
OpenSocialコンテナ(Orkut) -> 外部サーバへのリクエストの署名方式には HMAC-SHA1 を用いていましたが、今回はより一般的な RSA-SHA1 を用いた方法を説明します。<br /><br />

HMAC-SHA1では consumer key と consumer secret の２つが必要でしたが、RSA-SHA1ではそれらは必要なく、公開鍵だけが必要となります。

■ OpenSocialコンテナ(Orkut) -> 外部サーバ のリクエストの署名方式を HMAC-SHA1 から RSA-SHA1 へ変更<br /><br />

GIFT_SAMPLE/public/gifts.xml の 14行目辺りのparams["OAUTH_SERVICE_NAME"] = "HMAC";
をコメントアウトしてください。<br />
この変更により OpenSocialコンテナ(Orkut) -> 外部サーバへのリクエストの署名方式がデフォルトの RSA-SHA1 になります。<br /><br />

■ Rails側で RSA-SHA1 にて署名されたリクエストを受け取れるようにする<br /><br />

○ 公開鍵の取得<br /><br />

OpenSocialコンテナ(Orkut) -> 外部サーバへのリクエストのパラメータに
xoauth_signature_publickey=pub.1199819524.-1556113204990931254.cer
というkeyとvalueが付与されてきますが、この pub.1199819524.-1556113204990931254.cer ファイルが公開鍵となります。<br /><br />

で、そのファイルはどこにあるのかというと　http://sandbox.orkut.com/46/o/pub.1199819524.-1556113204990931254.cer にあります。<br /><br />

xoauth_signature_publickeyの値が変更されたらどうするんだ？という疑問が浮かぶかもしれませんが、Orkutの場合は、xoauth_signature_publickeyの値が変わる時にはGoogleが前もって告知してくれますので、現状はこの公開鍵のファイルの内容をソースコードにハードコーディングしておいても大丈夫だと思います。<br />
※逆に、自動で公開鍵ファイルを取得するような実装は危険です。そのファイルが正規のものかどうか判断のしようがありませんので。<br /><br />

○ gifts コントローラの修正<br /><br />

opensocial client library ( gem opensocial-0.0.2 )ですが、何故か署名方式に HMAC-SHA1 しか使用できないようになっています・・・。<br />
RSA-SHA1の署名方式が使用できるようにします。<br /><br />

gifts コントローラ内で require 'oauth/signature/rsa/sha1' を行います。<br /><br />

また check_signature メソッド内にてコールされている validate メソッド(OpenSocial::Auth#validate(...)) では opensocial client が内部で用いている oauth ライブラリの Consumer クラスのコンストラクタに options を渡せない(署名方式を指定できない)ので、その validate メソッドを用いず、新たに validate メソッドを作成します。<br /><br />

上記全てを反映した gifts コントローラは下のようになります。

<pre>
class GiftsController < ApplicationController
  include OpenSocial::Auth

  <font color="#ff0000">require 'oauth/signature/rsa/sha1'</font>

<font color="#ff0000">
# 公開鍵のハードコーディング
CERT = <<"EOS"
-----BEGIN CERTIFICATE-----
MIIDHDCCAoWgAwIBAgIJAMbTCksqLiWeMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIG
A1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlh
bjAeFw0wODAxMDgxOTE1MjdaFw0wOTAxMDcxOTE1MjdaMGgxCzAJBgNVBAYTAlVT
MQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChML
R29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVscnlhbjCBnzAN
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAseBXZ4NDhm24nX3sJRiZJhvy9eDZX12G
j4HWAMmhAcnm2iBgYpAigwhVHtOs+ZIUIdzQHvHeNd0ydc1Jg8e+C+Mlzo38OvaG
D3qwvzJ0LNn7L80c0XVrvEALdD9zrO+0XSZpTK9PJrl2W59lZlJFUk3pV+jFR8NY
eB/fto7AVtECAwEAAaOBzTCByjAdBgNVHQ4EFgQUv7TZGZaI+FifzjpTVjtPHSvb
XqUwgZoGA1UdIwSBkjCBj4AUv7TZGZaI+FifzjpTVjtPHSvbXqWhbKRqMGgxCzAJ
BgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEU
MBIGA1UEChMLR29vZ2xlIEluYy4xDjAMBgNVBAsTBU9ya3V0MQ4wDAYDVQQDEwVs
cnlhboIJAMbTCksqLiWeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
CETnhlEnCJVDXoEtSSwUBLP/147sqiu9a4TNqchTHJObwTwDPUMaU6XIs2OTMmFu
GeIYpkHXzTa9Q6IKlc7Bt2xkSeY3siRWCxvZekMxPvv7YTcnaVlZzHrVfAzqNsTG
P3J//C0j+8JWg6G+zuo5k7pNRKDY76GxxHPYamdLfwk=
-----END CERTIFICATE-----
EOS
</font>

  # Declares the keys for your app on different containers. The index is the
  # incoming consumer key sent in the signed makeRequest from the gadget.
  # These are sample gadget credentials and should be replaced with the HMAC
  # keys granted to your own gadget.
  KEYS = {
    'XXXXXXXXXX' => {
      :secret => 'XXXXXXXXXXXXXXXXXXXXXXXXXX',
      :outgoing_key => 'orkut.com:XXXXXXXXXX',
      :container => OpenSocial::Connection::ORKUT
    }
  }

  # Declares where your application lives. Points to the page where users are
  # redirected after loading the iframe.
  SERVER = 'http://www.example.com/'

  # Controls the auto-validation of the signed makeRequest.
  before_filter :check_signature, :only => [:iframe]

  # Renders pages with boilerplate HTML/CSS/JS.
  layout 'default'

  # Implicitly checks the signature of the incoming request and saves the
  # owner id, viewer id, and consumer key in a temporary session for later use.
  # The session id is returned as an iframe snippet that the gadget can use to
  # render the app.
  def iframe
    session[:id] = params[:opensocial_owner_id]
    session[:viewer] = params[:opensocial_viewer_id]
    session[:consumer_key] = params[:oauth_consumer_key]

    render :text => "&lt;iframe width='98%' height='600px' frameborder='0' src='#{SERVER}?sessid=#{session.model.session_id}' />"
  end

  # Loads the temporary session referenced by sessid, copies the values into
  # a persistent session that will be used to actually drive the app, and
  # deletes the temporary session (to prevent replay). Then, initiates a
  # connection to the given container and renders the appropriate content.
  def index

    proxied_session = session.model.class.find(:first,
                        :conditions => ['session_id = ?', params[:sessid]])

    if params[:sessid] && proxied_session
      session[:id] = proxied_session.data[:id]
      session[:viewer] = proxied_session.data[:viewer]
      session[:consumer_key] = proxied_session.data[:consumer_key]
      proxied_session.destroy
    end

    c = OpenSocial::Connection.new(:container => KEYS[session[:consumer_key]][:container],
                                   :consumer_key => KEYS[session[:consumer_key]][:outgoing_key],
                                   :consumer_secret => KEYS[session[:consumer_key]][:secret],
                                   :xoauth_requestor_id => session[:id])

    if session[:id] == session[:viewer]
      render_owner(c)
    elsif session[:viewer] && session[:id] != session[:viewer]
      render_viewer_with_app(c)
    else
      render_viewer_without_app(c)
    end
  end

  # Sends a gift on behalf of the viewer. If the viewer is the owner, the gift
  # is sent to the selected friend (but doesn't check to confirm that the owner
  # is friends with the specified ID). If the viewer is not the owner, the gift
  # is sent to the owner. Then the app redirects to the index.
  def give
    gift = Gift.new(params[:gift])
    if session[:id] != session[:viewer]
      gift.sent_by = session[:viewer]
      gift.received_by = session[:id]
    else
      gift.sent_by = session[:id]
    end

    if gift.save
      redirect_to :action => :index
    else
      flash[:error] = 'Error giving gift.'
      redirect_to :action => :index
    end
  end

  private

  # If the viewer is also the owner, this will render the list of gifts the
  # owner has sent or received. The owner can elect to send a gift to a friend.
  def render_owner(c)
    @id = session[:id]
    @gifts = Gift.find(:all,
                       :conditions => ['sent_by = ? OR received_by = ?', @id, @id],
                       :order => 'created_at DESC', :limit => 10)
    @gift_names = GiftName.find(:all)

    @people = fetch_gift_givers_and_friends(@id, @gifts, c)
    @owner = @people.delete(@id)

    render :action => "owner"
  end

  # If the viewer has the app installed, this will render the list of gifts
  # exchanged between the owner and viewer. The viewer can elect to send a gift
  # to the owner.
  def render_viewer_with_app(c)
    @id = session[:viewer]
    @owner_id = session[:id]

    @gifts = Gift.find(:all,
                       :conditions => ['(sent_by = ? AND received_by = ?) OR ' +
                                       '(sent_by = ? AND received_by = ?)',
                                       @owner_id, @id, @id, @owner_id],
                       :order => 'created_at DESC', :limit => 10)
    @gift_names = GiftName.find(:all)

    @viewer = OpenSocial::FetchPersonRequest.new(c, @id).send
    @owner = OpenSocial::FetchPersonRequest.new(c).send

    render :action => "viewer_with_app"
  end

  # If the viewer doesn't have the app installed, this will render the list of
  # gifts the owner has sent or received (without notes). No gifts may be sent.
  def render_viewer_without_app(c)
    @id = session[:id]
    @gifts = Gift.find(:all,
                       :conditions => ['sent_by = ? OR received_by = ?', @id, @id],
                       :order => 'created_at DESC', :limit => 10)
    @gift_names = GiftName.find(:all)

    @people = fetch_gift_givers_and_friends(@id, @gifts, c)

    render :action => "viewer_without_app"
  end

  # Requests social data for each of the people that have sent or received a
  # gift from the owner (oid). The method first generates a unique list of
  # user ids from the list of gifts, and sends a REST request for their data.
  # Users without the app installed will trigger an exception which is caught.
  # Finally, the owner's friends are fetched (which fills in the missing spaces
  # where insufficient permissions are granted to collect the user data
  # directly) and merged with the existing data.
  # This function could be sped up considerably by using a single RPC request
  # if the container supports it.
  def fetch_gift_givers_and_friends(oid, gifts, c)
    people = {}
    ids = gifts.collect {|g| [g.sent_by, g.received_by]}.flatten.uniq
    ids.each do |id|
      begin
        r = OpenSocial::FetchPersonRequest.new(c, id)
        people[id] = r.send
      rescue OpenSocial::AuthException
      end
    end
    friends = OpenSocial::FetchPeopleRequest.new(c, oid).send
    return people.merge(friends)
  end


  # Looks up the consumer secret paired with the given consumer key and
  # hands them off to the client library authentication.
  def check_signature
<font color="#ff0000">
    is_valid_request = false

    if params[:oauth_signature_method] == 'HMAC-SHA1'
      # 署名方式が HMAC-SHA1 
      key = params[:oauth_consumer_key]
      is_valid_request = validate(key, KEYS[key][:secret], {:signature_method => 'HMAC-SHA1'})
    elsif params[:oauth_signature_method] == 'RSA-SHA1'
      # 署名方式が RSA-SHA1
      # RSA-SHA1 では 公開鍵のみ必要なので validate メソッドの 第１引数は nil. 公開鍵は第２引数に指定する )
      is_valid_request = validate(nil, CERT, {:signature_method => 'RSA-SHA1'})
    end

    unless is_valid_request
      render :text => '401 Unauthorized', :status => :unauthorized
    end
</font>
  end

<font color="#ff0000">
  # opensocial client の validate メソッドを用いずに新たに作成.
  def validate(key = CONSUMER_KEY, secret = CONSUMER_SECRET, options={})

    consumer = OAuth::Consumer.new(key, secret, options)
    begin
      signature = OAuth::Signature.build(request) do
        [nil, consumer.secret]
      end
      pass = signature.verify
    rescue OAuth::Signature::UnknownSignatureMethod => e
      logger.error 'An unknown signature method was supplied: ' + e.to_s
    end
    return pass
  end
</font>

end
</pre>

以上で、署名方式 RSA-SHA1 を用いたリクエストが受け取れるようになります。<br /><br />

<b>が！</b><br /><br />

これだけではアプリケーションとしては動作しません。<br /><br />
何故かというと、index メソッド内にてユーザ情報を取得するために OpenSocialコンテナ(Orkut)へリクエストを送信するための Connection クラスのオブジェクトを作成しているのですが、そこで HMAC-SHA1 のデータが必要となるからです。<br /><br />
外部サーバ -> OpenSocialコンテナ(Orkut) へのリクエストには HMAC-SHA1 が必須となっていますので、結局前回のブログと同様に consumer key, consumer secret が必要になります。<br /><br />
それぞれを取得したら、下記のようにKEY定数を変更してます。<br />

<pre>
KEYS = {
    'orkut.com' => {
      :secret => '&lt;CONSUMER SECRET>',
      :outgoing_key => 'orkut.com:&lt;CONSUMER KEY>',
      :container => OpenSocial::Connection::ORKUT
    }
</pre>

これにてアプリケーションとして動作します。<br /><br />

外部サーバ -> OpenSocialコンテナ(Orkut)へのリクエストが必要ないのであれば RSA-SHA1 を用いるのが良いと思いますが、外部サーバ -> OpenSocialコンテナ(Orkut)へのリクエストも必要だとすると 両方向のリクエストの署名方式に HMAC-SHA1 を用いるのが楽でいいかもしれません。
<br /><br />
よういちろうさんが執筆された <a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774137480/eclipseplugin-22/ref=nosim">「OpenSocial入門 ソーシャルアプリケーションの実践開発」</a>が 2009年1月24日に発売されるとのことですので楽しみにしている今日この頃です<img src="http://parts.blog.livedoor.jp/img/emoji/ic_note.gif">OpenSocialを用いたアプリ開発をされる方には必須となるのではないでしょうか。
<br />


]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/1102807.html">
<title>速攻で作る OpenSocialアプリ</title>
<link>http://blog.banana-systems.com/archives/1102807.html</link>
<description>新年明けましておめでとうございます。


mixiアプリも採用の Open Social


日本最大のSNSである mixiが mixiアプリ にOpen Socialを採用したことにより、今年2009年は他社のサービスでもOpenSocialを採用するところが一気に増えてくるではないでしょうか。

そこで...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2009-01-02T06:16:29+09:00</dc:date>
<dc:subject>OpenSocial</dc:subject>
<content:encoded><![CDATA[新年明けましておめでとうございます。<br/>
<br/>

<h2>mixiアプリも採用の Open Social</h2>

<br/>
日本最大のSNSである mixiが mixiアプリ にOpen Socialを採用したことにより、今年2009年は他社のサービスでもOpenSocialを採用するところが一気に増えてくるではないでしょうか。<br/><br/>

そこで弊社Banana Systemsが2009年最初にお届けする記事は「速攻で作るOpenSocialアプリ」です。<br/><br/>

OpenSocialの詳細につきましては、<a href="http://code.google.com/intl/ja/apis/opensocial/">オフィシャル・ページ</a> にて説明されていますのでここでは割愛します。<br/><br/>

まず前提として、<br/><br/>

・<b>Orkut用のソーシャル・アプリ</b><br/>
　　今現在 mixiアプリはまだβ版であり一般には公開されていませんので、<br/>
　　GoogleのSNSであるOrkutにてソーシャル・アプリを開発します。<br/><br/>

・<b>自社サーバからデータの取得、保存</b><br/>
　　HTML, Javascriptのみを用いたシンプルなソーシャル・アプリではなく、<br/>
　　実際のサービスを考えた時には外部(自社)サーバからコンテンツ、<br/>
　　データの取得、また逆に保存を行いますので、↓のような形式とします。<br/>
　　<b>外部サーバ <-> Orkut(OpenSocialコンテナ) <-> ソーシャル・アプリ</b><br/>
<br/>
・<b>OpenSocial Client Libraryの使用</b><br/>
　　2008年12月17日に <a href=" http://opensocialapis.blogspot.com/2008/12/opensocial-now-friends-with-php-java.html">OpenSocial Blog</a> にてPHP, Java, Ruby, Python用のOpenSocial Client Library が発表されました。<br/>
　　このライブラリはサーバ間(外部サーバ <-> OpenSocialコンテナ)通信の複雑なやり取りを隠ぺいしてくれます。<br/>
　　Ruby用のものには Railsアプリのサンプルが入っていますので、今回はそのRailsアプリを動作させるまでを説明します。<br/>

<br/>
<h2>OpenSocialアプリの構築</h2>
<br/>
それでは始めていきましょう！<br/>
<br/>
<a href="http://code.google.com/p/opensocial-ruby-client/wiki/GettingStarted">Getting started with the Ruby Client Library</a> に大まかな説明がされているので、それに沿って進めていきますが、Orkut(OpenSocialコンテナ)から外部サーバへ通信する際に必要となるSigned 通信の設定方法が記述されていなかったり、migration
ファイルに誤りがありそのままでは動作しませんので、補足しながら説明していきます。<br/>
<br/>
1. 必要なライブラリのインストール<br/>
<br/>
　# gem install oauth json mocha rails<br/>
<br/>
2. opensocial ライブラリのダウンロードとインストール<br/>
<br/>
　<a href="http://code.google.com/p/opensocial-ruby-client/downloads/list">ダウンロード ページ</a> からopensocial-X.X.X.gem をダウンロードします。<br/>
<br/>
　ダウンロードしたgemをインストールします。<br/>
<br/>
　# gem install opensocial-X.X.X.gem<br/>
<br/>
3. サンプルの Railsアプリのダウンロード<br/>
<br/>
　<a href="http://code.google.com/p/opensocial-ruby-client/downloads/list">ダウンロード ページ</a> から gifts_sample_X.X.zip をダウンロードします。<br/>
<br/>
　ダウンロードしたzipファイルを展開すると gifts_sampleディレクトリ(Railsアプリのトップ)が作成されますので、Railsアプリを設置する適切なディレクトリに移動してください。<br/>
<br/>
4. DB設定<br/>
<br/>
　GIFT_SAMPLE/config/database.ymlの設定を適切なものに変更し、DBにデータベースを作成してください。<br/>
　※GIFT_SAMPLE は 当Railsアプリのトップディレクトリとします。<br/>
<br/>
今回はMySQLを使用しました。<br/>
※database, username, password はサンプルですので適宜変更してください。<br/>
<br/>
<pre>
development:
  adapter: mysql
  encoding: utf8
  database: gifts_development
  pool: 5
  username: banana
  password: banana
  host: localhost

test:
  adapter: mysql
  encoding: utf8
  database: gifts_test
  pool: 5
  username: banana
  password: banana
  host: localhost

production:
  adapter: mysql
  encoding: utf8
  database: gifts_production
  pool: 5
  username: banana
  password: banana
  host: localhost
</pre>

MySQL コンソールにて ( GRANT文は適宜変更してください )<br/>

<pre>
> GRANT ALL PRIVILEGES ON *.* TO banana@localhost IDENTIFIED BY 'banana' WITH GRANT OPTION;
> flush privileges;
> create gifts_development DEFAULT CHARSET=utf8;
> create gifts_test DEFAULT CHARSET=utf8;
> create gifts_production DEFAULT CHARSET=utf8;
</pre>

5. migrationファイルの修正<br/>
<br/>
　GIFT_SAMPLE/config/20081201192139_create_gifts.rb に note カラムを追加します。<br/>

<pre>
class CreateGifts < ActiveRecord::Migration
  def self.up
    create_table :gifts do |t|

      t.integer :gift_name_id
      t.string :sent_by
      t.string :received_by
      t.boolean :viewed
      
      # note カラムの追加
      <font color="#FF0000">t.string :note</font>
      
      t.timestamps
    end
  end

  def self.down
    drop_table :gifts
  end
end
</pre>

※通常はmigrationファイルを直接編集せずに ruby script/generate migration AddNoteToGift note:string コマンドにて新しい migrationファイルを作成しますが、DBにまだテーブルを作成していないので今回は migration ファイルを直接編集しました。↓の migration ファイルも同様に直接編集しています。<br/>
<br/>

　GIFT_SAMPLE/config/20081201203248_create_gift_names.rb に ギフトを追加します。<br/>
　※ Gift アプリはギフトを友達に贈ったりするものなのですが、ギフトはDBに保存しておかないといけません。<br/>

<pre>
class CreateGiftNames < ActiveRecord::Migration
  def self.up
    create_table :gift_names do |t|
      t.string :name
      t.timestamps
    end
    
    # ギフトの追加
    <font color="#FF0000">
    GiftName.create(:id => 1, :name => 'book')
    GiftName.create(:id => 2, :name => 'coffee')
    GiftName.create(:id => 3, :name => 'candy')
    GiftName.create(:id => 4, :name => 'icecream')
    </font>
    
  end

  def self.down
    drop_table :gift_names
  end
end
</pre>

6. DBにテーブル作成<br/>
<br/>
　Gift sample アプリのトップディレクトリにて<br/>
　# rake migrate<br/>
<br/>
7. サーバの起動<br/>
<br/>
　Gift sample アプリのトップディレクトリにて<br/>
　# ruby script/server<br/>
<br/>
　上記コマンドにより、本アプリは <b>http://www.example.com/</b>にて動作しているとし以降の説明を進めます。<br/>
<br/>
<h2>OrkutにてGiftsアプリのインストール</h2>
<br/>
1. Orkutにアカウントのない方は、<a href="http://www.orkut.com">ここから</a> アカウントを作成してください。<br/>
<br/>
2. デベロッパー サンドボックスへの登録<br/>
<br/>
　Orkutにてサンプルのアプリをインストールするには、サンドボックスに登録する必要があります。<br/>
<br/>
　Orkutにログイン後、<a href="http://sandbox.orkut.com/SandboxSignup.aspx">ここから</a> 登録します。<br/>
<br/>
　※以前は登録が承認されるまでに数日要しましたが、現在はすぐに完了します。<br/>
<br/>
3. Giftsアプリのインストール<br/>
<br/>
　サイドメニューのアプリの編集をクリックします。<br/>
<br/>
<img src="http://image.blog.livedoor.jp/banana_systems/imgs/d/a/dadb423b.gif" width="157" height="206" border="0" alt="orkut_sidemenu_1" hspace="5" class="pict" align="left"  />

<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>

　URLに本Giftsアプリの下記URLを入力し、「アプリケーションを追加」ボタンをクリックします。<br/>
<br/>
　<b>http://www.example.com/gifts.xml</b><br/>
<br/>
<img src="http://image.blog.livedoor.jp/banana_systems/imgs/7/3/73b8cc6a.gif" width="575" height="238" border="0" alt="orkut_add_sample_app_1" hspace="5" class="pict" align="left"  />
<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>

　アプリケーション追加をクリックします。<br/>
<br/>

<img src="http://image.blog.livedoor.jp/banana_systems/imgs/6/e/6e23c728.gif" width="292" height="340" border="0" alt="orkut_add_sample_app_2" hspace="5" class="pict" align="left"  />

<br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/>

　これでGiftsアプリのサンドボックスへのインストールは完了です。<br/>
<br/>
<h2>外部サーバにてOrkut(OpenSocialコンテナ)からの署名付きのリクエストを受け付ける</h2>
<br/>
　Gifts アプリでは、Orkutと外部サーバ間にてデータの改ざんが行われないように、Orkutから外部サーバへの通信には署名付けられるように実装されています。<br/>
　(GIFT_SAMPLE/public/gifts.xml の 13行目辺り params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED; )<br/>
　実サービスにおいても、署名付きリクエストは必須となるでしょう。<br/>
<br/>

<font color="#ff0000">追記: 2009-01-10 05:30 (コメント参照) ---------</font><br />
また、署名方式には HMAC-SHA1 を用いるように指定されています。
(GIFT_SAMPLE/public/gifts.xml の 14行目辺り params["OAUTH_SERVICE_NAME"] = "HMAC"; )<br/>
<font color="#ff0000">追記ここまで ------------</font><br /><br />

この署名方式では Consumer Key と Consumer Secret Key が必要となります。<br /><br />

1. Consumer Key と Consumer Secret Key の取得<br/>
<br/>
　Orkutでは、Consumer Key と Consumer Secret Keyを発行する際に、本アプリの所有者があなたか確認するためにトークンを発行します。<br/>
<br/>
　下記ページにGiftsアプリのURL( http://www.example.com/gifts.xml )を入力し、「submit」ボタンをクリックします。<br/>
　<a href="https://www.google.com/gadgets/directory/verify">https://www.google.com/gadgets/directory/verify</a><br/>
<br/>
　すると、 &lt;!-- ALm6fM2iKDPf9qY5 ... gMjzbD1cwDBifBX5w== --> といったトークンが発行されます。<br/>
　※ブラウザは閉じずにそのままにしておいてください。<br/>
<br/>
　GIFT_SAMPLE/public/gifts.xml の Content タグ内(CDATA開始タグとscriptタグの間)に発行されたトークンを挿入し、保存します。<br/>
　
<pre>
&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;Module>
  &lt;ModulePrefs title="Gifts">
	  &lt;Require feature="opensocial-0.8" />
	  &lt;Require feature="dynamic-height"/>
  &lt;/ModulePrefs>
  &lt;Content type="html">
    &lt;![CDATA[
    <font color="#FF0000">&lt;!-- ALm6fM2iKDPf9qY5 ... gMjzbD1cwDBifBX5w== --></font>
	    &lt;script>
  	    // Sends a signed makeRequest to a specified remote server to load the iframe
  	    function sendSignedRequest(server) {
</pre>

　さきほどのページにて「Verify」ボタンをクリックします。<br/>
　Giftsアプリの所有者があなたであることが確認されると、下記のように Consumer Key と Consumer Secret Key が発行されます。<br/>

<br/>
<b>OAuth Consumer Key:</b>  294828716828<br/>
<b>OAuth Consumer Secret:</b>  xw/zDlw3y4p4yRFZo2mCQCID<br/>

<br/>
2. Controller の修正<br/>
<br/>
　Gifts アプリの gifts controller にて、署名付きリクエストを受け付けられるようにします。<br/>
<br/>
　GIFT_SAMPLE/app/controllers/gifts_controller.rb を開き、KEYS定数に先ほどのConsumer KeyとConsumer Secretを追加します。<br/>

<pre>
  # Declares the keys for your app on different containers. The index is the
  # incoming consumer key sent in the signed makeRequest from the gadget.
  # These are sample gadget credentials and should be replaced with the HMAC
  # keys granted to your own gadget.
  KEYS = {
    # ↓追加
    <font color="#FF0000">
    '294828716828' => {
      :secret => 'xw/zDlw3y4p4yRFZo2mCQCID',
      :outgoing_key => 'orkut.com:294828716828',
      :container => OpenSocial::Connection::ORKUT
    },
    </font>
    # ↓削除してしまってかまいません
    '649048920060' => {
      :secret => '2rKmqUigMbFqK783szqzzOky',
      :outgoing_key => 'orkut.com:649048920060',
      :container => OpenSocial::Connection::ORKUT
    },
    'http://opensocial-resources.googlecode.com/svn/samples/rest_rpc/sample.xml' => {
      :secret => '6a838d107daf4d09b7d446422f5e7a81',
      :outgoing_key => 'http://opensocial-resources.googlecode.com/svn/samples/rest_rpc/sample.xml',
      :container => OpenSocial::Connection::MYSPACE
    }
  }
</pre>

　KESY定数はHashになっています。HashのKeyに先ほどの Consumer Key を指定します。そのkeyのvalueもHashになっていますので、secretには先ほどのConsumer Secretを指定します。また、outgoing_keyには orkut.com: に続けて再度 Consumer Key を指定します。<br/>
<br/>
　SERVER 定数も変更します。<br/>
　
<pre>
# Declares where your application lives. Points to the page where users are
  # redirected after loading the iframe.
  SERVER = 'http://www.example.com/'
</pre>

　これにて署名付きリクエストが受け付けられるようになります。<br/>
<br/>
　Giftsアプリが署名付きリクエストを外部サーバに送信する(実際には、リクエストを送信するのはOpenSocialコンテナ(今回はOrkutのサーバ))のはユーザが本アプリにアクセスした初めの一度だけです。<br/>
　これは Giftsアプリがコンテンツをiframeにて表示するからです。<br/>
<br/>
　よって、iframe内にて表示するコンテンツのURLを指定する必要があります。<br/>
<br/>
　GIFT_SAMPLE/public/gifts.xml を開いて、30行目辺りの sendSignedRequest メソッドへの引数を下記のように gifts controller の iframe アクションへのURLに変更します。<br/>
　
<pre>
    sendSignedRequest('http://www.example.com/gifts/iframe');
</pre>

　最後にサーバを再起動して終了です。<br/>
<br/>
　今回は OpenSocialコンテナ(Orkut) -> 外部サーバへの一方的な通信でしたが、OAuthを用いて外部サーバ -> OpenSocialコンテナへの通信も可能となります。<br/>
　mixi では <a href="http://developer.mixi.co.jp/connect">mixi connect</a> がそれに当ります。<br/>
<br/>
　今回の記事のタイトルが「速攻で作る OpenSocialアプリ」でありながら全くもって速攻では終わりませんでしたが、実サービスにて必須となるであろう署名付きリクエストの受付方法を示すことができましたのでご参考になれば幸いです。<br/>
<br/>　
　本年もBananan Systemsをどうぞ宜しくお願い申し上げます。]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/1037174.html">
<title>Flex/AIR チャートシリーズの LineSeries に常に値を表示する</title>
<link>http://blog.banana-systems.com/archives/1037174.html</link>
<description>Flex の Chart Series には ColumnSeries, PieSeries, LineSeries 等さまざまな種類のものがありますが、ColumnSeries, PieSeries などはlabelPositionプロパティに値を指定することにより、下の画像のように常に値を表示させることが可能です。




　　　　　　　　　...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-11-15T18:05:11+09:00</dc:date>
<dc:subject>Flex</dc:subject>
<content:encoded><![CDATA[Flex の Chart Series には ColumnSeries, PieSeries, LineSeries 等さまざまな種類のものがありますが、ColumnSeries, PieSeries などはlabelPositionプロパティに値を指定することにより、下の画像のように常に値を表示させることが可能です。<br>
<br>
<img src="http://image.blog.livedoor.jp/banana_systems/imgs/0/3/03664edc.gif" width="461" height="544" border="0" alt="Labels for ColumnSeries, PieSeries" hspace="5" class="pict" align="left"  /><br>
<br>
<br>
　　　　　　　　　　　<br>
<br /><br>
しかしながら、<b><font color="#ff0000">LineSeriesに関しては常に値を表示させることができません。</font></b><br>
マウスオーバーの際に値を表示させる DataTip(ToolTip ※1) は使用可能です。<br>
<br>
以前、自分も同じようなことを行いたく自作したLineSeriesDataLabelクラスがあったので少し変更して紹介します。<br>
<br>
<br>
<a href="http://www.banana-systems.com/blog/samples/LineSeriesDataLabel/Sample.html"><img src="http://image.blog.livedoor.jp/banana_systems/imgs/6/5/657b37d3.gif" width="461" height="463" border="0" alt="LineSeriesDataLabel_Original" hspace="5" class="pict" align="left"  /></a><br>
<br>
<br>
　　　　　　　　　　　<br>
<a href="http://www.banana-systems.com/blog/samples/LineSeriesDataLabel/Sample.html">LineSeries に常に値を表示する LineSeriesDataLabel</a><br>
(右クリックからソースが見れます)<br>
<br>
<br>
今後他のシリーズにも適応できるようにLineSeriesを直接継承せずに、ChartElementを継承しています。<br>
LineSeriesDataLabelのseriesプロパティにLineSeriesのインスタンスを指定します。<br>
<br>
各値を表示するラベル(UITextField)がチャートの表示領域からはみ出した場合には内側にずらす簡単な処理を入れていますが、複雑なポジショニングが必要な場合は、LineSeriesDataLabelクラスのlabelPositioningFunctionに独自のFunctionを設定してください。<br>
<br>
その Function の引数、実装等は LineSeriesDataLabel#defaultLabelPositioningFunction を参考にしてください。<br>
<br>
<br>
<br>
※１<br>
あまり気にする必要はありませんが、DataTip と ToolTip には下記の明確な違いがあります。<br>
「DataTip コントロールは ToolTip コントロールに似ていますが、マウスポインタから最も近いグラフのデータポイントを表す適切な値が表示されるという点で異なります。」 <br>
http://livedocs.adobe.com/flex/3_jp/langref/mx/charts/chartClasses/ChartBase.html]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/879007.html">
<title>itemRenderer パート４ : ステート ＆ トランジション</title>
<link>http://blog.banana-systems.com/archives/879007.html</link>
<description>原文 : http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p_3.html

itemRenderers: Part 4: ステート と トランジション

itemRenderer は、視覚的に情報を伝えることにとても優れています。

伝えると一口で言っても、名前を表示するだけのシンプ...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-08-27T12:26:44+09:00</dc:date>
<dc:subject>Flex</dc:subject>
<content:encoded><![CDATA[原文 : <a href="http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p_3.html">http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p_3.html</a><br>
<br>
itemRenderers: Part 4: ステート と トランジション<br>
<br>
itemRenderer は、視覚的に情報を伝えることにとても優れています。<br>
<br>
伝えると一口で言っても、名前を表示するだけのシンプルなものから、たくさんの色を使い表現するもの、時にはインタラクションを含むものまであります。<br>
<br>
itemEditor がまさにインタラクティブなコントロールですが、今回の記事の目的ではありませんので割愛します。<br>
今回の記事では、itemRenderer の data プロパティやユーザのアクションにより外観を変化させる itemRenderer について見ていきます。<br>
<br>
■ ステート<br>
<br>
&lt;mx:State&gt; は、itemRendererの外観を変更するのにとても適した方法です。<br>
ステートはとても簡単に使うことができ、トランジションと組み合わせることにより、ユーザにフィードバックを返して、使い勝手をよくすることができます。<br>
<br>
今回のサンプルでは、List コンポーネント用に、MXML を用いた itemRenderer を作成していきます。( ActionScript のみでも作成可能です )<br>
<br>
itemRenderer で表示するのは、image, title, author, price と 本を購入するための Button コントロールです。<br>
<br>
<pre class="prettyprint">&lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
&lt;mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" &gt;<br>
<br>
    &lt;mx:Image id="bookImage" source="{data.image}" /&gt;<br>
    &lt;mx:VBox height="115" width="100%" verticalAlign="top" verticalGap="0" paddingRight="10"&gt;<br>
        &lt;mx:Text text="{data.title}" fontWeight="bold" width="100%"/&gt;<br>
        &lt;mx:Label text="{data.author}" /&gt;<br>
        &lt;mx:HBox id="priceBox" width="100%"&gt;<br>
            &lt;mx:Label text="{data.price}" width="100%"/&gt;<br>
            &lt;mx:Button label="Buy" /&gt;<br>
        &lt;/mx:HBox&gt;<br>
    &lt;/mx:VBox&gt;<br>
&lt;/mx:HBox&gt;</pre><br>
<br>
もし本の在庫が無ければ、その本が非表示になるようにしましょう( data の &lt;instock&gt; タグの値　( yes / no ) で在庫の有無を判定します )。<br>
実装を簡単にするために、HBox の id に "priceBox" を指定しています。<br>
この HBox の可視状態を変更することにより、内部の Label と Button  両方の可視状態も変更できるからです。<br>
<br>
set data メソッドをオーバーライドしてもいいのですが、ここでは priceBox の可視状態を直接変更せずに、ステートを使ってみます。<br>
<br>
<pre class="prettyprint">&lt;mx:states&gt;<br>
        &lt;mx:State name="NoStockState"&gt;<br>
            &lt;mx:SetProperty target="{priceBox}" name="visible" value="false"/&gt;<br>
        &lt;/mx:State&gt;<br>
&lt;/mx:states&gt;</pre><br>
<br>
このタグをルートタグ直下に配置してください。<br>
<br>
やりたいことが簡単なわりに少々複雑になってしまっていますが、ステートの使い方がよくわかると思います。<br>
以下の２つのステートが存在します。<br>
<br>
・ ベースステート : これはコンポーネントの標準(デフォルト)の状態です。コンポーネントがステートを使っていないときは、この状態になっています。今回の例では、ステートがベースステートの時には priceBox の visible プロパティが true (デフォルト値) になります。 これは instock タグの値が yes の時です。<br>
<br>
・ NoStockState : これは instock タグの値が no の時のステートです。 このステートになると &lt;mx:State&gt; タグ内の SetProperty が処理されます。 target はそのSetProperty 処理の対象となるインスタンスを指定します。name プロパティはアップデートするプロパティ名、value プロパティは新しくセットする値です。<br>
<br>
set data メソッドは、instock の値に基づいてステートを切り替えます。<br>
<br>
<pre class="prettyprint">override public function set data( value:Object ) : void<br>
        {<br>
            super.data = value;<br>
            <br>
            if( data )<br>
            {<br>
                if( data.instock == "yes" )<br>
                    currentState = "";<br>
                else<br>
                    currentState = "NoStockState";<br>
            }<br>
        }</pre><br>
<br>
currentState は全ての UIComponent コントロールが備えているプロパティであり、現在のステート名を保持しています。 ステートを切り替えると、Flex フレームワークはベースステートから始め、新たなステートへの切り替えに必要な処理を行います。<br>
<br>
----------------<br>
itemRenderer は再利用されますので、必ず元の値(状態)に戻す処理を入れてください。itemRenderer に if を書いたら、必ず else も書く必要があります。<br>
----------------<br>
<br>
もし興味があればですが、下のコードのように set data メソッドをオーバーライドしない方法もあります。 データバインディングを使って、 root タグの中で currentState の値を直接変更しています。<br>
<br>
&lt;mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400"<br>
       currentState="{data.instock == 'yes' ? '' : 'NoStockState'}" &gt;<br>
<br>
インラインで data.instock の値を評価し、その結果を currentState プロパティに代入しています。 トリッキーなので保守性には優れていないかもしれません。<br>
<br>
■ エレメントの追加<br>
<br>
次の itemRenderer では、instack の値が yes の時にだけ price と 購入ボタンが表示されます。<br>
もちろんステートを使わずに同じことは出来ますが、もし itemRenderer に追加、削除するコントロールが多数ある場合にはステートを使うのが得策です。　<br>
なぜなら、itemRenderer の currentState プロパティに値をセットするだけでそれら多数のコントロールを管理できるからです。<br>
<br>
単に price と 購入ボタン を削除するだけでなく、在庫切れであることを表示する Label も追加します。<br>
<br>
変更を加えたコードは次のようになります。<br>
<br>
<pre class="prettyprint">&lt;mx:states&gt;<br>
        &lt;mx:State name="NoStockState"&gt;<br>
            &lt;mx:SetProperty target="{priceBox}" name="visible" value="false"/&gt;<br>
            &lt;mx:AddChild relativeTo="{priceBox}" position="before"&gt;<br>
                &lt;mx:Label text="-- currently not in stock --" color="#73DAF0"/&gt;<br>
            &lt;/mx:AddChild&gt;<br>
        &lt;/mx:State&gt;<br>
    &lt;/mx:states&gt;</pre><br>
<br>
&lt;mx:AddChild&gt; タグで priceBox に Label を追加しています。<br>
priceBox の visible プロパティに false をセットし、代わりにわかりやすい文字列を表示します。<br>
<br>
繰り返しますが、set data メソッドをオーバーライドすることで、同じように Label を追加することもできます。また、必要なコンポーネントを前もって追加しておいて、それらの可視状態を後で変更するといった方法も可能です。<br>
<br>
しかし、ステートにはわかりやすいメリットがあります。<br>
ステートを使えば、在庫切れ状態の表示に必要な処理がどんなに複雑になっても、NoStockState を編集するだけで十分です。ステートを切り替えるための ActionScript コードを変更する必要はありません。<br>
<br>
---------------<br>
    Flex Builder の Design View ではステートを編集することができます。<br>
---------------<br>
<br>
■ 伸び縮みする List<br>
<br>
今回の例は、List コントロールではうまく動作しませんが、VBox と Repeater を用いて実現できます。<br>
もし List コントロールを使用して、そのリストがスクロールされてしまうと伸長させたアイテムの itemRenderer が機能しなくなるかもしれません。<br>
例えば、高さが同じアイテムを複数保持するリストがあるとします。<br>
ここで２番目のアイテムの高さを伸長させると、そのアイテムの高さは他のアイテムよりも高くなります。 ここまでは大丈夫です。<br>
( And there's the catch: the visible items. )<br>
次にそのリストをスクロールさせます。 itemRenderer は再利用されるのを覚えていますよね。 ２番目のアイテムがリストの外に出ると、そのアイテムの itemRenderer はリストの最下部のアイテムを表示するために移動します。<br>
その時、その itemRenderer の高さを元に戻す必要があります。<br>
そして、２番目のアイテムが再度見えるようにListをスクロールします。<br>
２番目のアイテムの高さが伸長されたままになっていて欲しいところですが、itemRenderer は前の高さのことを知りません。<br>
以前の記事に書いたように、それらの情報は itemRenderer の data プロパティ、もしくは何かしらの外部ソースから得なければなりません。<br>
<br>
itemRenderer のリサイズはとても複雑になりがちなので、頑張って実装するわりには大したものにならないと思います。<br>
もしリサイズが必要なのであれば VBox と Repeater を用いる方法が良いと思います。しかし Repeater を用いる方法の欠点として、Repeater で表示する子コンポーネントのインスタンス全てが生成されてしまいます。<br>
もし Repeater を用いて 1000 個のレコードを表示すると、itemRenderer のインスタンスが1000 個生成されてしまいます。<br>
<br>
今回の例では、VBox の 子コンポーネントとなる itemRenderer を作成します。<br>
本のタイトルと著者のみを表示するとてもシンプルな itemRenderer ですが、クリックするとその場で itemRenderer が伸長するようにします。<br>
これは下記の２つを使って実現できます。<br>
<br>
* itemRenderer に追加情報を持つステートを持たせる<br>
* itemRenderer をスムーズに伸縮させるため Resize トランジションを使用する<br>
<br>
itemRenerer のベース ステートはとてもシンプルです:<br>
<br>
<pre class="prettyprint">&lt;mx:HBox width="100%"&gt;<br>
        &lt;mx:Label text="{data.author}" fontWeight="bold"/&gt;<br>
        &lt;mx:Text  text="{data.title}" width="100%" fontSize="12" selectable="false"/&gt;<br>
    &lt;/mx:HBox&gt;</pre><br>
<br>
ExpandedState ステートでは新たな情報を付加して itemRenderer の高さの調節をします。<br>
<br>
<pre class="prettyprint">&lt;mx:states&gt;<br>
        &lt;mx:State name="ExpandedState"&gt;<br>
            &lt;mx:AddChild position="lastChild"&gt;<br>
                &lt;mx:HBox width="100%"&gt;<br>
                    &lt;mx:Image source="{data.image}"/&gt;<br>
                    &lt;mx:Spacer width="100%"/&gt;<br>
                    &lt;mx:Label text="{data.price}"/&gt;<br>
                    &lt;mx:Button label="Buy"/&gt;<br>
                &lt;/mx:HBox&gt;<br>
            &lt;/mx:AddChild&gt;<br>
        &lt;/mx:State&gt;<br>
&lt;/mx:states&gt;</pre><br>
<br>
itemRenderer のサイズ変更にかかる手間は、トランジションをただ追加するのと大差ありません。<br>
<br>
<pre class="prettyprint">&lt;mx:transitions&gt;<br>
    &lt;mx:Transition fromState="*" toState="*"&gt;<br>
        &lt;mx:Resize target="{this}" /&gt;<br>
    &lt;/mx:Transition&gt;<br>
&lt;/mx:transitions&gt;</pre><br>
<br>
<br>
この トランジションは fromState と toState プロパティの両方にワイルドカードが指定されているので、ステートが変更される毎に再生されます。<br>
<br>
後は、itemRenderer のクリックイベントのハンドラを用意し、そのハンドラ内でステートを変更するだけです。<br>
<br>
    <pre class="prettyprint">&lt;mx:Script&gt;<br>
    &lt;![CDATA[<br>
       <br>
        private function expandItem() : void<br>
        {   <br>
            if( currentState == "ExpandedState" )<br>
                currentState = "";<br>
            else<br>
                currentState = "ExpandedState";<br>
        }<br>
    ]]&gt;<br>
    &lt;/mx:Script&gt;</pre><br>
<br>
■ Summary<br>
<br>
ステートは、itemRenderer の外観を変更するのにとても良い方法です。<br>
複数の変更をステートとしてまとめることにより、itemRendererへの変更を一斉に反映させることができます。<br>
<br>
次回の記事では、UIComponent クラスを継承して効率的な itemRenderer を作成します。<br>
<br>
 <br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/812058.html">
<title>itemRenderer パート３ : Communication ( データのやりとり )</title>
<link>http://blog.banana-systems.com/archives/812058.html</link>
<description>○ 原文
itemRenderers: Part 3: Communication

前回の記事では external itemRenderer の作成方法を、MXML と ActionScript の両方で説明しました。
サンプルコードでは、itemRenderer 内の Button がクリックされるとカスタム・イベント(BuyBookEvent)をディスパッチ...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-07-29T12:22:11+09:00</dc:date>
<dc:subject>Flex</dc:subject>
<content:encoded><![CDATA[○ 原文<br>
<a href="http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p_2.html">itemRenderers: Part 3: Communication</a><br>
<br>
前回の記事では external itemRenderer の作成方法を、MXML と ActionScript の両方で説明しました。<br>
サンプルコードでは、itemRenderer 内の Button がクリックされるとカスタム・イベント(BuyBookEvent)をディスパッチして、itemRenderer外でそのイベントに応じた処理を行えるようにしました。<br>
今回の記事では、itemRenderer とのデータのやり取りを掘り下げていきたいと思います。<br>
<br>
絶対に破ってほしくないルールが一つあります。それは「(外部から) itemRenderer のインスタンスを保持し、（publicなプロパティをセットすることにより）itemRendererを変更したり、itemRenderer の public メソッドを呼び出してはいけない」ということです。 <br>
Part 1 の記事で述べたように、 itemRenderer は再利用されるものなので保持するのはとても難しく、もしそれを行ってしまうと Flex frameworkの挙動を狂わせかねません。<br>
<br>
このルールをに則って考えると、itemRenderer でできるのは次のようなことです。<br>
<br>
* itemRenderer は自身を適用したコンポーネントを通じてイベントをディスパッチできます。（既にバブリングを紹介しました。この方法が優れていることを後で説明します）<br>
* itemRenderer では Application.application などのクラス変数が使用可能です。Application オブジェクトに「グローバルに」変数を定義したのなら、この方法でその変数にアクセス可能です。 <br>
* itemRenderer は自身を適用したコンポーネントのpublic 変数にアクセス可能です。<br>
* itemRenderer は data のレコードの全てのフィールドにアクセスできます。例えば、直接画面に表示するデータでなくとも、itemRendererの動作に影響を与えるフィールドにアクセスしたりできます。<br>
<br>
<br>
■ itemRenderer の処理を動的に変更する<br>
<br>
次のコードは、前回の記事で TileList 用に作成した MXML の itemRenderer です。<br>
これを外部のデータによってitemRendererの処理を動的に変えるようにします。 ( ファイル名を BookItemRenderer.mxml とします )<br>
<pre class="prettyprint">&lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
&lt;mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="250" height="115" &gt;<br>
	<br>
	&lt;mx:Script&gt;<br>
	&lt;![CDATA[		<br>
	]]&gt;<br>
	&lt;/mx:Script&gt;<br>
	<br>
	&lt;mx:Image id="bookImage" source="{data.image}" /&gt;<br>
	&lt;mx:VBox height="115" verticalAlign="top" verticalGap="0"&gt;<br>
		&lt;mx:Text text="{data.title}" fontWeight="bold" width="100%"/&gt;<br>
		&lt;mx:Spacer height="20" /&gt;<br>
		&lt;mx:Label text="{data.author}" /&gt;<br>
		&lt;mx:Label text="Available {data.date}" /&gt;<br>
		&lt;mx:Spacer height="100%" /&gt;<br>
		&lt;mx:HBox width="100%" horizontalAlign="right"&gt;<br>
			&lt;mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]"&gt;<br>
				&lt;mx:click&gt;<br>
				&lt;![CDATA[<br>
					var e:BuyBookEvent = new BuyBookEvent();<br>
					e.bookData = data;<br>
					dispatchEvent(e);<br>
				]]&gt;<br>
				&lt;/mx:click&gt;<br>
			&lt;/mx:Button&gt;<br>
		&lt;/mx:HBox&gt;<br>
	&lt;/mx:VBox&gt;<br>
	<br>
&lt;/mx:HBox&gt;</pre><br>
<br>
TileList を用いてアイテムのカタログの表示をしようとしているとします。<br>
金額の範囲を設定できる Slider コントロールも存在するとしましょう（Slider コントロールは itemRenderer の外側にあります）。<br>
金額の範囲外のアイテムは、フェードアウトさせます（itemRendererのアルファ値を変化させます）。<br>
itemRenderer の alpha 値を変更するためには、金額の範囲が変わったことを全ての itemRenderer に伝える必要があります。<br>
<br>
set data メソッドを次のようにオーバーライドします。<br>
<br>
<pre>override public function set data( value:Object ) : void<br>
{<br>
    super.data = value;<br>
    if( data.price &lt; criteria ) alpha = 0.4;<br>
    else alpha = 1;<br>
}</pre><br>
<br>
問題は、どのように criteria (金額の範囲)の値を変えればいいかです。<br>
itemRenderer を使う場合のベスト・プラクティスは、「itemRenderer は常に与えられたデータのみに基づいて動作させる」ことです。<br>
しかし、今回のようなケースでは criteria を data に含めるのは良い方法とは言えませんので、data の外に存在するようにしましょう。<br>
これを実現する方法はいくつかあります。<br>
<br>
* list に含める。 list ( List, DataGrid, TileList 等 ) コンポーネントを継承し、その継承したクラスに public な変数として criteria (金額の範囲) を保持させる。<br>
* グローバル変数として application オブジェクトに含める。<br>
<br>
私ならば、一番目のクラスを継承して criteria をクラスに含める方法を選びます。<br>
つまるところ、クラスはデータを表示するために使われていて、criteria は表示される項目の一部なのですから。<br>
今回の例では、TileListを継承して criteria を public なデータメンバーとして持たせます。<br>
<br>
<pre class="prettyprint">package<br>
{<br>
	<br>
	import mx.controls.TileList;<br>
	<br>
	public class CatalogList extends TileList<br>
	{<br>
		public function CatalogList()<br>
		{<br>
			super();<br>
		}<br>
		<br>
		private var _criteria:Number = 10;<br>
		<br>
		public function get critera() : Number<br>
		{<br>
			return _criteria;<br>
		}<br>
		<br>
		public function set criteria( value:Number ) : void<br>
		{<br>
			_criteria = value;<br>
		}		<br>
	}<br>
}</pre><br>
<br>
itemRenderer の外に存在するコントロール (今回の例では、Sliderコントロール) がこのクラスの criteria プロパティに値をセットすることで、itemRenderer に criteria の値を通知できるようになりました。<br>
<br>
■ listData<br>
<br>
itemRenderer は、itemRenderer がセットされたリスト自身の情報と、itemRenderer がどの行と列（DataGrid のように列を持つコンポーネントの時のみ）描画しているかを知ることができます。<br>
この情報が listData です。listData を使うと、次のように BookItemRenderer.mxml を書き直すことができます。<br>
<br>
<pre class="prettyprint">override public function set data( value:Object ) : void<br>
{<br>
    super.data = value;<br>
    var criteria:Number = (listData.owner as MyTileList).criteria;<br>
    if( data.price &lt; criteria ) alpha = 0.4;<br>
    else alpha = 1;<br>
}</pre><br>
<br>
	先に見せた BooktItemRenderer.mxml の &lt;mx:Script&gt; タグ内にこのメソッドを追加してみてください。<br>
<br>
listData プロパティは itemRenderer が属するコントロールへの参照である owner プロパティを持っています。<br>
今回のサンプルでは、owner は TileList を継承した MyTitleList です。 owener プロパティを MyTitleList へキャストすることにより、criteria へアクセス可能になります。<br>
<br>
■ IDropInListItemRenderer<br>
<br>
listData は、IDropInListItemRenderer インターフェイスを実装している itemRenderer にのみ存在します。<br>
残念ながら、コンテナ(HBoxなど) はそのインターフェイスを実装していません。<br>
コントロール (ButtonやLabelなど) はそのインターフェイスを実装していますが、コンテナの場合は自分でそのインターフェイスを実装しなければなりません。<br>
<br>
このインターフェイスの実装方法は簡単で、Flexのドキュメントにも記述されています。<br>
IDropInListItemRenderer を実装した BookItemRenderer クラスを自分で作るには、次のようにします。<br>
<br>
1. IDropInListItemRenderer インターフェイスを実装したクラスを用意する<br>
<br>
<pre class="prettyprint">&lt;mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" ... implements="mx.controls.listClasses.IDropInListItemRenderer"&gt;</pre><br>
<br>
2. listData の set と get メソッドを itemRenderer となるクラスの &lt;mx:Script&gt; タグ内に追加する。<br>
<br>
<pre class="prettyprint">import mx.controls.listClasses.BaseListData;<br>
<br>
private var _listData:BaseListData;<br>
public function get listData() : BaseListData<br>
{<br>
	return _listData;<br>
}<br>
public function set listData( value:BaseListData ) : void<br>
{<br>
	_listData = value;<br>
}</pre><br>
<br>
itemRenderer が IDropInListItemRenderer インターフェイスを実装していれば、list コンポーネントは listData を全ての itemRenderer にセットします。<br>
<br>
■ invalidateList()<br>
<br>
criteriaをクラスに(正常に機能するものとして)含めるのは少々複雑で、単にcriteriaに値を代入しただけではFlex frameworkはその値の変化に気付いてくれません。criteriaの値の変更時には、その変更を伝えるイベントのトリガーが必要です。<br>
次のコードは、set criteria メソッドを変更したものです。<br>
<br>
<pre class="prettyprint">public function set criteria( value:Number ) : void<br>
{<br>
	_criteria = value;<br>
	<br>
	invalidateList();<br>
}</pre><br>
<br>
_criteria へ値をセットした後にinvalidateList()を呼び出しています。<br>
invalidateList()を呼び出すことにより、全てのitemRenderer に dataProvider の値でデータをリセットさせます。リセットすることにより set data メソッドが再度呼ばれます。<br>
これらの処理は次のように表現できます。<br>
<br>
1. itemRenderer は与えられたデータをどのように表示するか判断するために、list オーナー(itemRendererが属しているlistコンポーネント) のcriteriaの値をチェックします。<br>
2. Flex の list クラスを継承した listオーナーには、itemRenderer が読み込むことができる public なプロパティがあり、そのプロパティに外部のコード（他のコントロールやActionScriptコード）が値をセットします。<br>
3. list のプロパティがセット（値が変更)されると、そのlistの invalidateList() メソッドが呼び出されます。それが itemRenderer をリフレッシュさせるトリガーとなり、結果としてdataがリセットされます。 ( そしてステップ１へとまた戻ります。)<br>
<br>
<br>
■ Event<br>
<br>
以前の記事で、itemRenderer とアプリケーションの他の部分のデータをやりとりのために、どのようにイベントをバブリングさせれば良いか示しました。 それはそれで簡単で良いのですが、「itemRendererの役割はデータを表現(表示)すること、コントロールの役割はデータを操作すること」という考えに基づいた、より良い方法があるのではないと考えています。<br>
<br>
MyTileList コントロールの本質は、売り物の本のカタログを表示することです。ユーザーがある本を選んで購入しようとしたときに、それをアプリケーションに伝えるのはlist コントロールの役割であるべきです。これをコードで書くと次のようになります。<br>
<br>
<pre class="prettyprint">&lt;CatalogList bookBuy="addToCart(event)" /&gt;</pre><br>
<br>
現在の状況では、イベントはバブルアップして TileList を通り抜けてしまいます。<br>
イベントをバブリングさせる方法では bookBuy イベントを リスト (TileList) コントロールに関連付けないので、コントロールをアプリケーションの別の場所に移動させられます。<br>
例えば、bookBuy イベントのハンドラをメインの Application に記述すると、( その bookBuy イベントをディスパッチする ) リストコントロールをアプリケーションの別の場所に移動させたいとした時に、ハンドラも共に移動しなければいけません。<br>
逆に、もしイベントをリストコントロールに関連付けていれば、そのコントロールを移動させるだけで済みます。<br>
<br>
Button のクリック・イベントが実はButtonによりディスパッチされるのではなく、Button 内の別の何か他のものによりディスパッチされてバブリングされてくるものだとしたらどうでしょうか。<br>
&lt;mx:Button click="doLogin()" label="Log in" /&gt;とは書けなくなります。 doLogin() メソッドをどこか別の箇所に移動しなければならず実装がややこしくなります。<br>
<br>
おわかりいただけたでしょうか。では、イベントをバブリングさせる方法から listコントロールにイベントをディスパッチさせる方法へと、サンプルを変更する手順を説明します。<br>
<br>
第１に、CatalogList にメタデータを追加して、コンパイラにそのコントロールがイベントをディスパッチすることを知らせます。<br>
<br>
<pre class="prettyprint">import events.BuyBookEvent;<br>
import mx.controls.TileList;<br>
<br>
[Event(name="buyBook",type="events.BuyBookEvent")]<br>
<br>
public class CatalogList extends TileList<br>
{</pre><br>
<br>
第２に、CatalogList にイベントをディスパッチさせるメソッドを追加します。 このメソッドは itemRenderer により呼び出されます。<br>
<br>
Second, add a function to CatalogList to dispatch the event. This function will be called by the itemRenderer instances:<br>
<br>
<pre class="prettyprint">	public function dispatchBuyEvent( item:Object ) : void<br>
	{<br>
		var event:BuyBookEvent = new BuyBookEvent();<br>
		event.bookData = item;<br>
		dispatchEvent( event );<br>
	}<br>
	<br>
}</pre><br>
<br>
第３に、itemRenderer 内の Buy ボタンが上記メソッドを呼び出すようにコードを変更します。<br>
<br>
Third, change the Buy button code in the itemRenderer to invoke the function:<br>
<br>
<pre class="prettyprint">			&lt;mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]"&gt;<br>
				&lt;mx:click&gt;<br>
				&lt;![CDATA[<br>
					(listData.owner as CatalogList).dispatchBuyEvent(data);<br>
				]]&gt;<br>
				&lt;/mx:click&gt;<br>
			&lt;/mx:Button&gt;</pre><br>
<br>
これで itemRenderer 内のボタンは、dataを引数としてlistコントロールのdispatchBuyEvent() メソッドを呼び出すことができるようになりました。<br>
そうすることにより、アプリケーションの他の部分とのデータのやりとりを行うという責務をlistコントロールに移しています。<br>
<br>
今回のサンプルの list コントロールは、data を保持するイベントをディスパッチします。<br>
ActionScriptとMXML（CatalogList.as ファイルで[Event]メタデータで指定されているので）のどちらでも好きな方を使って、アプリケーションはこのイベントに対するイベントリスナをセットすることができます。<br>
[Event]メタデータを使うと、あなたのコードは他の開発者にとって使いやすくなります。<br>
<br>
■ Summary<br>
<br>
itemRenderer は、イベントを用いてアクションを伝達するべきです。 カスタム・イベントを用いることによりデータを受け渡すことができるので、イベントを受け取る側はわざわざ itemRenderer にまでデータを取得しにいく必要がありません。<br>
<br>
itemRenderer は set data メソッドをオーバーライドして、その data の変更に反応するようにしなければいけません。<br>
そのメソッド内では listData.owner から必要なデータにアクセスできます。 また static なクラスや、メインのアプリケーション( Application.application )に保存されたデータにもアクセスできます。<br>
<br>
次回の記事では、itemRenderer の状態について見ていきます。<br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/783299.html">
<title>itemRenderer パート２ : external itemRenderer</title>
<link>http://blog.banana-systems.com/archives/783299.html</link>
<description>itemRendererシリーズのPart1では、inline itemRenderer について説明しました。
inline itemRendererとは、MXML タグと ActionScript のコードが itemRenderer を適用するコンポーネントと同じファイルに存在するものです。

inline itemRenderer は別ファイルで定義され...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-07-16T23:03:18+09:00</dc:date>
<dc:subject>Flex</dc:subject>
<content:encoded><![CDATA[itemRendererシリーズのPart1では、inline itemRenderer について説明しました。<br>
inline itemRendererとは、MXML タグと ActionScript のコードが itemRenderer を適用するコンポーネントと同じファイルに存在するものです。<br>
<br>
inline itemRenderer は別ファイルで定義されたクラスと同等だと説明しましたよね。<br>
実際にFlex コンパイラは内部的に、インラインで書かれたコードを抜き出してクラスを作成しています。 <br>
<br>
今回の記事では、自分でそのクラスを作ってみます。<br>
inline itemRendererの利点は、itemRendererを適用するコンポーネントと同じ個所に記述できるというものですが、itemRendererが複雑になってしまったときに読みづらいという欠点にもなります。<br>
<br>
itemRenderer を外部ファイルに切り出すことには、いくつかの利点があります。<br>
・itemRendererを複数のリストで利用できる<br>
・メンテナンス性に優れる<br>
・Flex Builderのデザイン・ビューで大枠をデザインすることができる<br>
<br>
<br>
■An MXML itemRenderer<br>
<br>
前回の記事では、DataGrid用に少々複雑なitemRendererを紹介しました。<br>
<br>
<pre >&lt;mx:DataGridColumn headerText="Title" dataField="title"&gt;<br>
	&lt;mx:itemRenderer&gt;<br>
		&lt;mx:Component&gt;<br>
			&lt;mx:HBox paddingLeft="2"&gt;<br>
				&lt;mx:Script&gt;<br>
				&lt;![CDATA[<br>
					<span style="color: red;">override public function</span> set data( value:Object ) : void {<br>
						super.data = value;<br>
						<span style="color: red;">var</span> today:Number = (<span style="color: red;">new</span> Date()).time;<br>
						<span style="color: red;">var</span> pubDate:Number = Date.parse(data.date);<br>
						<span style="color: red;">if</span>( pubDate &gt; today ) setStyle("backgroundColor",0xff99ff);<br>
						else setStyle("backgroundColor",0xffffff);<br>
					}<br>
				]]&gt;<br>
				&lt;/mx:Script&gt;<br>
				&lt;mx:Image source="{data.image}" width="50" height="50" scaleContent="<span style="color: red;">true</span>" /&gt;<br>
				&lt;mx:Text width="100%" text="{data.title}" /&gt;<br>
			&lt;/mx:HBox&gt;<br>
		&lt;/mx:Component&gt;<br>
	&lt;/mx:itemRenderer&gt;<br>
&lt;/mx:DataGridColumn&gt;<br>
</pre><br>
<br>
itemRendererの基本はHBoxであり、Image とText を含んでいます。背景色は item レコードの pubDate フィールドの値によって決まります。<br>
これと同じitemRendererを外部ファイルとして書く方法を説明しましょう。<br>
<br>
1. もし Flex Builder を使っているのなら、新規 MXML コンポーネントを作成してください。(コンポーネント名は GridColumnSimpleRenderer とします。好きな名前を付けてもらって結構です。) ルートタグは HBox とします。サイズは気にする必要はありません。<br>
2. もし SDK を使っているのなら、新規 MXML ファイルを作成してください。ファイル名は GridColumnSimpleRenderer.mxml としました。ルートタグは HBox とします。<br>
3. 新規作成したファイルに上記コードの &lt;mx:HBox&gt;タグ内を全てコピーしてください。( &lt;mx:HBox&gt;タグ自体は新規作成したファイルに存在するものなのでコピーする必要はありません。 )<br>
<br>
結果は以下のようになります。<br>
<br>
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
&lt;mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300"&gt;<br>
	&lt;mx:Script&gt;<br>
	&lt;![CDATA[<br>
		<span style="color: red;">override public function</span> set data( value:Object ) : void {<br>
			super.data = value;<br>
			<span style="color: red;">var</span> today:Number = (<span style="color: red;">new</span> Date()).time;<br>
			<span style="color: red;">var</span> pubDate:Number = Date.parse(data.date);<br>
			<span style="color: red;">if</span>( pubDate &gt; today ) setStyle("backgroundColor",0xff99ff);<br>
			else setStyle("backgroundColor",0xffffff);<br>
		}<br>
	]]&gt;<br>
	&lt;/mx:Script&gt;<br>
	&lt;mx:Image source="{data.image}" width="50" height="50" scaleContent="<span style="color: red;">true</span>" /&gt;<br>
	&lt;mx:Text width="100%" text="{data.title}" /&gt;<br>
&lt;/mx:HBox&gt;<br>
</pre><br>
<br>
4. ファイルを保存します。<br>
<br>
DataGridColumn の定義からインライン itemRenderer を削除して、下記コードと入れ替えます。<br>
<br>
<pre class="prettyprint">&lt;mx:DataGridColumn headerText="Title" dataField="title" itemRenderer="GridColumnSimpleRenderer"&gt;<br>
</pre><br>
<br>
アプリケーションを実行してみると、おっと..。DataGridの行の縦サイズが大きすぎますね。<br>
これは itemRenderer の height プロパティに 300 が指定されているからです。<br>
<br>
■ itemRenderer の width と height について<br>
<br>
List コントロールは itemRenderer の width を自動で調節します。従って、今回の例の width="400" という指定は無視されます。<br>
ユーザがカラムやリストの幅を変更するとitemRenderer の幅も変わってしまうのを考慮して、itemRendererを実装してください。<br>
<br>
heightに関しては話が別です。もし List の rowHeight プロパティに明示的に値を指定すると、全ての行がその指定した高さになり、itemRenderer で指定した height は無視されます。<br>
しかし、List の variableRowHeight プロパティに true をセットすると、itemRenderer に指定した height が優先されます。<br>
今回の例では height に 300 をセットしているので、各行の高さは 300 ピクセルになります。<br>
<br>
itemRendererから明示的に値を指定しているheightを削除することにより、正常に表示されるようになります。<br>
<br>
■ Dynamically Changing the itemRenderer<br>
<br>
今回の例では set data メソッドをオーバーライドし itemRenderer の背景色の変更する処理を行っています。<br>
これはよく使う方法です。<br>
set data メソッドをオーバーライドすることにより、新しい行のデータが data プロパティにセットされたタイミングで独自の処理を行わせることができます。(今回の例では、styleを変更しています)<br>
<br>
よくあるミスとして:<br>
<br>
・super.data = value; の記述を忘れてしまう。 これはとても重要です。この記述を忘れてしまうと itemRenderer の挙動がおかしくなってしまいます。<br>
 <br>
・条件に合致しなかったときに styleを戻す処理を忘れてしまう。 今回の例では、pubDate が未来の日付の時だけ背景色を変更し、過去の日付の時に背景色をデフォルトに戻し忘れてしまう。<br>
itemRendererは再利用されるので、必ずデフォルトに戻すといった処理を含めてください。<br>
<br>
<br>
■ ActionScript itemRenderer<br>
<br>
さて、もう一つ itemRenderer を書いてみましょう。今度は ActionScript のクラスを使います。<br>
前回の記事で、次のようなインライン itemRenderer を持つ TileList を使用しました:<br>
<br>
<pre>&lt;mx:itemRenderer&gt;<br>
	&lt;mx:Component&gt;<br>
		&lt;mx:HBox verticalAlign="top"&gt;<br>
			&lt;mx:Image source="{data.image}" /&gt;<br>
			&lt;mx:VBox height="115" verticalAlign="top" verticalGap="0"&gt;<br>
				&lt;mx:Text text="{data.title}" fontWeight="bold" width="100%"/&gt;<br>
				&lt;mx:Spacer height="20" /&gt;<br>
				&lt;mx:Label text="{data.author}" /&gt;<br>
				&lt;mx:Label text="Available {data.date}" /&gt;<br>
				&lt;mx:Spacer height="100%" /&gt;<br>
				&lt;mx:HBox width="100%" horizontalAlign="right"&gt;<br>
					&lt;mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]"&gt;<br>
						&lt;mx:click&gt;<br>
						&lt;![CDATA[<br>
							<span style="color: red;">var</span> e:BuyBookEvent = <span style="color: red;">new</span> BuyBookEvent();<br>
							e.bookData = data;<br>
							dispatchEvent(e);<br>
						]]&gt;<br>
						&lt;/mx:click&gt;<br>
					&lt;/mx:Button&gt;<br>
				&lt;/mx:HBox&gt;<br>
			&lt;/mx:VBox&gt;<br>
		&lt;/mx:HBox&gt;<br>
	&lt;/mx:Component&gt;<br>
&lt;/mx:itemRenderer&gt;<br>
</pre><br>
<br>
この itemRenderer を外部ファイルの ActionScript で記述します。 以下のステップに従って進めてください。<br>
<br>
1. ActionScript クラスの作成. クラス名は BookTileRenderer にし、インラインitemRendererと同様 HBox を継承します。<br>
<br>
<pre>package<br>
{<br>
	<span style="color: red;">import</span> flash.events.MouseEvent;<br>
	<br>
	<span style="color: red;">import</span> mx.containers.HBox;<br>
	<span style="color: red;">import</span> mx.containers.VBox;<br>
	<span style="color: red;">import</span> mx.controls.Button;<br>
	<span style="color: red;">import</span> mx.controls.Image;<br>
	<span style="color: red;">import</span> mx.controls.Label;<br>
	<span style="color: red;">import</span> mx.controls.Spacer;<br>
	<span style="color: red;">import</span> mx.controls.Text;<br>
	<br>
	<span style="color: red;">public class</span> BookTileRenderer <span style="color: red;">extends</span> HBox<br>
	{<br>
		<span style="color: red;">public function</span> BookTileRenderer()<br>
		{<br>
			super();<br>
		}<br>
		<br>
	}<br>
}<br>
</pre><br>
<br>
2. 子コンポーネントへの参照を保持するメンバ変数を定義します。<br>
<br>
<pre><span style="color: red;">private var</span> coverImage:Image;<br>
<span style="color: red;">private var</span> titleText:Text;<br>
<span style="color: red;">private var</span> spacer1:Spacer;<br>
<span style="color: red;">private var</span> authorLabel:Label;<br>
<span style="color: red;">private var</span> pubdateLabel:Label;<br>
<span style="color: red;">private var</span> spacer2:Spacer;<br>
<span style="color: red;">private var</span> buyButton:Button;<br>
</pre><br>
<br>
3. createChildren() メソッドをオーバーライドし、子コンポーネントをインスタンス化しHBoxに追加していきます。<br>
<br>
<pre><span style="color: red;">override protected function</span> createChildren():void<br>
{<br>
	coverImage = <span style="color: red;">new</span> Image();<br>
	addChild(coverImage);<br>
	<br>
	<span style="color: red;">var</span> innerBox:VBox = <span style="color: red;">new</span> VBox();<br>
	innerBox.explicitHeight = 115;<br>
	innerBox.percentWidth = 100;<br>
	innerBox.setStyle("verticalAlign","top");<br>
	innerBox.setStyle("verticalGap", 0);<br>
	addChild(innerBox);<br>
	<br>
		titleText = <span style="color: red;">new</span> Text();<br>
		titleText.setStyle("fontWeight","bold");<br>
		titleText.percentWidth = 100;<br>
		innerBox.addChild(titleText);<br>
	<br>
		spacer1 = <span style="color: red;">new</span> Spacer();<br>
		spacer1.explicitHeight = 20;<br>
		innerBox.addChild(spacer1);<br>
	<br>
		authorLabel = <span style="color: red;">new</span> Label();<br>
		innerBox.addChild(authorLabel);<br>
	<br>
		pubdateLabel = <span style="color: red;">new</span> Label();<br>
		innerBox.addChild(pubdateLabel);<br>
	<br>
		spacer2 = <span style="color: red;">new</span> Spacer();<br>
		spacer2.percentHeight = 100;<br>
		innerBox.addChild(spacer2);<br>
	<br>
		<span style="color: red;">var</span> buttonBox:HBox = <span style="color: red;">new</span> HBox();<br>
		buttonBox.percentWidth = 100;<br>
		buttonBox.setStyle("horizontalAlign","right");<br>
		innerBox.addChild(buttonBox);<br>
	<br>
			buyButton = <span style="color: red;">new</span> Button();<br>
			buyButton.label = "Buy";<br>
			buyButton.setStyle("fillColors",[0x99ff99,0x99ff99]);<br>
			buyButton.addEventListener(MouseEvent.CLICK, handleBuyClick);<br>
			buttonBox.addChild(buyButton);<br>
}<br>
</pre><br>
<br>
親と子の関係が分かりやすくなるように、インデントを入れてみました。 Buyボタンへのクリック・イベントのリスナーの追加も忘れずに行ってください。<br>
<br>
4. commitProperties() メソッドをオーバーライドし、子コンポーネントに data の値をセットします。<br>
<br>
<pre><span style="color: red;">override protected function</span> commitProperties():void<br>
{<br>
	super.commitProperties();<br>
	<br>
	coverImage.source = data.image;<br>
	titleText.text = data.title;<br>
	authorLabel.text = data.author;<br>
	pubdateLabel.text = data.date;<br>
}<br>
</pre><br>
<br>
5. Buyボタンのクリック・イベントのハンドラーを追加します。<br>
<br>
<pre><span style="color: red;">private function</span> handleBuyClick( event:MouseEvent ) : void<br>
{<br>
	<span style="color: red;">var</span> e:BuyBookEvent = <span style="color: red;">new</span> BuyBookEvent();<br>
	e.bookData = data;<br>
	dispatchEvent(e);<br>
}<br>
</pre><br>
<br>
6. メインのアプリケーションの TileList の itemRenderer を、 ActionScript で記述したものに変更します。<br>
inline itemRenderer を削除して、TileList タグ内の itemRenderer プロパティに新たな itemRenderer のクラス名を記述します。<br>
<br>
<br>
<pre class="prettyprint">&lt;mx:TileList id="mylist" x="29" y="542" width="694" itemRenderer="BookTileRenderer" <br>
     dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135" &gt;<br>
</pre><br>
<br>
もしFlexの既存のコンテナクラス（HBoxなど）を基底クラスとするのであれば、私はわざわざ ActionScript を用いてitemRendererを作ろうとはしません。<br>
見て分かる通りMXMLで作成したものと比べてとても複雑になりますし、実のところパフォーマンスもそれほどよくなりませんから。<br>
<br>
<br>
■ 再利用可能な itemRenderer<br>
<br>
次のコードは、CurrencyFormatterを用いて数字を表示する itemRenderer です。ファイル名を PriceFormatter.mxml とします。<br>
<br>
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
&lt;mx:Text xmlns:mx="http://www.adobe.com/2006/mxml"&gt;<br>
<br>
	&lt;mx:Script&gt;<br>
		&lt;![CDATA[<br>
			import mx.controls.dataGridClasses.DataGridListData;<br>
			<br>
			[Bindable] private var formattedValue:String;<br>
			<br>
			override public function set data(value:Object):void<br>
			{<br>
				super.data = value;<br>
				<br>
				<span style="color: red;">formattedValue = cfmt.format( Number(data[(listData as DataGridListData).dataField]) );</span><br>
			}<br>
		]]&gt;<br>
	&lt;/mx:Script&gt;<br>
	<br>
	&lt;mx:CurrencyFormatter precision="2" id="cfmt" /&gt;<br>
	<br>
	&lt;mx:text&gt;{formattedValue}&lt;/mx:text&gt;<br>
	<br>
&lt;/mx:Text&gt;<br>
</pre><br>
<br>
この itemRenderer のポイントは<span style="color: red;">赤字</span>の部分です。ここで、バインダブルな formattedValue 変数をセットしています。<br>
まず、cfmt という id の &lt;mx:CurrentFormatter&gt;タグがあるのが分かると思います（ここも ActionScriptで記述しても構いません）。<br>
上記のコードでは CurrentFormatter の <span style="color: red;">format()</span> メソッドを呼び出した結果を formattedValue にセットしています。<br>
<br>
format() メソッドは引数に<span style="color: red;">Number型</span>のオブジェクトを取るので、引数をNumberにキャストしています。<br>
なぜなら、リストのdataProviderはXMLであり、XMLに含まれるものは全てテキストだからです。<br>
data として Object を使えば、これを数値型の値にすることができますが、Number型にキャストして問題になることはありません。<br>
<br>
ご存じの通り、data とは itemRenderer で表示する値を保持した単なるプロパティです。よって、[ ] 表記を用いてデータのフィールドにアクセスすることも可能です。<br>
例えば、<span style="color: red;">data['price']</span> は金額のカラムです。<br>
しかし、itemRenderer を再利用するには、特定のカラム名を指定するのではなく、もっと汎用的なデータの指定方法が必要となります。<br>
<br>
そこで <span style="color: red;">listData</span> の登場です。IDropInListItemRenderer インターフェイスを実装する全ての Flex コンポーネントは、listData プロパティを持っています。<br>
<br>
Text, Label, Button, CheckBox などほぼ全てのコントールは IDropInListItemRenderer インターフェイスを実装しています。<br>
一方、HBox, Canvas などほぼ全てのコンテナは、IDropInListItemRenderer インターフェイスを実装していません。<br>
もしコンテナクラスを継承した itemRenderer で listData を使いたければ、自前でIDropInListItemRendererを実装しなければなりません。<br>
これについては次回の記事で説明します。<br>
<br>
itemRenderer に与えられた listData には、rowIndex プロパティや、自身(itemRenderer)を保持している DataGrid, List, TileList などのコンポーネントへの参照である owner プロパティが存在します。<br>
<br>
DataGrid で itemRenderer を使用すると、listData は実際には <span style="color: red;">DataGridListData</span> 型のオブジェクトになります。<br>
DataGridListData には、columnIndex プロパティや カラム (DataGridColumn) の名前を表す <span style="color: red;">dataFiled</span> プロパティなどもあります。<br>
<br>
先ほどの行を、内側から一つ一つ説明していきましょう。<br>
<br>
* <span style="color: red;">listData as DataGridListData</span> - listData を DataGridListData にキャストすることにより dataField プロパティにアクセスできるようにします。<br>
* <span style="color: red;">.dataField</span> - そのカラムに表示されるフィールドです。 これを用いることにより itemRenderer を汎用的にできます。 dataField を用いることで、この itemRenderer を複数のカラムに使用できます。 今回の例では、dataField プロパティの値は 'price' です。<br>
* <span style="color: red;">data[ ... ]</span> - 与えられたレコードの特定のフィールドにアクセスします。今回の例では、price カラムです。<br>
* <span style="color: red;">Number( ... )</span> - format() メソッドはNumber型の引数を取るので、Number型へのキャストを行っています。<br>
* <span style="color: red;">cfmt.format( ... )</span> - 引数に与えられた値を通貨の表記に変換しています。<br>
<br>
■ Summary<br>
<br>
itemRenderer を実装する方法(inline, external)を説明しましたが、自分が実装しやすい方法で良いと思います。<br>
ActionScriptだけを使う人もいます。FlexとActionScriptの経験を積めば、この方法がうまく行くでしょう。<br>
MXML はシンプルな itemRenderer を手早く仕上げるのに向いています.<br>
<br>
今後は UIComponentを継承した ActionScript クラスとして記述したもっと効率的なitemRendererを作成していきます。<br>
次回の記事では、itemRenderer とアプリケーションの他の部分とのデータのやりとりについて説明します。<br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/758255.html">
<title>itemRenderer パート１ : inline itemRenderer</title>
<link>http://blog.banana-systems.com/archives/758255.html</link>
<description>Flex / AIR のリスト系コンポーネント(List, DataGrid, TileListなど)はデフォルトのままでも各アイテム(行)を十分を奇麗に表示してくれるのですが、プロジェクトによってはデフォルトのままの表現では要求を満たさなかったり、ボタンを追加する必要がでてきたり等問題が発生...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-07-06T17:17:31+09:00</dc:date>
<dc:subject>Flex</dc:subject>
<content:encoded><![CDATA[Flex / AIR のリスト系コンポーネント(List, DataGrid, TileListなど)はデフォルトのままでも各アイテム(行)を十分を奇麗に表示してくれるのですが、プロジェクトによってはデフォルトのままの表現では要求を満たさなかったり、ボタンを追加する必要がでてきたり等問題が発生します。<br>
<br>
そこで、今のうちにしっかりとマスターしておこうということで Adobe Customer Care の Peter Ent さんのブログである itemRenderer シリーズを翻訳してみました。<br>
今回は itemRenderer パート１ : inline itemRenderer のみの翻訳となっていますが、これから週に１、２回のペースで順次追加していきます。<br>
<br>
○ 原文 itemRenderers: Part 1: inline renderers<br>
<a href="http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p.html">http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p.html</a><br>
<br>
■ Renderer の再利用<br>
<br>
サーバーから受け取ったデータの値に基づき DataGrid コンポーネントの5行目Ｘ4列目のセルを緑色に変更する時などに、外部から itemRenderer にアクセスしようとしているのをよく見かけます。<br>
しかし、外部から itemRenderer を取得し、それらを変更するのは Flex フレームワークとFlex コンポーネント・モデルに大きく違反しています。<br>
<br>
itemRenderer を理解するには、まずそれが何なのか、また私たち Adobe Flex エンジニアリング・チームが何を意図してそれをデザインしたのかを理解する必要があります。<br>
1000個レコードを見せたい時に Listコンポーネントが 1000個の itemRenderer を作成すると考えるのは誤りです。<br>
<br>
もし Listコンポーネントが10行表示するとしたのなら、約12個の itemRenderer が作成されます。<br>
その表示される10行分のitemRendererが10個と、バッファリング及びパフォーマンスのために2、3個作られます。<br>
Listコンポーネントは初期時に1～10行目を表示しますが、List をスクロールし3～12行目が表示されたとしても、元々の同じ12個の itemRenderer が用いられます。<br>
スクロールしたとしても新たな itemRenderer が作成されることはありません。<br>
<br>
Listコンポーネントがスクロールされると、3～10行目を表示しているitemRendererは元のデータを表示し続け、位置だけが上へ移動します。<br>
1行目と2行目を表示していた itemRenderer は、下へ移動し11行目と12行目を表示します。<br>
Listのサイズを変更しない限り、itemRendererは新しいデータを表示するために位置移動するだけでインスタンスは再利用されます。<br>
<br>
<br>
<blockquote><br>
5行目Ｘ4列目のセルの背景色を変更する際、既にユーザが Listをスクロールしてしまっていて、そのセルの itemRenderer が21行目のデータを表示しているかもしれないので注意してください。<br>
</blockquote><br>
<br>
では、どのようにすればよいのでしょうか？<br>
<br>
itemRenderer は、受け取ったデータの値に基づいて自身を変更しなければいけません。<br>
<br>
もしListのitemRendererがデータの値に基づいて色を変えるものであれば、そのitemRendererは自身が受け取ったデータを精査し、自分自身を変更しなければいけません。<br>
<br>
<br>
■ inline itemRenderer ( インライン アイテムレンダラ )<br>
<br>
今回の記事では、inline itemRendererを用いて上記の問題を解決していきます。<br>
inline itemRendererとは、MXMLファイル内のitemRendererを適応するコンポーネントの箇所に直接記述されたものです。<br>
次回の記事では、external itemRendererを用います。<br>
<br>
inline itemRendererはシンプルなものなので、一般的に簡単なレンダラーを作成する時、又は大きいアプリケーションのプロトタイピング時に用いられます。<br>
<br>
inline itemRendererを用いることに何も問題は無いのですが、アプリケーションのコードが複雑になってきた時には一つのクラスとして独立させるほうが好ましいです。<br>
<br>
今回の記事では全ての例にて同じデータを用います。データはbookを表し、author, title, publication date, thumbnail image等の値が存在します。<br>
各レコードはXMLノードであり、下記のようなものとなります。<br>
<pre class="prettyprint">&lt;book&gt;<br>
    &lt;author&gt;Peter F. Hamilton&lt;/author&gt;<br>
    &lt;title&gt;Pandora's Star&lt;/title&gt;<br>
    &lt;image&gt;assets/pandoras_star_.jpg&lt;/image&gt;<br>
    &lt;date&gt;Dec 3, 2004&lt;/date&gt;<br>
&lt;/book&gt;<br>
</pre><br>
<br>
それでは &lt;mx:List&gt; コントロールの簡単なitemRendererを作ってみます。<br>
表示されるものはauthor、続いてtitleとなります。<br>
<pre class="prettyprint">&lt;mx:List x="29" y="67" dataProvider="{testData.book}" width="286" height="190"&gt;<br>
    &lt;mx:itemRenderer&gt;<br>
        &lt;mx:Component&gt;<br>
            &lt;mx:Label text="{data.author}: {data.title}" /&gt;<br>
        &lt;/mx:Component&gt;<br>
    &lt;/mx:itemRenderer&gt;<br>
&lt;/mx:List&gt;<br>
</pre><br>
<br>
この itemRenderer はとてもシンプルなので labelFunctionを用いたほうが便利かもしれませんが、重要な点も示しています。<br>
まず一つ目は、inlineRenderer を定義するために &lt;mx:itemRenderer&gt; タグを用いていること。<br>
そして、そのタグ内に &lt;mx:Component&gt; タグが用いられていること。<br>
この &lt;mx:Component&gt; タグは、Flex コンパイラにコンポーネントをインラインにて定義することを知らせます。<br>
そのタグの意味を少し説明しておきます。<br>
<br>
itemRendererは&lt;mx:Component&gt; タグ内に定義します。上の例では、&lt;mx:Label&gt; コントロールを１つ用いて、そのコントロールの<br>
textフィールドにデータ・バインディング {data.author}: {data.title} にて値をセットしています。<strong>これはとても重要です。</strong> List コントロールは、itemRenderer の data プロパティに値をセットすることにより、各 itemRenderer のインスタンスに dataProvider のレコード(各行)を渡すのです。<br>
上記コードでは、Listの全ての行にて、inline itemRenderer のインスタンスが data プロパティを持ち、そのプロパティに &lt;book&gt; XMLノードがセットされます。<br>
List コントロールをスクロール度に、新しく表示される行のために itemRenderer が再利用されて data プロパティの値が変化します。<br>
<br>
別の言い方をすると、1行目の itemRenderer のインスタンスが data.author に"Peter F, Hamilton"がセットされているとしても、表示外にスクロールされると<br>
そのインスタンスは再利用されて "J.K, Rowling" がセットされるかもしれません。 これらは全て自動で処理されるので心配する必要はありません。<br>
<br>
次の例は、&lt;mx:List&gt; コントロールを用いた少々複雑なinline itemRenderer です。<br>
<br>
<pre>&lt;mx:List x="372" y="67" width="351" height="190" variableRowHeight="<span style="color: red;">true</span>" dataProvider="{testData.book}"&gt;<br>
	&lt;mx:itemRenderer&gt;<br>
		&lt;mx:Component&gt;<br>
			&lt;mx:HBox &gt;<br>
				&lt;mx:Image source="{data.image}" width="50" height="50" scaleContent="<span style="color: red;">true</span>" /&gt;<br>
				&lt;mx:Label text="{data.author}" width="125" /&gt;<br>
				&lt;mx:Text  text="{data.title}" width="100%" /&gt;<br>
			&lt;/mx:HBox&gt;<br>
		&lt;/mx:Component&gt;<br>
	&lt;/mx:itemRenderer&gt;<br>
&lt;/mx:List&gt;<br>
</pre><br>
<br>
先のコードの &lt;mx:Label&gt; が &lt;mx:HBox&gt;と&lt;mx:Image&gt;、&lt;mx:Label&gt;、&lt;mx:Text&gt;に変わった以外あまり違いはありません。<br>
レコードとのデータ・バインディングも有効です。<br>
<br>
■DataGrid<br>
<br>
DataGrid にも inline itemRenderer が使用可能です。下記コードは DataGridColumn に用いた例です。<br>
<br>
<pre>&lt;mx:DataGrid x="29" y="303" width="694" height="190" dataProvider="{testData.book}" variableRowHeight="<span style="color: red;">true</span>"&gt;<br>
	&lt;mx:columns&gt;<br>
		&lt;mx:DataGridColumn headerText="Pub Date" dataField="date" width="85" /&gt;<br>
		&lt;mx:DataGridColumn headerText="Author" dataField="author" width="125"/&gt;<br>
		&lt;mx:DataGridColumn headerText="Title" dataField="title"&gt;<br>
			&lt;mx:itemRenderer&gt;<br>
				&lt;mx:Component&gt;<br>
					&lt;mx:HBox paddingLeft="2"&gt;<br>
						&lt;mx:Script&gt;<br>
						&lt;![CDATA[<br>
							<span style="color: red;">override public function</span> set data( value:Object ) : void {<br>
								super.data = value;<br>
								<span style="color: red;">var</span> today:Number = (<span style="color: red;">new</span> Date()).time;<br>
								<span style="color: red;">var</span> pubDate:Number = Date.parse(data.date);<br>
								<span style="color: red;">if</span>( pubDate &gt; today ) setStyle("backgroundColor",0xff99ff);<br>
								else setStyle("backgroundColor",0xffffff);<br>
							}<br>
						]]&gt;<br>
						&lt;/mx:Script&gt;<br>
						&lt;mx:Image source="{data.image}" width="50" height="50" scaleContent="<span style="color: red;">true</span>" /&gt;<br>
						&lt;mx:Text width="100%" text="{data.title}" /&gt;<br>
					&lt;/mx:HBox&gt;<br>
				&lt;/mx:Component&gt;<br>
			&lt;/mx:itemRenderer&gt;<br>
		&lt;/mx:DataGridColumn&gt;<br>
	&lt;/mx:columns&gt;<br>
&lt;/mx:DataGrid&gt;<br>
</pre><br>
<br>
先の２つの例よりもかなり複雑ですが、itemRenderer を適応するタグの中で &lt;mx:itemRenderer&gt;と&lt;mx:Component&gt; が 定義されているという点では同じ構成になっているのが分かると思います。<br>
<br>
&lt;mx:Component&gt;の目的は、MXML内で ActionScript のクラスを作成するためです。<br>
&lt;mx:Component&gt; タグ内のものを別のファイルにコピーしてクラス名を付けるのをイメージしてみてください。<br>
inline itemRenderer は完全な MXML ファイルのようですよね。 ルートタグ ( 上記コードでは&lt;mx:HBox&gt; ) があり、&lt;mx:Script&gt; タグもあります。<br>
<br>
上記コードの &lt;mx:Script&gt; タグの目的は、data プロパティの set 関数をオーバーライドすることにより、itemRenderer の背景色を変えるためです。<br>
今回の例では、発行日(pubDate)が未来の場合に背景色を白(0xffffff)から紫(0xff99ff)にします。 <br>
<strong>itemRenderer は再利用されるので、発行日が過去の場合には白に戻す必要があります。<br>
この色を戻すという処理を行わなければ、ユーザがListを何度もスクロールしていると最終的に全ての itemRenderer の背景色が紫になってしまいます。</strong><br>
<br>
■ outerDocument<br>
<br>
スコープも変わります。 何を意味しているかというと、&lt;mx:Component&gt; タグ内で定義した変数のスコープは、そのコンポーネント/inline itemRenderer 内のみとなります。<br>
同じように &lt;mx:Component&gt; タグの外のものは別ファイルにて定義されたコンポーネントのように別スコープとなります。<br>
例えば、クリックすると本を購入できる Button を itemRenderer に追加するとします。<br>
クリックイベントに反応し購入処理を行うメソッドをコールする Button は、下記のように定義できます。<br>
<br>
<pre class="prettyprint">&lt;mx:Button label="Buy" click="buyBook(data)" /&gt;<br>
</pre><br>
<br>
もし buyButton() メソッドがファイルの &lt;mx:Script&gt; タグ内に定義されていると「buyBook() は定義されていないメソッドです」という旨のエラーが発生します。<br>
これは butyBook() が &lt;mx:Component&gt; 内のスコープではなく、ファイルのスコープにて定義されているからです。これはよくあるケースなので outerDocument 識別子<br>
を用いて回避してみます。<br>
<br>
<pre class="prettyprint">&lt;mx:Button label="Buy" click="outerDocument.buyBook(data)" /&gt;<br>
</pre><br>
<br>
outerDocument 識別子を用いてファイルもしくは外側のドキュメントを参照できます。<br>
注意事項として、outerDocumentを用いてアクセスするメソッドは protected や private ではなく、public でなければいけません。<br>
&lt;mx:Component&gt; は外部で定義されたクラスのように扱われるのを思い出してください。<br>
<br>
<br>
■ Bubbling Events<br>
<br>
更に複雑な例を見ていきましょう。下記コードは、同じデータを TileList を用いて表示します。<br>
<br>
<pre >&lt;mx:TileList x="29" y="542" width="694" dataProvider="{testData.book}" height="232" columnWidth="275" rowHeight="135" &gt;<br>
	&lt;mx:itemRenderer&gt;<br>
		&lt;mx:Component&gt;<br>
			&lt;mx:HBox verticalAlign="top"&gt;<br>
				&lt;mx:Image source="{data.image}" /&gt;<br>
				&lt;mx:VBox height="115" verticalAlign="top" verticalGap="0"&gt;<br>
					&lt;mx:Text text="{data.title}" fontWeight="bold" width="100%"/&gt;<br>
					&lt;mx:Spacer height="20" /&gt;<br>
					&lt;mx:Label text="{data.author}" /&gt;<br>
					&lt;mx:Label text="Available {data.date}" /&gt;<br>
					&lt;mx:Spacer height="100%" /&gt;<br>
					&lt;mx:HBox width="100%" horizontalAlign="right"&gt;<br>
						&lt;mx:Button label="Buy" fillColors="[0x99ff99,0x99ff99]"&gt;<br>
							&lt;mx:click&gt;<br>
							&lt;![CDATA[<br>
								<span style="color: red;">var</span> e:BuyBookEvent = <span style="color: red;">new BuyBookEvent();</span><br>
								e.bookData = data;<br>
								dispatchEvent(e);<br>
							]]&gt;<br>
							&lt;/mx:click&gt;<br>
						&lt;/mx:Button&gt;<br>
					&lt;/mx:HBox&gt;<br>
				&lt;/mx:VBox&gt;<br>
			&lt;/mx:HBox&gt;<br>
		&lt;/mx:Component&gt;<br>
	&lt;/mx:itemRenderer&gt;<br>
&lt;/mx:TileList&gt;<br>
</pre><br>
<br>
実行すると itemRenderer は下のように表示されます。<br>
<br>
<img src="http://image.blog.livedoor.jp/banana_systems/imgs/6/b/6b85d3ee.jpg" width="272" height="130" border="0" alt="TileListItemRenderer" hspace="5" class="pict" align="left"  /><br>
<br>
<br /><br /><br /><br /><br>
<br>
DataGrid で用いた itemRenderer と似ていますが、Button のクリック・イベントの発生時に outerDocument を通じて buyBook() メソッドを呼び出していません。<br>
今回の例では、クリック・イベントの発生時にカスタムのイベントをディスパッチしています。<br>
そのイベントは<strong>バブルアップ</strong>し、itemRenderer を抜け TileList を通り上位のヴィジュアル・コンポーネントによって受け取られます。<br>
<br>
<blockquote><br>
itemRenderer にButton, LinkButton 等のインタラクティブなコントロールを含ませることがよくあります。行を削除したり、今回のケースでは本の購入などクリックした時に何か処理を行わせるといった具合にです。<br>
</blockquote><br>
<br>
しかし itemRenderer に何か処理を行わせるのは賢明ではありません。itemRenderer の本来の役割は、見た目を良くすることだからです。<br>
イベントをバブリングさせることで itemRenderer は別のものに処理を渡すことが可能になります。<br>
ここでカスタム・イベントが有用となります。何故ならイベントは各行の data に紐付いているので、そのイベントに data を含めてディスパッチすればいいからです。<br>
そうすることにより、イベントの受け取る側は data を取得するために、わざわざ data を探しにいく必要がなくなるからです。<br>
<br>
■Summary<br>
<br>
inline itemRenderer を用いることにより List の見た目を簡単に変更できます。<br>
<br>
inline itemRenderer は別ファイルで定義された ActionScript のクラスのようなものとして扱ってください。 スコープも別ファイルで定義された ActionScript のクラスと同等となります。<br>
itemRenderer のインタラクションの結果としてデータのやりとりが必要であれば、カスタムイベントを使ってください。<br>
<br>
<strong>重要 : </strong>itemRenderer は再利用されるものなので保持しようせずに、各 itemRenderer に与えられたデータのみを扱うようにしてください。<br>
<br>
次回は external itemRenderer を見ていきます。<br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/331379.html">
<title>[Flex] Flex &amp; BlazeDS &amp; Google Maps インテグレーション</title>
<link>http://blog.banana-systems.com/archives/331379.html</link>
<description>先週の Flex User Group にて発表させて頂きましたFlex、BlazeDS と Google Mapsを組み合わせたアプリを公開いたしましたのでご報告させて頂きます。

アプリケーションということで MyFlight と命名いたしました。

■ MyFlight
http://www.banana-systems.com/MyFligh...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-04-02T17:10:14+09:00</dc:date>
<dc:subject>Flex</dc:subject>
<content:encoded><![CDATA[先週の Flex User Group にて発表させて頂きましたFlex、BlazeDS と Google Mapsを組み合わせたアプリを公開いたしましたのでご報告させて頂きます。<br>
<br>
アプリケーションということで MyFlight と命名いたしました。<br>
<br>
■ MyFlight<br>
<a href="http://www.banana-systems.com/MyFlight/view/Flight.html">http://www.banana-systems.com/MyFlight/view/Flight.html</a><br>
<br>
ぜひ、ご自分の飛行機を追加してみてください。<br>
<br>
BlazeDS の Messaging 機能にて各飛行機の緯度経度座標を client へ随時送信します。<br>
RemoteObject 機能を用いているのは、<br>
１．飛行中の飛行機をクリックした時のポップアップ・ウィンドウ内に表示するデータをサーバーから取得する際<br>
２．flight history (フライト履歴) を AdvancedDataGrid に表示する際<br>
３．空港の検索の際<br>
となっております。<br>
<br>
勉強会の際には Flex と BlazeDS の通信には Http Streaming を用いていましたが、公開したアプリでは Client Polling に変更しました。<br>
<br>
尚、BlazeDS の Http Streaming を用いた data push のデモビデオをYouTubeにアップさせて頂きました。<br>
<br>
■ ロンドン ヒースロー 空港から同時に500機の飛行機を飛ばすデモ<br>
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/yvtX-yzrRRU"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/yvtX-yzrRRU" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object><br>
<br>
■ ランダムで選択した空港から同時に500機の飛行機を飛ばすデモ<br>
<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/blL7SID28i8"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/blL7SID28i8" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object><br>
<br>
<br>
<br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/238811.html">
<title>[Flex/AIR] itemRenderer の使い方</title>
<link>http://blog.banana-systems.com/archives/238811.html</link>
<description>　
MUST READ THESE !!

Flex、又は AIR において Visible Componentを作成される方は必須です。　言いきります。

この辺りの詳細はflexのドキュメントにもあまり書かれていないので重宝すると思います。

Flex標準のVisible Componentは renderer をキャッシュしま...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-03-21T03:44:58+09:00</dc:date>
<dc:subject></dc:subject>
<content:encoded><![CDATA[　<br>
<b>MUST READ THESE !!</b><br>
<br>
Flex、又は AIR において Visible Componentを作成される方は必須です。　言いきります。<br>
<br>
この辺りの詳細はflexのドキュメントにもあまり書かれていないので重宝すると思います。<br>
<br>
Flex標準のVisible Componentは renderer をキャッシュしまくるので自作の itemRenderer を使おうとしてハマった方も多いのではないでしょうか。<br>
<br>
<a href="http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p.cfm">itemRenderers: Part 1: inline renderers</a><br>
<br>
<a href="http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p_1.cfm">itemRenderers: Part 2: External renderers</a><br>
<br>
<a href="http://weblogs.macromedia.com/pent/archives/2008/03/itemrenderers_p_2.cfm">itemRenderers: Part 3: Communication</a><br>
<br>
<br>
<br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/228744.html">
<title>[Flex/AIR] 画面に収まらないくらい大きなチャートのスクロールについて</title>
<link>http://blog.banana-systems.com/archives/228744.html</link>
<description>　
大きいチャートだとスクロールすると縦軸(又は横軸)が画面から消えてしまいシリーズの各値が何なのか分かり難くなるのですが、これだと縦軸(又は横軸)は常に表示されたままなので見やすいですね。

■ スクロールによってX軸が隠れる


■ スクロールしてもX軸が表...</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-03-20T01:23:40+09:00</dc:date>
<dc:subject>Flex/AIR News</dc:subject>
<content:encoded><![CDATA[　<br>
大きいチャートだとスクロールすると縦軸(又は横軸)が画面から消えてしまいシリーズの各値が何なのか分かり難くなるのですが、これだと縦軸(又は横軸)は常に表示されたままなので見やすいですね。<br>
<br>
■ スクロールによってX軸が隠れる<br>
<img src="http://image.blog.livedoor.jp/banana_systems/imgs/2/a/2a14f78d.gif" width="382" height="578" border="0" alt="チャート スクロールによって縦軸が隠れる" hspace="5" class="pict" /><br>
<br>
■ スクロールしてもX軸が表示され続ける<br>
<img src="http://image.blog.livedoor.jp/banana_systems/imgs/c/e/ceb75df5.gif" width="382" height="566" border="0" alt="チャート スクロールにされても縦軸が隠れない" hspace="5" class="pict" /><br>
<br>
<br>
こういうのって結構重要。<br>
<br>
<br>
<a href="http://www.connectedpixel.com/blog/scrollingcharts">Big Data Scrolling Flex Charts</a><br>
]]>
</content:encoded>
</item>
<item rdf:about="http://blog.banana-systems.com/archives/221512.html">
<title>[Flex/AIR] Chart にて選択した Legend (凡例) の Series のみ表示する</title>
<link>http://blog.banana-systems.com/archives/221512.html</link>
<description>　
下手に難しく実装してしまいそうなのですが、これはシンプルでいいですね。

 Interactive Charting Legend to select which series to display

</description>
<dc:creator>banana_systems</dc:creator>
<dc:date>2008-03-19T00:46:57+09:00</dc:date>
<dc:subject>Flex/AIR News</dc:subject>
<content:encoded><![CDATA[　<br>
下手に難しく実装してしまいそうなのですが、これはシンプルでいいですね。<br>
<br>
<a href="http://blog.flexmonkeypatches.com/2008/03/10/interactive-charting-legend-to-select-which-series-to-display/"> Interactive Charting Legend to select which series to display</a><br>
<br>
]]>
</content:encoded>
</item>

</rdf:RDF>