ページ

2012/11/25

Android ActionBarCompatでAndroid 2.xでもタブ+Fragmentを使う時にアクションボタンが切り替わるようにする

|
Android SDKに付属しているサンプルのActionBarCompatを少しだけ改良した ActionBarCompatRevというライブラリを作りました。
ActionBarCompatRev

ActionBarを使ったスタイルはAndroid 3.0以降で利用できますが、
実際にアプリ開発をするとなると、まだまだAndroid 2.3などの下位バージョンを
考慮する必要があり、デザイン・仕様を共通化するために前述のActionBarCompatなどを
使う必要があります。
もちろん、既にActionBarSherlockのような非常に高機能なライブラリも出ていますが
高機能ゆえにサイズも大きい、もしバグ(もしくは期待しない挙動)があったときに手を入れるのが難しい、など採用しにくいケースもあると思います。

そこで、規模が小さく内容を把握しやすいActionBarCompatを使おうと思ったのですが、問題だったのが、Tabを使ったレイアウトだとAndroid 2.xで動かした時に、タブ(=Fragment)を切り替えてもアクションボタンが切り替わらないということです。

ActionBarCompatのソースコードを読んでいくと、Fragmentのメニュー(onCreateOptionsMenu())が呼び出されるのはActivityが切り替わったタイミングだということが分かります。
なので、タブを使う場合のように1つのActivityの中で複数のFragmentが切り替わっていくパターンではFragmentごとにメニューを読み込んでくれません。

冒頭に書いたライブラリは、ActionBarCompatのこの点を改良したものです。
ライブラリを使ったサンプルを付けていますので、同じような問題に遭遇した方はお試しいただければと思います。

使うには、まずこのライブラリをAndroid Library Projectとして組込みます。

2012/10/27

Android 端末情報を確認・共有するアプリ

|
Androidアプリの開発をするときに困ることの1つに、端末の種類の多さがあります。

アプリが使う機能にもよりますが、想像以上に機種間の差はあったりするので検証する端末の選定は重要です。
しかしながら、キャリアやメーカーから公開されている仕様だけでは不十分なことが多く、もっと多くの情報を収集したいものです。
端末の情報を表示するアプリは既に多数ありますが、これをサーバへ送信して皆が見られるようにするためのアプリを公開しました。
DeviceInfo

名前が普通過ぎるので、アプリ名で検索すると残念ながら多数ヒットするのですが…。

このアプリのメニューから「送信」とすると、サーバへ端末情報が送られます。
もちろん、個人を特定するような情報は送っていません。
ファイルへのエクスポートもできます。

サーバサイドのアプリは現時点で準備中ですが、下記のような一覧が見られる予定です。
あとは如何に多くの端末情報が集められるかが課題ですね。

Android Intentでテキストを共有する時のタイトル

|
たまにやるとつい忘れてしまうのでメモです。

