Rubotoを使い、AndroidのActionBar上にFragmentを使ったTabを作る

RubotoでActionBar上にTabが表示されるかを試してみたときのメモ。

■環境

Platform JDK ant Ruby ruboto jruby-jars Device API level
Windows7 x64 1.7.0_25 1.9.1 RubyInstaller 1.9.3-p448 0.13.0 1.7.4 Nexus7 2012 android-17

■注意

前回 同様、初回起動時にActionBar上にあるタブがうまく動作しません。



■アプリの動き

  • ActionBar上に2つのタブが表示される
    • 「TAB1」をタップすると、「Fragment1」が表示される
    • 「TAB2」をタップすると、「Fragment2」が表示される
    • 同じタブをタップすると、「tab reselected」とtoast表示される

■アプリの生成

以下のコマンドでJRubyを含めて生成します。また、前回同様、メモリサイズの変更などを行なっておきます。

ruboto gen app --package com.example.tab --target android-17 --with-jruby

■レイアウト用xmlの作成

メイン画面のレイアウト

tab\res\layout\main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" android:layout_gravity="center">

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </LinearLayout>
</LinearLayout>
TAB1を選択した時に表示される、Fragment1のレイアウト

tab\res\layout\fragment1.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView android:id="@+id/textView1"
        android:text="Fragment A"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
TAB2を選択した時に表示される、Fragment2のレイアウト

tab\res\layout\fragment2.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView android:id="@+id/textView2"
        android:text="Fragment B"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

■Activityの修正

参考にしたサイト

以下のJavaでの実装例を参考にして、Rubyのコードを書きました。


Web上では、fragmentを Fragment.instantiate というメソッドで生成する例をよく見かけました。
ただ、Rubotoでやろうとすると第二引数の指定がうまくいきませんでした(以下が試してみてうまくいかなかった例)。

JRubyからJavaのクラス継承

以下のように、「<」を付ければ良いようです。
参考:現実世界のJRuby

class Fragment1 < Fragment
end
Fragmentの onCreateView メソッド名

Activityの on_create のように、メソッド名をスネークケースにしたところ、エラーで動作しませんでした。
Javaと同じようにキャメルケースとしたところ、問題なく動作しました。
参考:JRubyで継承元のメソッドをオーバーライドする場合は、メソッド名をアンスコ区切りに変えてはいけない - k-yamadaのブログ



ソース
require 'ruboto/widget'
require 'ruboto/util/toast'

java_import 'android.app.ActionBar'
java_import 'android.app.Fragment'


class TabActivity
  def onCreate(bundle)
    super
    self.setContentView(Ruboto::R::layout::main)

    ab = getActionBar()
    ab.setNavigationMode(ActionBar::NAVIGATION_MODE_TABS)
    ab.addTab(ab.newTab().setText("tab1").setTabListener(TabListener.new(self, Fragment1.new())))
    ab.addTab(ab.newTab().setText("tab2").setTabListener(TabListener.new(self, Fragment2.new())))
  end
end


class TabListener
  # toastを使うため、activityを渡している
  def initialize(activity, fragment)
    @activity = activity
    @fragment = fragment
  end

  def onTabReselected(tab, fragment_transaction)
    @activity.toast "tab reselected"
  end

  def onTabSelected(tab, fragment_transaction)
    fragment_transaction.replace(Ruboto::R::id::fragment_container, @fragment)
  end

  def onTabUnselected(tab, fragment_transaction)
    fragment_transaction.remove(@fragment)
  end
end


class Fragment1 < Fragment
  def onCreateView(inflater, container, bundle)
    return inflater.inflate(Ruboto::R::layout::fragment1, container, false)
  end
end

class Fragment2 < Fragment
  def onCreateView(inflater, container, bundle)
    return inflater.inflate(Ruboto::R::layout::fragment2, container, false)
  end
end

スクリーンショット

  • 初回起動時

  • 二回目起動時

  • TAB2選択時

  • 同じタブをタップした時



■ソース

上記のxmlとrbファイルをGistに上げました。
RubotoでActoinBar上にFragmentを使ったTabを作るサンプル



■参考