2016年7月11日月曜日
2013年11月14日木曜日
Android Eclipse ADTでビルドできていたプロジェクトが急にコンパイルエラーになった時の対処法
自分のところではEclipseを落としてあげなおしたときに良く発生する。
原因としては自動生成のR.javaがなくなるなどgenフォルダの中身がおかしくなってコンパイルエラーというようである。
そうなってしまうと厄介だが、今のところ以下のいづれかで復旧できるようだ。
- プロジェクトをクリーンする。
- プロジェクトを右クリックし、"Android Tools"→"Fix Project Properties"を選択、その後プロジェクトをクリーンする。
- プロジェクトを一旦削除(ファイルは消さないこと!)。その後再度インポート。
ただ状況によってどれが効くというのが異なるので、いろいろ試してみることになる。
2013年7月8日月曜日
Android 指を近づけただけで応答 FloatingTouch
XPERIAの場合APIとしてはView.OnHoverListenerあたりが呼ばれるようです。 HoverはPCではよく使うAPIですがAndroidにもあるので、いつ呼ばれるのだろうと思っていましたがこういう機能向けだったのですね。 Galaxyのほうも同じような仕様なんだろうか…。
うまくつかうとUIの表現方法が広がりそうですが実機が無いので試せないのが残念。 今後搭載端末が増えてきて標準的に使えるようになるのを期待したいです。
それにしてもコマーシャルを見ているとGalaxy特有の機能に見えてしまう。 SONYももっと宣伝すればいいのに… キラーアプリを作れなかったのか?
2013年7月5日金曜日
Android Nexus7にADB接続しようとしてはまる
開発者オプションが無い
設定画面にUSBデバッグを有効化するための開発者オプションが見当たらない。 以下の記事によると隠しコマンドになったようである。どうしてこうなった…
ADBが認識しない
USBデバッグを有効にできたので早速つないでみるとNexus側では "USBデバッグが接続されました" と出るのだが、PC側のADBが "waiting for device" のまま…。 以下記事によると接続の種別を変更する必要があるとのこと。これは分からないわ。これら方々の記事のおかげで助かった。 Android SDKもバージョンが変わるとわけのわからないトラブルが起きるが、Nexusも同じとは困ったものである。
関連記事
2013年6月20日木曜日
Android カスタムViewの作成
複数の子ViewをもつようなカスタムViewの場合、典型的な作り方のパターンとしてはViewGroupを継承してカスタムクラスを作り、最低限以下のメソッドをオーバーライドする。
- onMeasureメソッド
- ViewGroupのサイズを決める
- onLayoutメソッド
- 実際に子Viewを配置する
2013年5月21日火曜日
Android Eclipse ADT22へのアップグレードではまる
非常に困っていたが以下のページに助けられる。
どうやらAndroid SDK Build-toolsというツールが新しくビルドに必要になったようである。
こんなのわかるわけない…
せめて自動でインストールしてほしい。
まるで古いPCのアドベンチャーゲームをやらされているような感覚である。 そして、後日Nexus7でもやられてしまった。
関連記事
2013年5月19日日曜日
Android Viewのカスタム属性の作り方
背景色の属性
背景色関連の属性は以下のように色(color)や画像(drawable)で設定できるようにできるとよい。<!-- 色(color)でmyBackgroundを指定する --> <myapp.MyView android:id="@+id/myview" android:layout_width="wrap_content" android:layout_height="wrap_content" myapp:myBackground="#ffff0000" /> <!-- イメージ(drawable)でmyBackgroundを指定する --> <myapp.MyView android:id="@+id/myview" android:layout_width="wrap_content" android:layout_height="wrap_content" myapp:myBackground="@drawable/bg" />
こんなときはまず attr.xmlでは以下のようにreference
とcolor
の属性として定義する。
<resources> <declare-styleable name="MyView"> <attr name="myBackground" format="reference|color"/> </declare-styleable> </resources>
自作ViewのJava側ではDrawableとして受け取り、背景色を設定したいviewにsetBackgrund()で設定する。 colorもDrawableで受けることができ、背景色として設定できるようだ。 この方法でよいのか公式なドキュメントを確認できていないが問題なく動いているようである。
public MyView(Context context, AttributeSet attrs) { super(context, attrs); Drawable myBackground = a.getDrawable(R.styleable.MyView_myBackground); if (myBackground != null) { myView.setBackgroundDrawable(myBackground); } }ちなみに、AttributeSet.getColor()で受けると以下のようなエラーが出る。 背景色を設定できるようにする場合は特に理由がなければDrawableで受けるようにしておいたほうが無難だろう。
Caused by: android.view.InflateException: Binary XML file line #35: Error inflating class myapp.MyView
at android.view.LayoutInflater.createView(LayoutInflater.java:613)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:749)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:749)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:270)
at android.app.Activity.setContentView(Activity.java:1881)
at myapp.MyActivity.onCreate(MyActivity.java:58)
at android.app.Activity.performCreate(Activity.java:5104)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
... 11 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.constructNative(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at android.view.LayoutInflater.createView(LayoutInflater.java:587)
... 24 more
Caused by: android.content.res.Resources$NotFoundException: File res/drawable-hdpi/bg.png from drawable
resource ID #0x7f020011: .xml extension required
at android.content.res.Resources.loadColorStateList(Resources.java:2094)
at android.content.res.TypedArray.getColor(TypedArray.java:319)
at myapp.SampleQueryView.(SampleQueryView.java:54)
... 27 more
2013年5月16日木曜日
Android Twitter4JでOAuth認証する
Callback URLを使わずお手軽にすませる方法を使った。 この場合はTwitterログインの後にPIN番号が画面に表示されるので、それを手で入力して認証を完了する。 大まかな手順は次のようである。
- TwitterにRequestTokenトークン要求
- Twitterから認証のためのログインページURLが返されるのでブラウザで表示
- ユーザーがログインしてアプリを許可するとTwitterがPIN番号を発行
- TwitterにPINを渡してAccessトークン取得
今回紹介するコードはAndroidアプリつぶマップで使用しているので、 どのように動くか興味ある方はインストールしてもらえるとよいかと思う。
Twitter4Jラッパークラス
RequestTokenトークン要求とAccessトークン取得の際、素のままのTwitter4J APIを呼び出してもよいが、 それぞれの処理間での値の保持のためラップしたクラスを作成し呼び出すようにした。public class TwitterTest { private static final String CONSUMER_KEY = "アプリに発行されたConsumer Key"; private static final String CONSUMER_SECRET = "アプリに発行されたConsumer Secret"; private Twitter twitter; private RequestToken requestToken; public String requestToken() { // インスタンスの初期化 twitter = TwitterFactory.getSingleton(); // Twitterに登録したアプリのConsumer key と Consumer secretを設定 twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); // RequestTokenトークン要求 // 認証ページのURLを返すのでWebViewで表示しユーザーにログインしてもらう requestToken = twitter.getOAuthRequestToken(); return requestToken.getAuthorizationURL(); } // Accessトークン取得。引数にWebで表示されたPINを入れる。 // 認証済みのTwitterインスタンスを返す public Twitter getAccessToken(String pin) throws TwitterException { if (requestToken == null) return false; // RequestTokenとPINよりAccessトークン取得 AccessToken _accessToken = twitter.getOAuthAccessToken(requestToken, pin); return twitter; } }
アプリ側
アプリ側ではラッパークラスを使い次のようにコードを書く。
まず、TwitterにRequestTokenトークン要求する。ラッパークラスのrequestToken()
メソッドを呼び出す。
TwitterTest t = new TwitterTest(); String url = t.requestToken();
メソッド呼び出しの結果、認証のためのTwitterへのログインページURLが返されるのでこのURLをWebViewに表示する。 以下のような画面が開く。同じ画面の上部にはPIN番号を入力するためのEditTextを配置した。
ログインページでユーザーがログインしてアプリを許可するとTwitterがPIN番号を発行する。 画面表示されたPIN番号をユーザーに入力してもらう。
入力されたPINを引数にラッパークラスのgetAccessToken()
メソッドを呼び出すことで認証が完了する。
String pin = "ユーザー入力のPIN番号"; Twitter twitter = t.getAccessToken(pin);
メソッドの返り値である認証済みTwitterインスタンスを使ってツイート取得や検索ができる。 以下は検索の例である。
Query q = new Query(); q.setQuery("キーワード"); QueryResult qr = twitter.search(q);
皆さんもぜひ試してみてください。
2013年4月3日水曜日
Android ParcelでString[]を読んだり書いたり
メソッド名の対応からしてwriteStringArray()で書いたものをreadStringArray()で読み込むのだろうと思っていたが読むほうはcreateStringArray()になるようである。 以下のような感じになるらしい。
public class MyObj implements Parcelable { private String[] texts; // 読むほう public MyObj(Parcel in) { texts = in.createStringArray(); } // 書くほう @Override public void writeToParcel(Parcel out, int flags) { out.writeStringArray(texts); } ... }
readStringArray()でも読み込めるようだが、書き込んだ長さと同じ配列をあらかじめ準備して引数に指定するらしい。 長さが決まってない配列に使うのは面倒そうだし、nullは扱えなさそうである。 固定長の配列を連続読み込みするような場合には使えそうだが、それ以外はあまり出番が無いのではと思う。
2011年1月18日火曜日
Android アプリからツイートする(非正攻法)
しかし、OAuthなどが必要だったり敷居が高く感じる人も多いであろう。
端末にTwitter公式アプリが入っているという前提があるなら、Intentを使うことで投稿準備ができた状態でTwitterアプリを起動し最後に投稿ボタンを押してもらうだけでツイートできるようになる。
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setClassName("com.twitter.android", "com.twitter.android.PostActivity");
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "Androidから投稿なう"); // 投稿するメッセージ
try {
startActivity(intent);
}
catch(ActivityNotFoundException ex) {
// Twitter公式アプリが無いときのエラー処理
}
ただし公式アプリのクラスを直接呼び出しているので、将来アプリがバージョンアップされた場合には動かなくなる可能性があることに注意。
とりあえず Twitter公式アプリVersion1.0.5で動作を確認した。他のバージョンでの動作報告をコメントしてもらえるとありがたいです。
Android アプリからSMSを送る
試行錯誤の結果、以下のような方法で宛先と本文を埋めた状態で標準SMSアプリが起動することがわかった。あとはユーザーに送信ボタンを押してもらえばSMSが発信される。
Uri uri = Uri.parse("smsto://");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setType("vnd.android-dir/mms-sms");
intent.putExtra("address", "090xxxxxxxx"); // 電話番号を入れる
intent.putExtra("sms_body", "こんにちは"); // 送信メッセージを入れる
try {
startActivity(intent);
}
catch(ActivityNotFoundException ex) {
// SMSアプリが無いときのエラー処理
}
2011年1月13日木曜日
Android 文字列リソースのエラー
アップデート前まで正常だったプロジェクトで、strings.xmlがこのようなエラーをはくようになりビルドできなくなってしまった。
W/ResourceType(15164): Bad XML block: header size 276 or total size 18088132 is larger than data size 0
...\res\values\strings.xml:13: error: Multiple substitutions specified in non-positional format; did you mean to add the formatted="false" attribute?
...\res\values\strings.xml:13: error: Unexpected end tag string
プロジェクトが壊れてしまったのかと思いあわてたが、ここの情報によると、書式文字列のチェックが厳しくなったようで、%sなどの置換部分が2個以上ある場合はそれぞれに位置を指定する必要があるらしい。
たとえば、今までこうしていたのは、
<string name="message">製品名=%s %d円です</string>
このように書き換える必要がある。
<string name="message">製品名=%1$s %2$d円です</string>
おまけ
アップデート直後にEclipseを起動すると "sdk platform tools component is missing" というエラーが出るかもしれない。その場合は、メニューから Android SDK and AVD Managerを起動してインストールされているパッケージすべてをUpdate Allしてやると復旧するようです。
2010年12月5日日曜日
Android エミュレータにメディアデータを追加
- Virtual Device起動。
- コマンドプロンプトから adb shell でシェルに入り mkdir /sdcard/music でフォルダを作成する。
- コマンドプロンプトに戻り adb push {mp3ファイル} /sdcard/music を実行して、さっき作ったフォルダにmp3ファイルを転送。({mp3ファイル}のところにはファイル名いれる)
- Virtual Deviceのアプリの一覧から "Dev Tools" を実行し "Media Scanner" を実行する



"Media Scanner" を実行すると上記のような画面になり、標準のメディアプレイヤーで追加したファイルを認識できるようになる。
2010年11月18日木曜日
java.util.Dateとandroid.text.format.Time どちらを使うべきか?
http://developer.android.com/reference/android/text/format/Time.html
まだドキュメントを読んだ限りなので、ちょっと実際に使って試してみることにしよう。
2010-12-20 追記
android.text.format.TimeはSerializableでないことが判明。つまりIntentに入れてActivity間で受け渡すデータとしては使えないので注意が必要です。
Timeをサブクラス化してSerializableをimplementsしたものを作ればいいのかもしれないが、将来的にTimeのインスタンス変数にSerializableでないものが入ってくるとアウトなので今回は利用を見送る。
2010年9月9日木曜日
Android nativeコードでファイル書き込み
Javaからnativeの関数に byte[]配列を渡してそれをファイルに書き込む関数を作ってみた。以下のJavaソースをjavahで変換して対応するヘッダファイルを作成。
FileManager.java
package test.filetest;
public class FileManager {
public native int write(byte[] data);
static { System.loadLibrary("filetest"); }
}
test_filetest_FileManager.h
#include <jni.h>
#ifndef _Included_test_filetest_FileManager
#define _Included_test_filetest_FileManager
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Java_test_filetest_FileManager_write
(JNIEnv *, jobject, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
Java_test_filetest_FileManager_write関数の実装は以下のようにしてみた。エラーから抜けるのにgotoを多用しているので人により好き嫌いあるかもしれないが、NDKだとtry-catchが使えないようなのでこの方法が楽といえば楽。
test_filetest_FileManager.cpp
#include "test_filetest_FileManager.h"
#include <string.h>
#include <fcntl.h>
#include <android/log.h>
#define _DBGPRINTF(...) __android_log_print(3, "filetest", __VA_ARGS__)
JNIEXPORT jint JNICALL Java_test_filetest_FileManager_write
(JNIEnv* env, jobject caller, jbyteArray jBin)
{
jbyte* bin = NULL;
int binSize;
int err = 0;
int s;
_DBGPRINTF("Java_test_filetest_FileManager_write");
// ファイルを /data/data/{pkgname} 以下のフォルダに読み書きする
// ここでは hello.dat を書き込み用にオープン(O_WRONLY)。無ければ作成(O_CREAT)
// ファイルのmodeを 0666 に設定
int fd = open("/data/data/test.filetest/hello.dat",
O_WRONLY|O_CREAT,
S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
_DBGPRINTF("fd=%d\n", fd);
if (fd < 0) {
_DBGPRINTF("Error: open");
err = -1;
goto error_exit0;
}
// 渡されたJava byte配列を C byte 配列に変換
bin = env->GetByteArrayElements(jBin, NULL);
if (bin == NULL) {
_DBGPRINTF("Error: GetByteArrayElements");
err = -1;
goto error_exit1;
}
// 渡されたJava byte配列の長さを取得
binSize = env->GetArrayLength(jBin);
_DBGPRINTF("Write: %d bytes");
// writeで書き込み
s = write(fd, (void*)bin, binSize);
_DBGPRINTF("Write: ret=%d", s);
if (s < 0) {
_DBGPRINTF("Error: write");
err = -1;
}
// C byte配列を使い終わったのでメモリから解放
env->ReleaseByteArrayElements(jBin, bin, 0);
error_exit1:
// ファイルクローズ
close(fd);
error_exit0:
return err;
}
Androidのファイルシステムの中身は adb shell コマンドでシェルに入ることで確認できる。プログラムを実行すると、/data/data/test.filetestフォルダには確かにファイルができていた。
# cd /data/data/test.filetest
cd /data/data/test.filetest
# ls -l
ls -l
-rwxr-xrwT app_28 app_28 256 2010-09-09 13:31 hello.dat
drwxr-xr-x system system 2010-09-09 13:23 lib
その上位のフォルダ一覧を見てみると、フォルダ(パッケージ名)ごとに所有者やパーミションがついているのがわかる。アプリごとに所有者が違うということはAndroidはこの仕組みを使ってアプリごとのアクセス管理をしているのだろうか?
# cd /data/data
cd /data/data
# ls -l
ls -l
drwxr-xr-x app_28 app_28 2010-09-09 13:23 test.filetest
drwxr-xr-x app_24 app_24 2010-07-14 12:15 jp.hews.intent
drwxr-xr-x app_21 app_21 2010-01-27 13:32 com.android.mms
drwxr-xr-x app_5 app_5 2010-01-27 13:32 com.android.camera
drwxr-xr-x app_4 app_4 2010-01-27 13:32 com.android.email
...
2010/9/27追記:
openの際に O_CREAT が指定されている場合にはmode(ファイルパーミション)指定が必要のため追加