ラベル android の投稿を表示しています。 すべての投稿を表示
ラベル android の投稿を表示しています。 すべての投稿を表示

2016年7月27日水曜日

Android CoordinatorLayoutのAnchor機能のまとめ

Android Design Support Library が提供する CoordinatorLayout は Google が推進している Material Design を実装するために、これまでの Layout と比べると幾つかの新しい機能を実装している。

その1つに Anchor 機能がある。これは CoordinatorLayout 内に配置される複数の子View の相対的な位置関係を指定する機能である。

幾つかの単語を定義する。

Anchor View

基準となるView。CoordinatorLayoutの子View、更にその下の階層のView等任意のViewがなれる。
但し、以下に定義する Anchored View 自身やその子、子孫Viewは Anchor View にはなれない。またCoordinatorLayout自身も Anchor View にはなれない。(Design Support Library 23.1 では CoordinatorLayout は Anchor View になれたが、23.2 又はそれ以上で確認したところ、IllegalStateException が発生するようになっていた。)

Anchored View

基準となるAnchor View に対し、相対的に位置関係を指定して配置される View。CoordinatorLayoutの直接の子Viewのみがなれる。これは Layout は一般的に自分の子Viewの配置のみを決めるためである。さらに下の階層での配置はその階層で決められる。

Design Support Library 以外のこれまでの View も Anchor View 及び Anchored View になれる。
また Anchor機能 は CoordinatorLayout が提供するもう1つの機能 Behavior とは無関係である。 但し Behavior にもレイアウト機能があるので、Behaviorが設定されていて、それがレイアウトを行う場合(より正確には Behavior#onLayoutChild が true を返した時)は Anchor での配置よりそちらが優先される。Behavoior の実装で Anchor を使用している場合もある。

Anchor View と Anchored View の指定はレイアウトファイルでおこなう。
以下は AndroidStudio で New Project を Scrolling Activity をテンプレートとして作成したファイル activity_scrolling.xml を一部抜粋、修正したものである。FloatingActionButton の代わりに普通の ImageButton を使用している。これは従来の普通の View で Anchor が設定できることを示すため。(なおコンパイルできるようにjavaソースコードも適当に変更している。)

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    <!-- 省略 -->
>

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        <!-- 省略 -->
    >
    </android.support.design.widget.AppBarLayout>

    <ImageButton
        <!-- 省略 -->
        app:layout_anchor="@id/app_bar"
        app:layout_anchorGravity="center_horizontal|bottom"
        android:layout_gravity="start|bottom"/>
</android.support.design.widget.CoordinatorLayout>

まず基準となる Anchor View には id をふっておく(android:id="@+id/app_bar")。
そして Anchored View となるViewで app:layout_anchor="@id/app_bar" と Anchor View を指定する。

次に Anchor View のどの場所を配置の基準とするかを app:layout_anchorGravity で決める。

left
基準点を Anchor View の左辺上にする。
right
基準点を Anchor View の右辺上にする。
center_horizontal
基準点を Anchor View の左右の中央にする。
top
基準点を Anchor View の上辺上にする。(指定しない場合のデフォルト値)
bottom
基準点を Anchor View の下辺上にする。
center_vertical
基準点を Anchor View の上下の中央にする。
start
配置の方向が左から右か、その逆かで基準点を Anchor View の左辺上または右辺上にする。(指定しない場合のデフォルト値)
end
配置の方向が左から右か、その逆かで基準点を Anchor View の右辺上または左辺上にする。
center
基準点を Anchor View の上下左右の中心にする。

この例だと app:layout_anchorGravity="center_horizontal|bottom" で基準点を Anchor View の左右中央で下辺上に設定している。

さらにこの基準点に対しどのように View を配置するかを android:layout_gravity で指定する。

left
基準点の左側に配置する。(上下の指定はあるが左右は指定しない場合のデフォルト値)
right
基準点の右側に配置する。
center_horizontal
基準点が View の左右の中心に来るように配置する。
top
基準点の上側に配置する。(左右の指定はあるが上下は指定しない場合のデフォルト値)
bottom
基準点の下側に配置する。
center_vertical
基準点が View の上下の中心に来るように配置する。
start
配置の方向が左から右か、その逆かで View を基準点の左側または右側に配置する。
end
配置の方向が左から右か、その逆かで View を基準点の右側または左側に配置する。
center
基準点が View の上下左右の中心に来るように配置する。(指定しない場合のデフォルト値)

この例では android:layout_gravity="start|bottom" で、基準点に対し左下側(左から右配置を仮定)に View を配置している。
但し、padding や margin も含めた範囲内から View がはみ出る場合はその分移動させられ、強制的に枠内に収まるように配置される。

以下は例に上げたレイアウトファイルの場合の画面。

2015年7月8日水曜日

LinuxでのAndroid Emulator使用時のHardware Accelerationの設定

LinuxでAndroid Emulatorを使用する場合、Hardware Accelerationを設定すると起動にかかる時間やレスポンスが改善する。Hardware AccelerationにはGraphics AccelerationとVM(Virtual Machine) Accelerationがある。ここではVM Accelerationの設定方法を示す。
基本的には http://developer.android.com/tools/devices/emulator.html#acceleration に載っていることと同じ。

VM Accelerationを行うには
  • CPUがhardware virtualizationをサポートしていること。
egrep -c '(vmx|svm)' /proc/cpuinfo
とし1以上が返ってくればCPUは対応している。
  • BIOSでhardware virtualization supportがイネーブルになっていること。

が条件となる。

LinuxではKVMをインストールすることでVM Accelerationが可能になる。インストール方法は https://help.ubuntu.com/community/KVM/Installation を参考に行った。

インストールするモジュールは(Ubuntuの場合)
qemu-kvm, libvirt-bin, ubuntu-vm-builder, bridge-utils

sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
等でインストールする。

インストール後libvirtdのグループに自分を加える。
sudo adduser `id -un` libvirtd
その後一度ログアウトし、再びログインする。

端末で
virsh -c qemu:///system list
とし
 Id    名前                         状態
----------------------------------------------------

と表示されれば、インストールは成功。

EmulatorでVM Accelerationを行うには更に
  • Android SDK ToolsがRevision17以上
  • AVDがx86ベースのシステムイメージで作成されている
である必要がある。
この条件を満たすことを確認したらAndroid Studioのメニューの「Run」-「Edit Configurations...」で表示されたダイアログで「Emulator」タブの「Additional command line options」をチェックし、右側に
-qemu -m 512 -enable-kvm
と記述する。
後はこのx86ベースのEmulatorを立ちあげれば良い。

Emulatorが立ち上がるまで(ロック画面が表示されるまで)の時間を計測してみると

x86ベース、VM Acceleration無し 28秒
x86ベース、VM Acceleration有り 18秒
armベース 6分30秒

となった。
armベースは桁外れに遅い。

2015年7月6日月曜日

Android StudioでEmulatorが立ち上がらない時

LinuxでAndroid Studioを使用している時、Emulatorを立ち上げようとすると

NAND: could not write file /tmp/android-***/emulator-***

などというメッセージがでてEmulatorが立ち上がらない場合がある。

そのような時は /tmp ディレクトリがどうなっているか確かめる。

df -h で

Filesystem              Size  Used Avail Use% Mounted on
...
tmpfs                   256M  110M  147M  43% /tmp
...

等と出た場合はtmpfsでメモリ上に/tmpディレクトリがマウントされている。

Emulatorは500Mバイト以上のファイルサイズを/tmp/android-***ディレクトリ上に要求するので、上のようにサイズが256Mバイトだと容量が足りずあのようなエラーメッセージがでる。

解決方法としては/etc/fstabを編集して

tmpfs /tmp tmpfs rw,size=1024m 0 0

等とし、/tmpのサイズを1Gバイト以上にする。または上記をコメントアウトしてディスク上に/tmpディレクトリが存在するようにする。

ちなみに私は、/tmpは一時的なファイルが頻繁に書き込まれるディレクトリなので、SSDになるべく書き込みが少なくなるようにこの設定を行っていました。LinuxでSSDを使っている人は同じ罠にはまるかも?


Android Studioインストール時のエラー

Androidの新しい開発環境Android StudioをLinux(Linux Mint)にインストールしたら、以下のメッセージがでて進まない。

The following SDK components were not installed:
sys-img-x86-addon-google_apis-google-22 and
addon-google_apis-google-22

何度かリトライしてみたがダメ。
結局


  • ホームディレクトリの.AndroidStudio1.2を.AndroidStudio1.2.oldとリネーム
  • ホームディレクトリの.androidを.android.oldとリネーム
  • 念の為インストールしたandroid-studio以下のファイルも全部削除
  • AndroidStudioを再インストール。今度はインストールできた!

これでOK。
恐らく以前Eclipseで開発していた時から存在していた.androidがなにか悪さをしていた模様だが、何がダメだったのかはっきり分からず気持ち悪い。

2014年3月19日水曜日

Android向けAdMob SDKからGoogle Play 開発者サービス SDKへの移行

2014年8月1日でこれまで利用してきたAndroid向けAdMob SDKのサポートが終了する
これに伴って旧来のAdMob SDKを利用しているアプリはGoogle Playへの新規登録やアップデートができなくなる。但し引き続き広告配信は可能。
GoogleはGoogle Play開発者サービスへの移行を推奨している。ここでは移行の手順を簡単に纏めておく。なお以下はEclipseで開発していることを前提にしている。Android Studio等他の環境を使用している場合は参考先を確認すること。

Google Play Services SDKのインストール


Google Play開発者サービスを使用するにはまず開発環境にGoogle Play Services SDKをインストールする必要がある。

  • Android SDK Managerを立ちあげる。Eclipseからならメニューのwindow → Android SDK Manager を選択。
  • ExtrasのGoogle Play servicesをインストール。対象端末はAndroid 2.3以上。
  • Buildに使用するSDKのバージョン(EclipseならPackage ExplorerのProperties - Android - Project Build Targetで指定するSDKのバージョン)のGoogle APIsもインストールする。
  • メニューの File → Importを選択しダイアログを表示させる。
  • Android → Existing Android Code into Warkspace を選択し Next ボタンをクリックしダイアログを表示する。
  • AndroidのSDKをインストールしたディレクトリの下に sdk/extras/google/google_play_services/libproject/google-play-services_lib/ があるはずなので、Root Directoryにこのディレクトリを記入する。
  • Copy projects into workspaceをチェックする。これによって現在のワークスペースにライブラリのコピーが作成される(これ大事らしい)。
  • Finishボタンをクリックしてワークスペースへライブラリプロジェクトをインポートする。

Google Play Servicesのライブラリを自分のプロジェクトから参照する


Google Play Services SDKをインストールしたら、自分のプロジェクトからそれを利用できるようにライブラリを参照する。
なおlibディレクトリに入っている古いAdMob用ライブラリ(GoogleAdMobAdsSdk-*.*.*.jar)は衝突するので削除しておく。

  • Package ExplorerでAdMobを利用するプロジェクトを右クリックしPropertiesを選択しダイアログを表示させる。
  • Androidを選択し下段のLibraryのAddボタンをクリック。
  • google-play-services_libを選択しOKボタンを押す。
  • Applyボタンをクリック(必要?)。
  • OKボタンを押し、ダイアログを閉じる。

Manifestファイルを編集


Manifestファイルを開き<application>エレメントの子エレメントとして以下の記述を追加する。

<application>
省略...
    <activity android:name="com.google.android.gms.ads.AdActivity"   android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
    <meta-data android:name="com.google.android.gms.version"
                            android:value="@integer/google_play_services_version" />
省略...
</application>

Proguardの設定


(参考:Set Up Google Play Services SDK)
Proguardが必要なクラスを削除しないようにプロジェクトディレクトリ下にproguard-project.txtを作成する。これは google-play-services_lib ライブラリプロジェクトの下にあるものをコピーしてくれば良い。


ソースコードの修正


AdMobの使い方は様々なのであくまで一例。
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;

// 省略

// なにかの関数の中。onResumeが良い?

// Google Play servicesが利用可能か確認。
int res = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (res == ConnectionResult.SUCCESS) {
    // Google Play service使用可能
  // adView を作成する
  adView = new AdView(this);
    // MY_ADD_UNIT_IDには自分の広告ユニットIDを入れる。
  adView.setAdUnitId("MY_ADD_UNIT_ID");
  adView.setAdSize(AdSize.BANNER);
   
  // LinearLayout をルックアップする
  LinearLayout layout = (LinearLayout)findViewById(R.id.my_layout);

  // adView を追加
  layout.addView(adView);

  // 広告のリクエストを行い広告を表示する。
  // またテストデバイス(ここではエミュレータ)では
    // 広告が表示されないようにする。
  AdRequest adRequest = new AdRequest.Builder()
  .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
  .build();
  adView.loadAd(adRequest);
}

2012年5月23日水曜日

minSdkVersion, targetSdkVersionとProject Build TargetのSDKバージョンの違い

Androidのアプリを作成する時にはManifestファイルに
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="7"/>
等のようにSDKのバージョン(APIレベル)を設定すると思います。
さらにビルドするときに使用する SDKのバージョンもあります。
これらの違いはよく混乱するので簡単に纏めておきます。

minSdkVersion
指定したバージョン以上のシステムでならアプリが動作することを示します。これ未満のバージョンのシステムにはアプリはインストールできません。
動作することを保証するのは開発者の責任です。使用しているクラス・メソッドのAPIレベルを確認し、必要なら対処しましょう。

targetSdkVersion
指定したバージョンより新しいシステムではコンパチビリティを保証するためtargetSdkVersionで指定したシステムと同じように動作するようになります。
例えばこの値を11未満(Android 3.0.x HONEYCOMB未満)にするとたとえAndroid 3.0.xのシステムでもデフォルトのテーマはこれまでの @android:style/Theme になります(forward-compatibility)。
11に設定していればAndroid 3.0.xのシステムではHONEYCOMBスタイルの @android:style/Theme.Holo が使用されます。他にもAction Barの使用やオプションメニューがなくなる等の影響がでてきます。
アプリを作成した場合はエミュレータや実機で動作を十分に確認すると思いますが、そこで確認したことが保証されるよう確認に使用したバージョンをここに指定しましょう。

Buildに使用するSDKのバージョン
EclipseならPackage ExplorerのProperties - Android - Project Build Targetで指定するSDKのバージョンです。
この値によってソースコードで使用できるクラスやメソッドが決まります。targetSdkVersionより大きい値も設定できます。その場合、使用しているクラス・メソッドのAPIレベルを確認し対処するのは開発者の責任です。

2012年1月14日土曜日

Android - Javaでの文字列から小数への変換

色々な国で使われるアプリだと小数点がコンマ(,)で表現されるところもあることを意識しましょう。主にヨーロッパとかロシア、南米ですね(参考)。

EditTextビュー等に入力された文字列を数値に変換する時に


String str = someEditTextView.getText().toString();
float v = Float.valueOf(str);

として値を取り出すとコンマが小数点だと認識されずに例外(NumberFormatException)が発生してしまいます。

小数点がピリオド(.)でもコンマ(,)でも正しく文字列を値に変換するには

String str = someEditTextView.getText().toString();
NumberFormat nf = NumberFormat.getInstance(); // デフォルトのロケールを使う
Number n = nf.parse(str);
float v = n.floatValue();

等としましょう。これならロケールに合わせてピリオドやコンマを正しく小数点と認識してくれます。

また値を入力するためのEditTextビューに

<EditText
    ...
    android:inputType="numberDecimal"
/>

と数字関係の文字のみ入力できるようにしている場合には

    android:digits="0123456789.,"

等と入力できる文字を指定してコンマも入力できるようにしましょう。

2011年12月30日金曜日

Android Service を自動的に再起動する方法

Androidで常駐するアプリを作るときはサービスを用いて基本的にはずっと起動させておく。ところが以下の場合にはサービスが停止されてしまう。その場合に再起動させる方法を以下に示す。


1) Android OSがメモリ等リソースが少なくなると強制的に停止する場合がある。
Service.onStartCommandの戻り値を START_STICKY 又は START_REDELIVER_INTENT にすることで、OSが勝手に再起動してくれる。


