Rubotoでウィジェット(View)を使おうとしたら、
1 | ruboto_import_widgets :LinearLayout, :ListView, :TextView |
とかをする必要がある。まぁ何をやっているのかはわかるが、実際にどういう処理をやっているのかを読んでみようと思って、ruboto/widget.rbを開いてみた。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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を見てみよう。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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を使ってみたので、載せておく。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | 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は。