My Photo

November 18, 2012

Androidアプリ開発メモ068:Tweenアニメーション

Viewに対して回転やサイズ変更などをすることでアニメーションを作成する。
下記のクラスを利用できるが、クラスを直接利用するのではなくXMLでリソースファイルとして定義してプログラムに読み込んで利用することがほとんど。

クラス説明XML要素
AlphaAnimationアルファ値変更<alpha>
RotateAnimation回転<rotate>
ScaleAnimation拡大・縮小<scale>
TranslateAnimation移動・変形<translate>

上表のクラスは android.view.animation.Animationクラスを継承している。

アニメーションの定義の例。下記の定義はだんだん濃くなりながら回転していくアニメーションである。
ファイル名は制限はない。例えば tween.xml とかにしておく。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator">
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
           android:duration="3000" />
    <rotate android:fromDegrees="0" android:toDegrees="360"
            android:pivotX="50%" android:pivotY="50%"
            android:duration="3000" />
</set></pre>

android:interpolator属性はアニメーションの変化の割合を指定する。値は下表参照。

属性説明
@android:anim/accelerate_interpolator動きがだんだん速くなる
@android:anim/accelerate_decelerate_interpolator動きがだんだん遅くなる

属性にはalpha、rotate、scale、translateの各要素に固有のものと、共通のものがある。それぞれの意味は参考ページを参照。

下記は1秒で縦に縮んで、2秒で元に戻るアニメーションの例。
android:startOffset属性を使って、前半のscale要素で定義した動きが終わってから後半のscale要素で定義した動きが始まるように、後半のscale要素にはandroid:startOffset属性を持たせてある。

<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator"
     android:shareInterpolator="false"
     android:fillAfter="true" >
    <scale android:fromXScale="1.0" android:toXScale="1.0"
           android:fromYScale="1.0" android:toYScale="0.5"
           android:pivotX="50%" android:pivotY="50%"
           android:duration="1000" />
    <set android:interpolator="@android:anim/accelerate_interpolator">
        <scale android:fromXScale="1.0" android:toXScale="1.0"
               android:fromYScale="1.0" android:toYScale="2.0"
               android:pivotX="50%" android:pivotY="50%"
               android:startOffset="1000" android:duration="2000" />
    </set>
</set>

上記の例で注意点が2つ。
まず、アニーメーションが終わった後そのままの状態を保つためにandroid:fillAfter属性をtrueとしているが、この属性はscale要素など個々の動きを定義する要素に設定しても効かない。Tweenアニメーションの定義のトップレベルのset要素に設定しないと意味がないようだ。
また、後半のscale要素のY軸方向の値は1.0~2.0としている。縮めた物体を元に戻すなら0.5~1.0のような気がするが、それでは思ったようなアニメーションにはならない。
scale要素のandroid:fromXScale属性などの値はそこで定義されたアニメーションが始まる時点の大きさが基準となるからである。
0.5~1.0では前半の動きで既に大きさの0.5倍になっているところからさらに0.5倍(元の大きさの0.25倍)からはじまって、1倍(0.5×1.0で元の0.5倍)の大きさで終わってしまう。1.0~2.0なら元の大きさの0.5倍(0.5×1.0)から始まって、元の大きさ(0.5×2.0)となる。

tweenアニメーションのリソースファイルを読み込んで実行する onCreate() の例。

public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	ImageView img = (ImageView)findViewById(R.id.test_image);
	Animation animation = AnimationUtils.loadAnimation(this, R.anim.tween);
	img.startAnimation(animation);
}

参考文献:Androidアプリケーション開発標準資格教科書 10-5 NDKの概要

参考ページ:
7.5.1 アニメーションリソース - ソフトウェア技術ドキュメントを勝手に翻訳
Viewにアニメーションを付与する(Tweenアニメーション) << Tech Booster
Androidアプリのアニメーションリソースの使い方、定義方法 | mucchinのAndroid戦記

Android SDK tools, Revision 21 に更新

Android SDK が更新されたというので SDK Manager を起動してみた。
しかし新しいパッケージはAndroid4.2(API 17)の下に「Google APIs」しか表示されない。あれ?
API 17 のSDK Platform なしに Google API だけをインストールできるはずもなく。SDK Manager を再起動してみたが変わらず。
しばらくして、メニューの Packages に reload っていうのがあるのに気付いて実行したら、無事に SDK Platform やらドキュメントやら他の項目も出てきた。
#気付くのが遅い^^;