2) 電源が落とされた場合。
Intent.ACTION_BOOT_COMPLETED ブロードキャストを受けるレシーバを作成しそこからサービスを起動する。

Manifestには

<receiver android:name=".BootUpReceiver"
    android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
     </intent-filter>
</receiver>

等と記述。注意点としてRECEIVE_BOOT_COMPLETEDのパーミッションを取る必要がある。

このブロードキャストを受けるクラスとして

public class BootUpReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
            context.startService(new Intent(context, SomeService.class));
        }
    }
}

等としてサービスを起動しましょう。


***** 2012-10-21追記 *****
Android 4.04のスマフォ(XPERIA GXです)でテストしたところ、Intent.ACTION_BOOT_COMPLETED ブロードキャストが受け取れなくなっていた。
Manifestに


  <uses-permission
    android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>


を追加することで解決した。


3) アプリケーションをアップデートした場合。
Intent.ACTION_PACKAGE_REPLACED ブロードキャストを受けるレシーバを作成しそこからサービスを起動する。

Manifestには

<receiver android:name=".PackageReplacedReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_REPLACED" />
        <data android:scheme="package" android:path="your.package.path" />
    </intent-filter>
</receiver>

等と記述。自分のものだけ受け他のアプリのものは除くようにandroid:pathを設定すること。

