Scala で Android アプリ開発(準備編)
備忘という名の引き継ぎ的なアレ。Mac OSX 10.7 上で開発する事を前提とする。
sbt の準備
この辺りを見ながら sbt-launch.jar をダウンロードし、/usr/local/bin 配下に sbt-launch-0.11.3.jar という名前で保存する。
このままだと使い難いので、下記の Shell Script を用意する。
$ cat /usr/local/bin/sbt java -Dhttp.proxyHost=XXX.XXX.XXX.XXX -Dhttp.proxyPort=XXXX -Xmx512M -Dfile.encoding=UTF-8 -jar `dirname $0`/sbt-launch-0.11.3.jar "$@"
パラメータ | 説明 |
---|---|
-Dhttp.proxyHost=XXX.XXX.XXX.XXX -Dhttp.proxyPort=XXXX | Proxy 設定。Proxy を経由せずに外部と http/https 接続できるのであれば不要 |
-Xmx512M | 最大ヒープサイズ。環境に合わせて適度な値を… |
-Dfile.encoding=UTF-8 | これを入れないと sbt console 上で日本語が使えない |
giter8 の準備
まず cs コマンドをインストールする。
$ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
~/bin 配下にコマンドがインストールされるので ~/bin に PATH を通しておく。
次に g8 コマンドをインストールする。
$ cs n8han/giter8
Android SDK の準備
Android SDK | Android Developers から Mac OS X (intel) の android-sdk_r18-macosx.zip をダウンロードし、unzip で /usr/local 配下に伸張する。すると /usr/local/android-sdk-macosx が作成されるので下記の環境変数を設定する。
$ export ANDROID_SDK_HOME=/usr/local/android-sdk-macosx
zsh を利用しているなら .zshenv に上記を追加しておく。
また /usr/local/android-sdk-macosx/tools も作成されているので PATH を通しておく。
$ export PATH=$PATH:/usr/local/android-sdk-macosx/tools
zsh を利用しているなら .zshenv に下記を追加する。
path=(/usr/local/android-sdk-macosx/tools(N) $path)
次に /usr/local/android-sdk-macosx/tools/android を実行し Android SDK Manager を起動する。
$ android sdk
Proxy 設定が必要であれば、Android SDK Manager メニューから環境設定を選択し、Proxy settings の項目を穴埋めする。
Android SDK Manager 起動後、Tools フォルダ内の Android SDK Platforms-tools と Android X.X.X (API XX) の幾つか*1にチェックを入れ、下部の Install N package ボタンを押す。
すると、/usr/local/android-sdk-macosx/platform-tools と /usr/local/android-sdk-macosx/platforms/android-NN が作成される。platform-tools には adb コマンドが入っているので PATH を通しておく。
$ export PATH=$PATH:/usr/local/android-sdk-macosx/platform-tools
Android NDK の準備
Android NDK | Android Developers から Mac OS X (intel) の android-ndk-r8-darwin-x86.tar.bz2 をダウンロードし、tar jxf で /usr/local 配下に伸張する。すると /usr/local/android-ndk-r8 が作成されるので下記の環境変数を設定する。
$ export ANDROID_NDK_ROOT=/usr/local/android-ndk-r8
PATH も通しておく。
$ export PATH=$PATH:/usr/local/android-ndk-r8
準備した環境を試す
プロジェクトを作成する。
$ cd /path/to $ g8 jberkel/android-app -b master Template for Android apps in Scala package [my.android.project]: com.github.cooldaemon.HelloWorld name [My Android Project]: Hello World main_activity [MainActivity]: scala_version [2.9.1]: api_level [10]: useProguard [true]: Applied jberkel/android-app.g8 in hello-world
$ cd /path/to/hello-world $ sbt // メッセージ省略 > compile // メッセージ省略 > android:install-device // メッセージ省略 >
実機でアプリを起動すると hello, world! と表示される。
実践編へ続く。
Scala で Android アプリ開発(Toast 編)
Toast 表示中に Toast を表示すると問題があるらしい*1ので、次のようなラッパーオブジェクトを用意する。
package は、準備編で用意したもの。
package com.github.cooldaemon.HelloWorld import _root_.android.content.Context import _root_.android.widget.Toast object AtomicToast { private[this] var toast: Toast = null def show(message: String)(implicit c: Context) = synchronized { if (toast != null) toast.cancel() toast = Toast.makeText(c, message, Toast.LENGTH_LONG) toast.show() } }
これを Activity から使うには、次のようにする。
package com.github.cooldaemon.HelloWorld // 省略 class MainActivity extends Activity with TypedActivity { implicit lazy val c: Context = this // 省略 // 何らかのメソッドの中で… AtomicToast.show("Selected Foo") // 省略 }
implicit で Context を渡すのが嫌な方は適宜修正してご利用ください。
*1:先に本記事のような対策をしてしまったので、困った経験がない
Scala で Android アプリ開発(ActionBarSherlock 編)
ActionBar は使いたいけれど、ターゲットの API Level が低い場合に重宝する ActionBarSherlock を Scala から使う。
sbt プロジェクト設定
まずは依存するライブラリを指定する。準備編を参考に作成したディレクトリ /path/to/hello-world の直下に main.sbt を作成する。
import sbt._ import Keys._ import AndroidKeys._ libraryDependencies ++= Seq( "com.actionbarsherlock" % "library" % "4.0.0-SNAPSHOT" artifacts(Artifact("library", "apklib", "apklib")), "android" % "compatibility-v4" % "r3-SNAPSHOT" )
次に依存するライブラリの位置を指定する。/path/to/hello-world/project/build.scala を次のように修正する。
import sbt._ import Keys._ import AndroidKeys._ object General { val settings = Defaults.defaultSettings ++ Seq ( name := "Hello World", version := "0.1", versionCode := 0, scalaVersion := "2.9.1", platformName in Android := "android-15", // android-10 から android-15 に変更 resolvers += "ActionBarSherlock snapshots" at "http://r.jakewharton.com/maven/snapshot/" // 新規追加 ) // 以下、変更が無いので省略
最後に依存ライブラリを取得する。
$ cd /path/to/hello-world $ sbt > update // 省略 [info] Resolving com.actionbarsherlock#library;4.0.0-SNAPSHOT ... [info] Resolving android#compatibility-v4;r3-SNAPSHOT ... // 省略 [info] Done updating. [success] Total time: 4 s, completed 20XX/XX/XX XX:XX:XX > exit
API Level の修正
AndroidManifest.xml 内の
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.drops_market.Downloader"> <!-- 変更が無いので省略 --> <!-- android:minSdkVersion="10" から変更 --> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15"/> </manifest>
Activity から使用する
package com.github.cooldaemon.HelloWorld import _root_.android.app.Activity import _root_.android.os.Bundle import _root_.android.content.Context // 新規に追加 import _root_.com.actionbarsherlock.app.SherlockActivity import _root_.com.actionbarsherlock.view.{Menu, SubMenu, MenuItem} // Activity を SherlockActivity に変更 class MainActivity extends SherlockActivity with TypedActivity { implicit lazy val c: Context = this override def onCreate(bundle: Bundle) { setTheme(R.style.Theme_Sherlock) // テーマを設定 super.onCreate(bundle) setContentView(R.layout.main) findView(TR.textview).setText("hello, world!") } object MenuID extends Enumeration { val FOO,BAR,BAZ = Value } // 試しにメニューを追加してみる override def onCreateOptionsMenu(menu: Menu): Boolean = { menu .add(0, MenuID.FOO.id, Menu.NONE, "Foo") .setIcon(android.R.drawable.ic_menu_view) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) val subMenu = menu.addSubMenu(0, MenuID.BAR.id, Menu.NONE, "Bar") subMenu.getItem .setIcon(android.R.drawable.ic_menu_more) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) subMenu .add(0, MenuID.BAZ.id, Menu.NONE, "Baz") .setIcon(android.R.drawable.ic_menu_add) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) true } override def onOptionsItemSelected(item: MenuItem): Boolean = { item.getItemId match { case id if id == MenuID.FOO.id => AtomicToast.show("Selected Foo") case id if id == MenuID.BAR.id => AtomicToast.show("Selected Bar") case id if id == MenuID.BAZ.id => AtomicToast.show("Selected Baz") case _ => } true } }
Toast 編で作成した AtomicToast を利用している。
テーマとして何が使えるか?SHOW_AS_ACTION_IF_ROOM って何よ?という疑問をお持ちの方は Google 先生にお尋ねください。
/path/to/hello-world/main.sbt に次のような設定していると…
javacOptions ++= Seq("-Xlint:unchecked") scalacOptions ++= Seq("-verbose", "-unchecked", "-deprecation")
ものすごい数の ActionBarSherlock 関連の警告が出力されるが、ご愛嬌という事で。