Android4.2(API 17)をインストール、更新された項目をアップデートし、Eclipse のADTも更新。
Eclipseを再起動する。
で、メニューの「Android Virtual Device Manager」をクリックしたが、AVDは起動しない。
Eclipse からではなく単独でなら起動する。あら?
Eclipse で適当なプロジェクトを実行してみたらエラー発生。エラーのダイアログには「内部エラーが発生しました。 /com/android/sdklib/ISdkLog」とか出ている。
さっそくgoogleさんに聞いてみると
「eclipse -clean すればいい」
と。
基本ですね^^;

しかし、まだすんなりとは行かない。
コマンドプロンプトを管理者権限で起動し、「start eclipse.exe -clean」とやってみたら、eclipseの画面は出てきたが「応答なし」orz
タスクマネージャでeclipseを殺してもう1回やってみたが、やはり「応答なし」になる。
-cleanオプションを付けずに、普段どおりにショートカットから起動したら立ち上がったが、AVDが起動しない状態はかわらない。うーん。。。

管理者権限がいけないのかと思い、普通にコマンドプロンプトから「eclipse.exe -clean」とやったら今度はちゃんと起動した。
念のため1回終了して、ショートカットからEclipseを起動しなおしたらEclipseからAVDが起動するようになった。プロジェクトの実行もできた。

ふーっ。時間かかった。
まあ、自分がボケていただけだが、毎度、すんなりいきませんなあ。


参考ページ:スマートラボラトリー開発記録

September 10, 2012

Androidアプリ開発メモ067:NDKのサンプルを動かしてみた

NDKのサンプル「com.example.hellojni.HelloJni」を動かしてみた。

WindowsでNDKを利用するにはCygwinが必要というので、まずCygwinをダウンロードしてインストール。Cygwinのバージョンは1.7.16。
#セットアップファイルがsetup.exeって嫌だなあ。ファイル名で何のインストールをするものなのかわからない。
インストールするパッケージはとりあえず参考ページ3を見てgcc4、make、gdblibexpat1を選択。依存関係で必要なものは「これも必要だけどインストールするか?」って聞かれるので一緒に入れる。
インストールが終わってスタートメニューかデスクトップのアイコンから起動すると /home の下にユーザのディレクトリ作られる。
あと、必要かどうかわからないが、Windowsのユーザ環境変数に、名前はHOMEで値は"/home/<ユーザ名>"を追加。

Cygwinのインストールが終わったらNDKをダウンロードして展開。NDKのリビジョンはRevision 8b。
Cygwinの /home/<ユーザ名>、Windowsから見れば <Cygwinのフォルダ>\home\<ユーザ名> に展開。
展開場所はCygwinのフォルダの配下じゃなくてもどこでもいいだんろうが、Cygwin配下ではない場合はパスが "/cygdrive/<ドライブ>/..."となってなんかかっこ悪い。せっかくCygwinを入れたのだからUNIXっぽいパスにしたかったので。
それから.bash_profileのPATHにNDKのディレクトリを追加。
これで準備完了。

Eclipseでサンプルのプロジェクトを作成する。新規>その他で「Android Project from Existing Code」で <NDKのフォルダ>\samples\hello-jni を選択。
アプリとプロジェクトとtestのプロジェクトが出てくる。アプリだけでいいんだろうけど、とりあえず両方選んでみた。
あとは参考サイト1の通り。Cygwinでhello-jniディレクトリに移動し、ndk-buildを実行すると共有ライブラリファイルlibhello-jnai.soが作成さる。

into@note02 ~
$ cd android-ndk-r8b/

into@note02 ~/android-ndk-r8b
$ cd samples/

into@note02 ~/android-ndk-r8b/samples
$ cd hello-jni/

into@note02 ~/android-ndk-r8b/samples/hello-jni
$ ndk-build
Gdbserver      : [arm-linux-androideabi-4.6] libs/armeabi/gdbserver
Gdbsetup       : libs/armeabi/gdb.setup
Cygwin         : Generating dependency file converter script
Compile thumb  : hello-jni <= hello-jni.c
SharedLibrary  : libhello-jni.so
Install        : libhello-jni.so => libs/armeabi/libhello-jni.so