このブロードキャストを受けるクラスとして

public class PackageReplacedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
             context.startService(new Intent(context, SomeService.class));
        }
    }
}

等としてサービスを起動しましょう。


***** 2012-1-17追記 *****
どうもandroid:pathを設定しても全てのアプリのアップデートを受けてしまう様子。PackageReplacedReceiverクラスの方で



public class PackageReplacedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
         if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
             if (intent.getDataString().equals("package:your.package.path")) {
                context.startService(new Intent(context, SomeService.class));
             }
        }
    }
}

とIntentのDataをチェックしてからサービスを起動しましょう。


2011年12月25日日曜日

JavaのTimerとTimerTaskの罠

Androidアプリで定期的に行う処理を起動するのに

java.util.Timer



java.util.TimerTask

を使用してハマった件のメモ。

スマホの時刻を手動で一時的に先の時刻に変更すると(例えば今8時だったら9時とか)、時刻を元に戻したときTimerがまったく動かなくなる!

どうもTimer、TimerTaskはシステムの時刻を元に次に処理を起動する時刻を決めて、その時刻になったら処理を呼び出すという動作のようだ。そのため一時的に未来の時刻になるとその時刻を元に次に起動する時刻を設定してしまう。時刻を元に戻すとその未来の時刻まではうんともすんとも言わなくなってしまうようだ。

