Rubotoでウィジェット(View)を使おうとしたら、
ruboto_import_widgets :LinearLayout, :ListView, :TextView
とかをする必要がある。まぁ何をやっているのかはわかるが、実際にどういう処理をやっているのかを読んでみようと思って、ruboto/widget.rbを開いてみた。
def ruboto_import_widget(class_name, package_name="android.widget") klass = java_import("#{package_name}.#{class_name}") || eval("Java::#{package_name}.#{class_name}") return unless klass RubotoActivity.class_eval " def #{(class_name.to_s.gsub(/([A-Z])/) { '_' + $1.downcase })[1..-1]}(params={}) if force_style = params.delete(:default_style) rv = #{class_name}.new(self, nil, force_style) elsif api_key = params.delete(:apiKey) rv = #{class_name}.new(self, api_key) else rv = #{class_name}.new(self) end if parent = (params.delete(:parent) || @view_parent) parent.addView(rv, (params.delete(:parent_index) || parent.child_count)) end rv.configure self, params return rv unless block_given? old_view_parent, @view_parent = @view_parent, rv yield @view_parent = old_view_parent rv end " setup_list_view if class_name == :ListView setup_spinner if class_name == :Spinner setup_button if class_name == :Button setup_image_button if class_name == :ImageButton setup_linear_layout if class_name == :LinearLayout setup_relative_layout if class_name == :RelativeLayout end
メタプログラミングRubyを読んでいたおかげで意味がわかる!!
まず最初の行で、クラスのインポートを行っている。3行目でクラスがなければリターン。
クラスがあった場合、RubotoActivityをオープンしてウィジェット名のメソッドを定義している。:ListViewを渡したら、RubotoActivity.list_viewメソッドができるわけだ。ブロックを渡しているとなんか処理してくれるっぽい。
:ListViewがある場合、setup_list_viewメソッドが実行される。
では、setup_list_viewを見てみよう。
def setup_list_view Java::android.widget.ListView.class_eval do attr_reader :adapter, :adapter_list def configure(context, params = {}) if params.has_key? :list @adapter_list = Java::java.util.ArrayList.new @adapter_list.addAll(params[:list]) item_layout = params.delete(:item_layout) || R::layout::simple_list_item_1 @adapter = Java::android.widget.ArrayAdapter.new(context, item_layout, @adapter_list) setAdapter @adapter params.delete :list end if params.has_key? :adapter @adapter = params[:adapter] end super(context, params) end def reload_list(list) @adapter_list.clear @adapter_list.addAll(list) @adapter.notifyDataSetChanged invalidate end end end
これもまた、ListViewクラスをオープンしてメソッドを定義している。メタプログラミング恐るべし。
ListView.configureメソッドとListView.reload_listメソッドを定義して、ListViewを使いやすくしているようである。
チュートリアルにあったソースコードを元に、ListViewを使ってみたので、載せておく。
ruboto_import_widgets :LinearLayout, :TextView, :ListView $activity.start_ruboto_activity "$sample_activity" do setTitle 'This is the Title' def on_create(bundle) self.content_view = linear_layout(:orientation => :vertical) do @list = %w(aaa bbb ccc ddd eee fff ggg) @list_view = list_view(:list => @list, :on_item_click_listener => proc{|av, v, pos, item_id| toast(@list[pos]) }) end end end
:ListViewを読み込んでいるため、RubotoActivity内ではlist_viewメソッドが使える。
パラメータとして:list => @listを渡しているが、:item_layoutを省略しているので、R::layout::simple_list_item_1というListViewでは最もシンプルなViewが自動的に使われ、配列がArrayAdapterに渡されてListViewが表示される。
:on_item_click_listenerを無名関数(proc)で定義できるみたいだった。このソースでは、ListViewの押された行の文字列をトーストで表示するという処理をしている。非常に簡単な処理だが、これを調べるだけでもひと苦労するのである。コードは短いけど開発環境がいいのがないのかなぁ、Rubotoは。