libhello-jnai.so ができたらEclipseでプロジェクトを最新の状態で更新する。
これで通常のAndroidアプリケーションと同様にAVD上や実機上で実行できる。

参考文献:Androidアプリケーション開発標準資格教科書 10-5 NDKの概要

参考ページ:
1.Android NDKサンプルプログラムのビルドについて~その2~: TBヘッドライン
2.AndroidのNDK 1.5でHelloJNIを動かす手順 - Android(アンドロイド)情報-ブリリアントサービス
3.Android NDK開発環境の設定

July 12, 2012

Androidアプリ開発メモ066:Android SDK tools Rev.20 で変わった事

PHP5認定技術初級試験を受けるまで、しばらくAndroidの勉強はほとんどやってなかった。
受験後、Android SDK を更新してみたら色々と変わっているところがあったので、メモしておく。SDK だけじゃなくて Android のWEBサイトとかも。

Android Application Project

まず、EclipseでAndroidアプリのプロジェクトの新規作成が Android プロジェクトから Android Application Project に変更されている。名前が変わっただけではなくウィザードも変わっている。
アイコンを作ることができる。また、作成するアクティビティについて色々設定できるようになっている。
一番最初にプロジェクトを作ったときはウィザードの最後でサポートライブラリを入れるように要求された。サポートライブラリがあれば、Android 3.0 以前でもフラグメントを使えるらしい。それだけじゃないと思うが、現時点で知ってるのはそれだけ^^;

Aap_01 Aap_02 Aap_03 Aap_04 Install_dependencies

作成されるアプリの雛形のコードも色々と変わっている。
アクティビティのコードではその時点では使われていないMenuItemとサポートライブラリがインクルードされている。
また、メソッドは以前は onCreate() だけだったが、onCreateOptionsMenu() が加わっている。ウィザードでオプションメニューを使う/使わないという選択はなかったのだが。

Mainactivity_java

res配下では、以前は画像ファイル用に drawable-ldpi、drawable-mdpi、drawable-hdpi の3つのフォルダがあったが、drawable-xdpi というのが加わった。タブレットなどより高解像度の画面用だろう。
layout には アクティビティ用のレイアウトファイル、menu にはメニュー項目のファイル、value にはがある。レイアウトファイルは以前は main.xml で固定だったが、アクティビティ名に応じたファイル名に変わった。
中身にも変更があって、ルートのレイアウトは以前は LinearLayout だったが RelativeRayout になっている。
メニュー項目のファイルはレイアウトファイルと同じファイル名になっている。
values には strings.xml(文字列リソースファイル)、styles.xml(スタイルファイル)、dimens.xml がある。dimens.xml は中身を見ると長さ(8dpとか16dpとか)が定義されている。
values-large には dimens.xml が定義されている。これはより高解像度の画面のための dimens.xml ということだろうか?
values-v11、values-v14 には styles.xml がある。API LEVEL 11 は Android 3.0.x、API LEVEL 14 は4.0~4.0.2なので、values-v11/styles.xml はAndroid 3.x(タブレット)用のスタイルファイル、values-v14/styles.xml はAndroid 4.x 用のスタイルファイルということだろうか?

Sampleapplicationfile_4

lint

Rev.20 よりもっと前からだと思うが、コードに対するチェックが厳しくなっている。
あらゆるXMLファイルに
「文書に対する文法制約 (DTD または XML スキーマ) が検出されませんでした。」
という警告が出る。また、XMLの要素に対して
「○○要素に××属性がない。」
のような警告がたくさん出るようになった。

Android Developers

WEBサイトのデザインが変わった。
記事へのURLが変わったところもあるのかもれいない。Android 開発ガイド - ソフトウェア技術ドキュメントを勝手に翻訳で画像へのリンクが切れているところがある。

参考ページ:
Y.A.M の 雑記帳: Android Dimension 単位
Android Lint の利用方法を記載 | Bescottee
Androidで互換性の高いアプリを書くための最悪ではない程度のプラクティス - oops

July 11, 2012

Androidアプリ開発メモ065:ライブフォルダ

「ライブフォルダはコンテンツプロバイダの単なるリアルタイムビュー」とドキュメントに書いてある。
表示される情報を見るまでにホーム画面から最低で2クリック必要なので、いまいち便利さがわからない。表示中にデータに変更があるとリアルタイムで表示が更新されるそうだが、それだけ?