まあActivityのような画面に表示されているときだけ動作するものにはそれでも良いのだが、Serviceのようなバックグラウンドでずっと動くものには要注意である。
そういう場合は

Executors.newSingleThreadScheduledExecutor()
等で

ScheduledExecutorService

を作成しましょう。これなら上記の問題は無いです。

2011年12月1日木曜日

2011年7月10日日曜日

Tab ContentのActivityからServiceにbindする場合の注意

Tabで切り替えるActivityからServiceに以下の様にbindしようとすると、bindできない。

bindService(intent, connection, BIND_AUTO_CREATE);

解決法は

getApplicationContext().bindService(intent, connection, BIND_AUTO_CREATE);

とすること。
その場合はunbindも同様に

getApplicationContext().unbindService(connection);

としませう。

2011年5月19日木曜日

Android Tab Layout の Icon についてのメモ

Tab Layoutのアイコンは低、中、高解像度のデバイス用にそれぞれアイコンを用意する。
アイコンのサイズ等の仕様はAndroid Developers の Icon Design GuidelinesTab Icons にかかれている。これによると


低解像度(ldpi) 中解像度(mdpi) 高解像度(hdpi)
アイコンサイズ 24 x 24 px 32 x 32 px 48 x 48 px
描画領域のサイズ 22 x 22 px 28 x 28 px 42 x 42 px
アイコンの置き場所
Android 2.0以上
res/drawable-ldpi-v5 res/drawable-mdpi-v5 res/drawable-hdpi-v5
アイコンの置き場所
Android 2.0未満
res/drawable-ldpi res/drawable-mdpi res/drawable-hdpi