IntentのExtraにテキストデータを入れて共有するとき、以下のようにすると思います。
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/*");
intent.putExtra(Intent.EXTRA_SUBJECT, "something");
このとき、Intent#putExtra()の第1引数がIntent.EXTRA_TITLEだとEvernoteなどはタイトルとして認識してくれますが、Gmailなどは認識しません。

メール系のアプリでもタイトルとして扱われるようにするには、Intent.EXTRA_SUBJECTとする必要があるようです。 EvernoteはIntent.EXTRA_SUBJECTでもタイトルとして扱っており、こちらの方が適用範囲が広いかもしれません。

Heroku + Node.js + Express + Mongoose

|
Heroku + Node.js + Express + Mongoose で、とあるアプリケーションを作成しているのですが、Heroku上ではどのバージョンの組み合わせが動くのか分からず嵌ってしまいました。 以下は、現時点(2012/10/26)で動作したpackage.jsonの内容です。 誰かの参考になれば幸いです。
{
  "name": "hoge",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "express": "3.0.0",
    "mongoose": "1.7.4",
    "jade": "*"
  },
  "engines": {
    "npm": "1.1.41",
    "node": "0.6.13"
  }
}

2012/10/18

Android keystoreの操作

|
AndroidというよりはJDKの話ですが、前のエントリの続きのメモです。
keystoreの変更操作は、バックアップを取ってから実行した方が良いです。

■keystore内のエントリの確認方法

keytool -list -keystore keystoreファイル名 -storepass パスワード

■keystoreのキー(エントリ)のパスワード変更方法

keytool -keypasswd -alias エイリアス -keypass キーのパスワード -new キーの新しいパスワード -keystore keystoreファイル名 -storepass keystoreのパスワード
パスワードをパラメータに指定しない場合は対話形式で入力できるようです。

■keystore自体のパスワード変更方法

keytool -storepasswd -keystore keystoreファイル名 -storepass keystoreのパスワード -new keystoreの新しいパスワード
念のためですが、これらのパスワード変更をしても署名(証明書のフィンガープリント)が変わってしまうことはなく、パスワード変更前に作ったAPKに対して上書きインストールできました。

■keystore内のエントリの削除方法

例えば、いくつものアプリのキーを1つのkeystoreにまとめてしまっていて、一部のアプリを移管しないといけない場合などに必要かもしれません。
くれぐれも、必要なエントリをうっかり削除しないように気をつけてください。
keytool -delete -keystore keystoreファイル名 -alias 削除対象エントリのエイリアス -storepass keystoreのパスワード

Android keystoreのエイリアス変更方法

|

keystoreのエイリアスを変更する方法です。
keystoreを作り直せば良い話ですが「もうこのkeystoreで署名したAPKは出回ってしまってるのでkeystoreを作り直すことはできない」という場合に使えそうです。

keytool -changealias -alias 現在のエイリアス -destalias 新しいエイリアス -keystore keystoreファイル名 -storepass パスワード

2012/09/09

Android adbコマンドで特定端末を簡単に指定

|
Androidでテストやデバッグをするとき、複数の端末をつないでadbコマンドを何度も叩くことがあると思います。

デバッグ用のkeystoreで一通り開発した後、一度アンインストールしてからリリース用の署名入りkeystoreで署名してProGuardのかかったapkをインストールし、(内容によっては)adb pullで端末内のファイルを取り出して確認したり…とadbコマンドは意外に多用します。

そんなとき、adbはどの端末に対して実行するのか指定するように言ってきますが それにはadb devicesを実行してシリアル番号を調べないといけません。一度実行すれば別の場所に控えておくことはできますが、やはりコマンドを作るときに毎回シリアル番号をコピー&ペーストしなければいけません。

こんな手順は面倒すぎる…ということで、シリアル番号を簡単に指定できるシェルスクリプトを作りました。
ADBS - github

Mac/Linuxならパスの通ったディレクトリにプログラムを置くだけ動きます。Windowsの場合はCygwin環境でパスの通ったディレクトリに置けば動くかと思います。

これを使うと、シリアル番号の最初の1文字を入力するだけで実行できます。

$ adb devices
List of devices attached
304D19E0D41F543F  device
275501700028393   device

となっているときに、304D19E0D41F543Fの端末に対してshellコマンドを実行するには

$ adbs -s 3 shell
shell@android:/ $

とするだけです。一覧を見てから実行したければ'-s'オプションを外して

$ adbs shell
List of devices attached
[3] 304D19E0D41F543F
[2] 275501700028393
1st character of the serial number you want to use: 2
shell@android:/ $

とできます。
もしシリアル番号の先頭文字が同じ端末があった場合、シリアル番号を前方一致で探しているので

$ adbs -s 30 shell

のように2文字目以降も指定すれば動くはずです。

2012/08/19

Android TextViewのURLをリンクにしつつURL以外もクリック可能にする

|
TextViewに含まれるURLをリンクにして、クリックするとブラウザでリンク先を表示するケースはよくあると思います。
それに加えて、URL以外の場所をクリックした場合はそのイベントを拾いたかったのですが、なかなかその方法が見つからず苦労したので、ここに整理したものを公開します。
同じことをされたい方がいらっしゃいましたらどうぞご利用ください。(LinkUtils.java)
※もう一つのActivityは使用例です。
https://gist.github.com/3394338
上記の抜粋ですが、使い方は以下のようになります。
        String testStr = "URLの形式のみブラウザで開きます。http://www.yahoo.co.jp/とhttp://d.hatena.ne.jp/をリンク表示にしてクリック可能にしました。";

        TextView textView = new TextView(this);
        textView.setText(testStr);
        LinkUtils.autoLink(textView, new LinkUtils.OnClickListener() {
            @Override
            public void onLinkClicked(final String link) {
                Log.i("SensibleUrlSpan", "リンククリック:" + link);
            }

            @Override
            public void onClicked() {
                Log.i("SensibleUrlSpan", "ビュークリック");
            }
        });

        setContentView(textView);
文字列を設定したTextViewを渡して、LinkUtils.autoLink()を呼び出します。 単にURLをリンクにするのであればこれだけで動きます。
URLがクリックされたときと、その他の場所がクリックされたときをハンドリングする場合は、第2引数に専用のリスナーを設定します。
URLとして認識される文字列はLinkUtilsの中に正規表現で持っていますが、autoLink()の第3引数で変更可能です。
内容としては、内部でURLSpanとLinkMovementMethodを継承したクラスを作り、リンクがクリックされたかどうかを検知できるようにして実現しています。

以下を参考にさせて頂きました。ありがとうございます。

http://www.techmaru.net/wordpress/20101015/textviewlink/

2012/08/03

Android ViewPagerを使った円形のインジケータ

|
左右にスワイプしてページを切り替えるViewPagerというものがありますが、円形のインジケータを使用したサンプルを作成しました。
viewpagersample

既にライブラリとしてViewPagerIndicatorというすばらしいものがあります。が、別のAndroidプロジェクトをいくつも組み込むと環境構築手順やビルド手順も煩雑になりますし、単に円形タイプのインジケータを組み込みたいだけなのでこちらは使いたくない…という方がもしいらしたら、参考にしてみてください。
小さいクラスなので中身もすぐに理解できると思います。

2012/06/12

Android Theme.Lightでのダイアログの文字色

|
テーマに@android:style/Theme.Lightを使用すると、
背景が白の明るい配色になり、文字色は黒系の色になります。

@android:style/Themeの場合は逆です。こちらの場合は問題ないのですが、 @android:style/Theme.Lightを使うときにダイアログを独自のレイアウトで組み立てると、 背景色は黒なのに、文字色も黒系(グレー)になってしまいます。
つまり、各TextViewに文字色を都度設定しなければならないようです。

これを避けるために、独自レイアウトの背景色がTheme.Lightと同じになるよう
テーマをカスタマイズしよう、という案を思いつきますが ダイアログの背景色を変えると、
今度は標準的なAlertDialogに影響が出ます。
AlertDialog.Builder#setView()でレイアウトを変えない普通のAlertDialogの場合、 Theme.Lightを適用していても文字色が白系のまま変わらないためです。
背景白・文字白系で非常に見にくくなります。
*AlertDialogの色を変えられるのは背景色のみのようです。

背景・文字色ともに変更ができるのは
レイアウトを指定しないAlertDialogではなく
レイアウトを指定したAlertDialogなので
レイアウトファイルの中で背景色と文字色両方を指定するのが正解なのだと思います。

ちなみに上記はAndroid 2.3.3とAndroid 4.0(エミュレータ)で確認しています。

2012/03/31

Android ListViewがクリックできない原因の一例

|

ListViewをカスタマイズして以下のような行のレイアウトを作成しましたが、
クリックできなくなってしまいました。
ListView#setOnItemClickListener()を使っても、クリックのイベントに反応しなくなったのです。
しかしなぜか ListView#setOnItemLongClickListener() は有効でした。

レイアウトは以下です。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:padding="2dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/updated_at"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true" />

    <TextView
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/title"
        android:inputType="text" />

</RelativeLayout>

試行錯誤した挙げ句、原因は最後の TextView に指定している android:inputType だとわかりました。
これが設定されていると、この TextView が focusable になり、
ListViewではクリックイベントを拾えなくなるようです。

対策は、以下のように focusable="false" とすることです。
これで、ListView#setOnItemClickListener() が有効になります。

    <TextView
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/title"
        android:inputType="text"
        android:focusable="false"  />

2012/01/07

Android SonyTablet SをMacのadbで認識させる

|

SonyTablet Sを、Windowsでは接続できましたがMacで接続できず試行錯誤していましたが、
SONYのサイトに説明がありました。
http://www.sony.jp/support/tablet/products/info/sdk.html

単純に接続しただけでは以下のようになり、adbで認識されません。

% adb devices
List of devices attached 

%

上記サイトの説明のようにすると、下記の通りadbでデバイスを認識するようになります。

% echo "0x54c" >> $HOME/.android/adb_usb.ini
% adb kill-server
% adb start-server
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
% adb devices
List of devices attached 
4289142435fa597 device

%

2012/01/02

Android MapActivityをIntentで起動したときにNoClassDefFoundError発生

|

AndroidでGoogle Maps APIを使う場合に、次のようにIntentでMapActivityのサブクラスを起動するケースがあると思います。

Intent intent = new Intent().setClass(this, SampleMapActivity.class);

その際、別のプロジェクトからコピーしてきた場合などで、ある記述が抜けていると NoClassDefFoundErrorが発生してしまいます。 他の情報が全くなく、はまってしまったので対処方法を記録します。

解決策は下記の通りなのですが、uses-libraryでGoogle Maps APIを使っていることをAndroidManifest.xmlに明記する必要があります。
Cannot resolve MapActivity class on Android
具体的には以下のようにapplicationタグの子としてuses-libraryを書きます。

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme" >
        <uses-library android:name="com.google.android.maps" />

uses-libraryがなくてもコンパイルは成功するので、注意が必要です。