アクティビティでは onCreate() でインテントのアクションが ACTION_CREATE_LIVE_FOLDER かどうかを判定し、ACTION_CREATE_LIVE_FOLDER だった場合は setResult() で RESULT_OK と必要なもの(情報の取得先のURIなど)をセットしたインテントを返す。

package com.example.livefolderex;

import android.os.Bundle;
import android.provider.LiveFolders;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;

public class LiveFolderEx extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// インテントのアクションがACTION_CREATE_LIVE_FOLDERかどうかを判定
		Intent intent = getIntent();
		String action = intent.getAction();
		if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
			setResult(
					RESULT_OK,
					creatLiveFolder(
							this, "LiveFolderEx", R.drawable.icon,
							LiveFolders.DISPLAY_MODE_LIST));
		} else {
			setResult(RESULT_CANCELED);
		}
		finish();
	}

	private Intent creatLiveFolder(
			Context context, String name, int icon, int displayMode) {
		
		Intent intent = new Intent();
		intent.setData(LiveFolderProvider.CONTACTS_URI);
		intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
		intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
				Intent.ShortcutIconResource.fromContext(context, icon));
		intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, displayMode);
		return intent;
	}
}

コンテンツプロバイダの方はquery()で表示されるデータを返す。
下記は上記のアクティビティで使っている LiveFolderProvider の抜粋。

// URL
public static final String AUTHORITY = "com.example.livefolderex.test";
public static final Uri CONTACTS_URI = Uri.parse("content://" + AUTHORITY + "/test");

@Override
public boolean onCreate() {
	return true;
}

/** カーソルの列名 */
private static final String[] CURSOR_COLUMNS = new String[] {
	BaseColumns._ID,
	LiveFolders.NAME,
	LiveFolders.DESCRIPTION,
	LiveFolders.INTENT,
	LiveFolders.ICON_PACKAGE,
	LiveFolders.ICON_RESOURCE
};

@Override
public Cursor query(Uri uri, String[] projection, String selection,
		String[] selectionArgs, String sortOrder) {
	// クエリー実行時に呼ばれる
	MatrixCursor mc = new MatrixCursor(CURSOR_COLUMNS);
	
	// マトリックスカーソルへの行追加
	mc.addRow(new Object[] { 
			0,						// ID
			"Webページの表示",		// 名前
			"Webページを表示する",	// 詳細,
			Uri.parse("http://www.google.com/"),	// インテント
			getContext().getPackageName(),	//アイコンパッケージ
			R.drawable.icon
	});
	
	mc.addRow(new Object[] {
			1,							// ID
			"ダイアラーの表示",			// 名前
			"ダイアラーを表示する",		// 詳細
			Uri.parse("tel:117"),			// インテント
			getContext().getPackageName(),	// アイコンパッケージ
			R.drawable.icon
	});
	return mc;
}

AndroidManifest.xml にはアクティビティのインテントフィルタに "android.intent.action.CREATE_LIVE_FOLDER" を、カテゴリに "android.intent.category.DEFAULT" を設定する。これでホーム画面を長押ししてライブフォルダを作成を選んだときの選択肢にこのアプリが表示されるようになる。
本に載っていたサンプルアプリではprovider要素にandroid:multiprocess属性があって値がtrueだった。これは必要なんだろうか?なくてもライブフォルダは表示されるが。
下記は抜粋(application要素)。

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".LiveFolderEx"
        android:label="@string/title_activity_live_folder_ex" >
        <intent-filter>
            <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
    
    <provider
        android:name="LiveFolderProvider"
        android:authorities="com.example.livefolderex.test">
    </provider>
</application>

参考ページ:
f. ライブフォルダ - ソフトウェア技術ドキュメントを勝手に翻訳
Android Developers Blog: Live folders

July 08, 2012

Androidの開発環境壊れたかと思った

数日前、Androidの開発環境を更新した。
SDK Tools がRev.20、SDK Platform-tools がRev.12になって、ADTもそれに合わせて更新。
そしてEclipseを再起動すると、なんかおかしい。
まずEclipseのメニューからAVDが起動しない。AVD選択のダイアログは出るのだが、AVDを選択して開始ボタンを押しても Launch Options の画面が出てこない。AVDがない状態でAndroidプロジェクトを実行すると、AVDは立ち上がるのだが起動途中の画面で止まる。いつまでたってもホーム画面まで行ってくれない。
また、SDKマネージャも起動しない。こちらはうんともすんともいわない。
さらにLogCatのビューがおかしい。「LogCatのビューでつくれないよ。」みたいなエラーメッセージがビューに表示されてログは表示されない。前の2つは回避策(スタートメニューから起動する)があるのだが、LogCatビューが機能しないのは困る。
ADTをインストールしなおしてみたが状況は変わらず。SDKやEclilpseの再インストールは時間がかかりそうなので避けたい。