アイコンの絵はサイズギリギリではなく、周囲に save margine として余白を置くよう推奨している。また絵が四角型の場合はバランスを取るためにもう少し小さくするように推奨している。

Androidでは2.0からタブアイコンのスタイルが大きく変わったので(どう変わったのかはよく知らない)、それぞれのアイコンの置き場所を表の様に分けている。注意点として Manifestファイルの <uses-sdk> の属性 android:targetSdkVersion を 5 以上(Android 2.0以上)に設定すること。たぶんこれでアイコンが上記の2つの場所に分けておかれていることを(Android 2.0以上の)システムが認識できる。

実際に(Illustrator、inkscape等で)アイコンを描く時には、各解像度のサイズの整数倍のサイズで書くように推奨している。 24, 32, 48 さらにメニュー用アイコンで使う72 px を考慮すると、これらの最小公倍数は lcm(24, 32, 48, 72) = 144 となる。よって144 x 144 px の整数倍のサイズの画像を作成し(例えば 864 x 864 px)、これを各サイズに縮小するよう勧めている。

2011年5月17日火曜日

Tab Layoutを使ってみる

Tab Layoutの使い方のメモ。ちょっと未来の自分のために(^_^;
内容は Android Developers の Tutorial と同じ。これを自分に分かりやすく(日本語で)書いたもの。

タブを使うときのレイアウトは以下のような構成になる。

TabHost <- root node, 一番上の親
    LinearLayout <- 以下の子を縦に配置する
        TabWidget <- タブが表示される領域
        FrameLayout <- タブによって切り替えられたコンテンツが表示される領域

まず最初に、メインのレイアウトファイル main.xml を以下の様につくろう。


<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:padding="5dp">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:padding="5dp" />
    </LinearLayout>
</TabHost>

ポイントは LinearLayout の orientation を "vertical" にすること、また TabWidget の id は "@android:id/tabs"、コンテンツを表示する FrameLayout の id は "@android:id/tabcontent" とする必要がある。これはこの id を使って親の TabHost がこれらを認識するため。

このサンプルでは3つの Activity をタブによって切り替える。外観は次の様になる。


お次はこれらの Activity を作ろう。
Activityは
  • ArtistsActivity
  • AlbumsActivity
  • SongsActivity
それぞれのソースコードは
ArtistsActivity.java:

package test.hellotabwidget;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class ArtistsActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TextView textview = new TextView(this);
        textview.setText("This is the Artists tab");
        setContentView(textview);
    }
}