Eclipseのログを見るとクラスがないとかメソッドがないとかエラーが出ている。
ネットで調べても有用な情報なく、日本Androidの会にでも相談してみようかと思った。しかし、質問する直前に解決方法を見つけた。非常に簡単なことだった。というか、最初に気付けよ!っ て感じ。

解決策は、eclipse に-cleanオプションを付けて起動する。
恥ずかしいorz
日本Androidの会に相談しなくて良かった。バカな質問をするところだった。

たた、なぜか1回だけではだめで、2回やらないといけないかった。
1回目の-cleanオプション付けた起動でLogCatビューは直ったのだが、SDKマネージャとAVDの問題は直らなかった。
もう1回-cleanオプションで起動したら、残りの問題も解決した。

まあとにかく、この度の教訓。
Android SDKとADTを更新したら、次のEclipseの起動時には念のため-cleanオプションを付けろ。何か異常があればもう1回-cleanオプション付けて再起動しろ。

June 12, 2012

Androidアプリ開発メモ064:コンテツプロバイダ その2:コンテンツURI

関連する以前の記事:Androidアプリ開発メモ022:コンテンツプロバイダ

定義済みコンテンツプロバイダ用URI

端末内の様々なデータ、たとえば電話帳のデータなどをコンテツプロバイダから取得できる。そのURIは定数として定義されている。

URI コンテンツ 必要なパーミッション
CallLog.Calls.CONTENT_URI
通話ログ(発着信履歴)
android.permission.READ_CONTACTS
ContactsContract.Contacts.CONTENT_URI
電話帳(1アカウント1レコード)
android.permission.READ_CONTACTS
ContactsContract.Data.CONTENT_URI
電話帳(1データ1レコード:1人の人に自宅電話、携帯電話、勤務先電話が登録されていれば3レコードになる)
android.permission.READ_CONTACTS
ContactsContract.CommonDataKinds.Phone.CONTENT_URI
電話帳に登録してある電話番号
android.permission.READ_CONTACTS
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI
電話帳に登録してある住所
android.permission.READ_CONTACTS
ContactsContract.CommonDataKinds.Email.CONTENT_URI
電話帳に登録してあるメールアドレス
android.permission.READ_CONTACTS
Settings.System.CONTENT_URI
端末の各種設定
Settings.Secure.CONTENT_URI
端末のセキュリティに関する設定
UserDictionary.Words.CONTENT_URI
ユーザー辞書
MediaStore.Images.Media.INTERNAL_CONTENT_URI
端末内のストレージにある画像データ
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
外部メディアにある画像データ
MediaStore.Audio.Media.INTERNAL_CONTENT_URI
端末内のストレージにあるオーディオデータ
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
外部メディアにあるオーディオデータ
MediaStore.Video.Media.INTERNAL_CONTENT_URI
端末内のストレージにあるビデオデータ
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
外部メディアにあるビデオデータ
Browser.BOOKMARKS_URI
ブラウザのブックマーク
com.android.browser.permission.READ_HISTORY_BOOKMARKS
com.android.browser.permission.WRITE_HISTORY_BOOKMARKS

APIDemosの Views>Lists>7.Cursor(Phones) が電話帳から電話番号を取ってきている。ソースコードは List7.java。
以下は電話番号を電話帳から取ってくる自分で作った超適当なサンプルコード。

package com.example.contactssample;

import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.TextView;

public class ContactsSample extends Activity {
	
	private static final String[] PROJECTION = new String[] {
//		ContactsContract.Contacts._ID,
//		ContactsContract.Contacts.DISPLAY_NAME,
//		ContactsContract.Contacts.HAS_PHONE_NUMBER,
//		ContactsContract.Contacts.LOOKUP_KEY
		ContactsContract.CommonDataKinds.Phone._ID,	// _id
		ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,	// display_name
		ContactsContract.CommonDataKinds.Phone.NUMBER,	// data1 電話番号
		ContactsContract.CommonDataKinds.Phone.TYPE,	// data2 電話番号のタイプ(自宅、携帯、etc...)
		ContactsContract.CommonDataKinds.Phone.LABEL,	// data3 ?
	};

	Uri uri
//		= ContactsContract.Contacts.CONTENT_URI;
		= ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		TextView textView1 = (TextView)findViewById(R.id.textView1);
		TextView textView2 = (TextView)findViewById(R.id.textView2);
		
		// Get a cursor with all people
		Cursor cursor = managedQuery(uri,
//				null,	// 何も指定しないと全カラムを取ってくるようだ
				PROJECTION,
				null, null, null);
		
		// 行数、列数をログに出力
		int count = cursor.getCount();
		int columnCount = cursor.getColumnCount();
		Log.v("CP", "count=" + count + ",columnCount=" + columnCount);
		
		// 列名をテキストビュー1に出力
		String[] columnNames = cursor.getColumnNames();
		StringBuffer sb = new StringBuffer();
		for (String s : columnNames) {
			sb.append(s);
			sb.append("/");
		}
		textView1.setText(sb.toString());
		
		// データをテキストビュー2に出力
		sb.delete(0, sb.length());
		while (cursor.moveToNext()) {
			for (int i = 0; i < columnCount; i++) {
				sb.append(cursor.getString(i));
				sb.append("/");
			}
			sb.append("\n");
		}
		textView2.setText(sb.toString());
	}
}

Activity#managedQuery() はdeprecatedになっていた。Android 3.0(API Level 11)以降は CursorLoader ってのを使うのが推奨らしい。

以下のコードはSDカードにあるファイルを取得している。 ContentResolver を使っているがURIが "file://" で始まっている。これはコンテンツプロバイダーを使っているといえるのだろうか?

InputStream is
    = getContentResolver()
        .openInputStream(Uri.parse("file:///sdcard/test.txt"));

参考ページ:
Androidのコンテントプロバイダで使えるURI一覧(1) | mucchinのAndroid戦記
ContactsContractを使った電話帳データの取得 | GE Android Blog
ContentProvider | Tech Booster
メモメモ - [Android]端末の電話帳を読み込む

June 09, 2012

Androidアプリ開発メモ063:タッチモードとフォーカス

関連する以前の記事:Androidアプリ開発メモ015:タッチイベント

タッチモード

Androidの入力モードには「タッチモード」と「タッチモードではないモード(キー入力モード)」がある。
画面をタッチするとタッチモードになり、画面へのタッチ以外の操作(物理キーやトラックボールなど)でViewを選択するとタッチモードを抜ける。
タッチイベントはタッチモードで画面をタッチしたときに発生する。

フォーカス

キーイベントはフォーカスがあるビューに通知される。
Viewがフォーカスを取得できるかどうかの属性は以下の2つ。

focusable
フォーカス可能である。
focusableInTouchMode
タッチモードでフォーカス可能である。

TextView は focusable と focusableInTouchMode ともに false である。トラックボールでもタッチでもフォーカスを移せない。
EditText は focusable と focusableInTouchMode ともに true である。トラックボールでもフォーカスを移せるし、タッチしてもフォーカスを移せる。
Button は focusable は true であるが focusableInTouchMode は false である。トラックボールでフォーカスを移すことができ、その状態で物理キーの決定キーを押せば Button を押せる。画面上の Button をタッチした場合はタッチモードになるので focusableInTouchMode=false である Button にフォーカスは移らない。

focus属性はfocusable属性を含んでいるようで、
Button のように focusable=true、focusableInTouchMode=false はありうるが、
focusable=false、focusableInTouchMode=true はありえない、というか、そう設定してもfocusableの設定が効いてフォーカスを持つことはできない。View#setFocusable() の説明にも合致する。

focusable、focusableInTouchMode はXMLでもViewクラスのメソッドでも設定できる。
独自のViewを作ったときなどを除いて、設定が必要な場合はあまりないと思うが。

レイアウトファイルのでの設定の例。デフォルトと同じように設定しているから意味はないが。
<EditText
android:id="@+id/editText1"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="@string/hello"
  android:inputType="text"
  android:focusable="true"
  android:focusableInTouchMode="true" />

public void setFocusable(boolean focusable)
ビューがフォーカスを受け取れるかどうかを設定する。これをfalseに設定すると、このビューはタッチモードではフォーカス不可能であることが保証される。