AlbumsActivity.java:

package test.hellotabwidget;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class AlbumsActivity extends Activity {
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TextView textview = new TextView(this);
        textview.setText("This is the Albums tab");
        setContentView(textview);
    }
}

SongsActivity.java:

package test.hellotabwidget;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class SongsActivity extends Activity {
@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TextView textview = new TextView(this);
        textview.setText("This is the Songs tab");
        setContentView(textview);
    }
}

それぞれの内容はほとんど同じで、表示する文字列だけが異なる。またこれらの Activity はここでは簡単のためにレイアウトファイル(*.xml)は使わない。直接 TextView を生成してコンテンツとして設定している。

次はタブに表示するアイコン。これは先のTutorialからコピーしたものを用いる。面倒なので3つのタブ全て同じアイコンとする。実際にはそれぞれに別のアイコンを用意する。
タブが選択されたときのアイコン(ic_tab_artists_grey.png)
非選択時のアイコン(ic_tagb_artists_white.png)

これらのアイコンを res/drawable/ に保存する。
さらに以下の xml ファイルを同じ res/drawable/に保存し、タブの選択・非選択時のアイコン画像を指定する。

ArtistsActivityのタブには
ic_tab_artists.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- When selected, use grey -->
    <item android:drawable="@drawable/ic_tab_artists_grey"
          android:state_selected="true" />
    <!-- When not selected, use white-->
    <item android:drawable="@drawable/ic_tab_artists_white" />