public void setFocusableInTouchMode(boolean focusableInTouchMode)
タッチモードにおいてビューがフォーカスを受け取れるかどうかを設定する。これをtrueに設定すると、このビューがフォーカス可能であることを確認する。

public final boolean requestFocus()
特定のビューまたはその子孫のいずれかにフォーカスを与えることを試みるために、これを呼び出します。
戻り値  実際にフォーカスを得たかどうか

参考ページ:
l. タッチモード - ソフトウェア技術ドキュメントを勝手に翻訳
たかがフォーカス,されどフォーカス - 愚鈍人
Y.A.M の 雑記帳: Android Button の色や画像を変える <-フォーカスある/なしのボタンの画像を変えるとか
Android第87回 タッチイベントとは

June 05, 2012

Androidアプリ開発メモ062:国際化、ローカライズ

下記のコードはカレントのロケールの取得とリソースからの文字列の取得を行っている。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Locale locale = getResources().getConfiguration().locale
    String localeName = locale.getLanguage()
    String description = getString(R.string.my_text);
    ...
}

ロケールに応じた処理が必要な場合は上記コード12行目のようにロケールを取得してロケール毎に処理を作りこめばよい。
14行目は文字列リソースを取得している。このコードやレイアウトファイル内の "@string/<リソース名>" は、文字列リソースのXMLファイルを言語に応じたフォルダに置けばロケールに応じて適切な文字列リソースを取得するようになっている。
言語毎のリソースのフォルダはresフォルダの下で、フォルダ名は以下。

言語 フォルダ名
日本語 values-ja
英語 values-en
ドイツ語 values-de
フランス語 values-fr
イタリア語 values-it
ロシア語 values-ru
中国語 values-zh
韓国語 values-ko

参考ページ:
遮蔽空間 Androidアプリ他言語対応
アプリを国際化してAndroid Marketから世界へ発信 (1/2) - @IT

February 02, 2012

Androidアプリ開発メモ061:独自のAlertDialog

関連する記事:Androidアプリ開発メモ046:スタイルとテーマ

AndroidManifest.xmlでテーマを設定するやり方と、レイアウトをXMLファイルで定義してAlertDialog.Builder#setView()でセットするやり方がある。

テーマを設定して背景色を変える

AlertDialogの背景色を設定したテーマを作り、それをAndroidManifest.xml のapplicaaction要素またはactivity要素にセットする。

ダイアログを出すメソッド
/** レイアウトファイルにおいてButton要素のonClick属性に設定されているメソッド。 */
public void showAlertDialogMethod1(View v) {
	Log.v("TEST", "showAlertDialogMethod1():v.getTag()=" + v.getTag());
	MyDialogListener listener = new MyDialogListener();
	AlertDialog.Builder myDialogBuilder = new AlertDialog.Builder(this);
	myDialogBuilder.setTitle("タイトル")
		.setMessage("メッセージ")
		.setPositiveButton("Yes", listener)	// ボタン押下の処理が必要なければ引数listenerはnullでよい
		.setNeutralButton("Maybe", listener)
		.setNegativeButton("No", listener)
		.setCancelable(false);
	AlertDialog myAlertDialog = myDialogBuilder.create();
	myAlertDialog.show();
}

色の定義のファイル res/values/color.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
	<color name="red">#ff0000</color>
	<color name="green">#00ff00</color>
	<color name="blue">#0000ff</color>
	<color name="yellow">#ffff00</color>
	<color name="magenta">#ff00ff</color>
	<color name="cyan">#00ffff</color>
	<color name="pink">#ffc0cb</color>
	<color name="khaki">#f0e68c</color>
	<color name="skyblue">#87ceeb</color>
	<color name="saddlebrown">#8b4513</color>
</resources>

テーマ・スタイルを定義したファイル res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
	<style name="MyTheme" parent="@android:style/Theme.Black">
		<item name="android:alertDialogStyle">@style/AlertDialog</item>
	</style>
	<style name="AlertDialog">
		<item name="android:fullBright">@color/red</item>
		<item name="android:fullDark">@color/green</item>
		<item name="android:topBright">@color/blue</item>
		<item name="android:topDark">@color/yellow</item>
		<item name="android:centerBright">@color/magenta</item>
		<item name="android:centerDark">@color/cyan</item>
		<item name="android:centerMedium">@color/pink</item>
		<item name="android:bottomBright">@color/khaki</item>
		<item name="android:bottomDark">@color/skyblue</item>
		<item name="android:bottomMedium">@color/saddlebrown</item>
	</style>
</resources>

スタイルの定義のitem要素がよくわからない。なんとかBrightとかなんとかDarkとかなんとかMediumとか、それぞれどういう意味なんだか。
上記の設定・コードでアプリを作ってAVD上で出たボタン付きのAlertDialogは下のイメージのようになる。

上からタイトル、メッセージ、ボタンのAlertDialogでは
タイトル部分の背景はtopDarkで指定したyellow、メッセージ部分の背景はcenterDarkで指定したcyan、ボタンの部分の背景はbottomMediumで指定したsaddlebrown。
メッセージ、ボタンのAlertDialogでは
メッセージ部分の背景はtopDarkで指定したyellow、ボタンの部分の背景はbottomMediumで指定したsaddlebrown。
タイトル、リストのAlertDialogでは
タイトル部分の背景はtopDarkで指定したyellow、リスト部分の背景はbottomBrightで指定したkhaki。
タイトル、ラジオボタン付きリスト、ボタンのAlertDialogでは
タイトル部分の背景はtopDarkで指定したyellow、リストの部分の背景はcenterBrightで指定したmagenta、ボタンの部分の背景はbottomMediumで指定したsaddlebrown。
他のitemについては何の設定なのか不明。AlertController.java のソースをちょっと見てみたけどわからない。lightとかdarkってのがあるのでバックライトの点灯状態によって色が変わるとか、あと選択されているときとされていない時とかいろいろ考えたが、どちらも実際の動きを見た限りでは違うような気がする。

setView()で独自のレイアウトにする

独自のダイアログのレイアウトをXMLファイルで作成し、setView() でセットする。setView() の引数はリソースIDではなくビューである。
ビューはLayoutInflater#inflate()で作成する。
このやり方は自由度は高いがボタンの処理などを自前で実装する必要がある。下記のサンプルプログラムの実装で動きはするが、適切な方法かはわからない。

ダイアログを出すメソッド
/**
 * レイアウトファイルでbutton2のonClick属性に設定されているメソッド。
 * ボタンを押すとダイアログのEditTextに入力した値をActivityのTextViewにセットする。
 */
public void showAlertDialogMethod2(View v) {
	//ViewGroup dialogRootLayout = (ViewGroup)findViewById(R.id.dlg2LinearLayout1);
	//ViewGroup dialogRootLayout = (ViewGroup)findViewById(R.id.dlg2Layout2);
	// inflate()の第2引数、nullでいいんだろうか?上の2つのようななにかビューを指定すべき?
	View dialog2 = getLayoutInflater().inflate(R.layout.dialog2, null);
	AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
	dialogBuilder
		.setView(dialog2)
		.setCancelable(false);
	final AlertDialog myAlertDialog = dialogBuilder.create();
	
	final EditText dlg2EditText1 = (EditText)dialog2.findViewById(R.id.dlg2EditText1);
	
	// ダイアログのボタンの取得とリスナの設定
	Button dlg2Button1 = (Button)dialog2.findViewById(R.id.dlg2Button1);
	dlg2Button1.setOnClickListener(new View.OnClickListener() {
		@Override
		public void onClick(View v) {
			aTextView.setText(dlg2EditText1.getText());
			myAlertDialog.cancel();
		}
	});
	
	myAlertDialog.show();
}

ダイアログのレイアウトファイル res/layout/dialog2.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/dlg2LinearLayout1"
	android:orientation="vertical"
	android:background="@color/pink"
	android:layout_width="300dp"
	android:layout_height="200dp">
	<EditText
		android:id="@+id/dlg2EditText1"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content" />
	<Button
		android:id="@+id/dlg2Button1"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="OK" />
</LinearLayout>

参考ページ:Y.A.M の 雑記帳: Android AlertDialog の背景を変更する

より以前の記事一覧

April 2017
Sun Mon Tue Wed Thu Fri Sat
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30            
無料ブログはココログ

日本blog村

  • にほんブログ村 IT技術ブログへ
  • にほんブログ村 アニメブログへ
  • にほんブログ村 サッカーブログ アルビレックス新潟へ

好きな音楽家

メモ

XI-Prof