</selector>

同様に AlbumActivity には ic_tab_albums.xmlを、SongsActivity には ic_tab_songs.xml 用意する。内容はic_tab_artists.xml とまったく同じでよい(実際にはそれぞれに使用するアイコンを別に指定する)。

次はユーザーがアプリ起動時に呼び出す一番上の Activity を作成する。
HelloTabWidget.java:

package test.hellotabwidget;

import android.app.TabActivity;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.widget.TabHost;

public class HelloTabWidget extends TabActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Resources res = getResources(); // Resource object to get Drawables
TabHost tabHost = getTabHost();  // The activity TabHost
TabHost.TabSpec spec;  // Resusable TabSpec for each tab
Intent intent;  // Reusable Intent for each tab

// Create an Intent to launch an Activity for the tab (to be reused)
intent = new Intent().setClass(this, ArtistsActivity.class);

// Initialize a TabSpec for each tab and add it to the TabHost
spec = tabHost.newTabSpec("artists").setIndicator("Artists",
res.getDrawable(R.drawable.ic_tab_artists))
.setContent(intent);
tabHost.addTab(spec);

// Do the same for the other tabs
intent = new Intent().setClass(this, AlbumsActivity.class);
spec = tabHost.newTabSpec("albums").setIndicator("Albums",
res.getDrawable(R.drawable.ic_tab_albums))
.setContent(intent);
tabHost.addTab(spec);

intent = new Intent().setClass(this, SongsActivity.class);
spec = tabHost.newTabSpec("songs").setIndicator("Songs",
res.getDrawable(R.drawable.ic_tab_songs))
.setContent(intent);
tabHost.addTab(spec);

tabHost.setCurrentTab(2);
}
}

ここでは通常の Activity ではなく TabActivity を継承する。
getTabHost() で TabHost を取得し、 newTabSpec(String) で TabHost.TabSpec を作成しそれぞれのタブの特性を設定する。そして addTab(TabSpec) でそれぞれのタブを追加していく。
最後にsetCurrentTab(int)で最初に表示されるタブをインデックスで指定する。

最後にManifestにこれらのActivityを追加するのを忘れないこと。
AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="test.hellotabwidget"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="6" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".HelloTabWidget"
 android:label="@string/app_name"
           android:theme="@android:style/Theme.NoTitleBar">
             <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
           
        <activity android:name=".ArtistsActivity" />
        <activity android:name=".AlbumsActivity" />
        <activity android:name=".SongsActivity" />
    </application>
</manifest>

精度

スマートフォンを操作したりしているときに歩数計アプリが誤動作しないように、一定時間カウントが続かないと歩いてると見なさない様にした。これでずいぶん誤動作が減ったんだけど、家の中でちょっと歩いたりとかがカウントされにくい。
難しいな~。

2011年5月16日月曜日

歩数計アプリ

歩数計のAndroidアプリを作っている。とりあえず動作確認のプロトタイプは作っていてまずまず動作する。次は公開するために、UIをきちんと作り込んだり、搭載する機能を決めたりする予定。6月半ばには公開できるかな?