Home > Android
Android Archive
[Android] android.webkit.BrowserCallbackAdapter を m5 バージョンに対応させる
- 2008-04-02 (水)
- Android
Android の SDK が m3 から m5 に変わり、API に大幅な変更が出ていますが、これまで使っていた、BrowserCallbackAdapter が使えなくなってしまったのでそれを変更するメモ。
まず、M5 の Changes Overview のページには、
BrowserCallback has been removed and the functionality has been separated with two new classes: android.webkit.WebViewClient and android.webkit.WebChromeClient.
とあるので、WebViewClient と、WebChromeClient の2つのクラスを使うように変更すればいいみたい。
そもそもやっていたことは、サーバ上の HTML を取得してきた時点で、その HTML の一部を書き換える処理。
BrowserCallback クラスを拡張したクラスを作って、そのクラスを WebView に setBrowserCallback で渡していました。
public class ContactView extends Activity {
private WebView wv ; // WebView
(省略)
/**
* callback Class for loading url
* @author hal
*/
public class MyCallback extends android.webkit.BrowserCallbackAdapter{
(省略)
public boolean overrideUrlLoading(String url){
this.myLoadUrl(url); // manual load
return false;
}
public void myLoadUrl(String url){
// HttpClient クラスを使ってデータを取得して、書き換えて data 変数に格納する処理
(省略)
// WebView へ 表示
wv.loadDataWithBaseURL(url,data, "text/html", method.getResponseCharSet(),null);
}
}
/**
* initialize
*/
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.view_contact);
// WebKitのViewを取得
wv = (WebView) findViewById(R.id.web);
// コールバック用クラスを設定
cb = new MyCallback();
wv.setBrowserCallback(cb);
// JavaScript の利用を許可
wv.getSettings().setJavaScriptEnabled(true);
}
(省略)
}
という処理ですね。
で、BrowserCallbackAdapter が使えなくなり、WebViewClient と WebChromeClient に分割され使いかたも変わっています。WebViewClient が、キーイベントと URL ロード関連のイベントリスナ、WebChromeClient が ウィンドウ関連のイベントリスナになっている模様。
WebViewClient の リファレンスを見ると、shouldOverrideUrlLoading というメソッドがあるので、それを使えばいいのかな。第一引数に WebView のインスタンスが渡ってくるようです。
今回の場合、 BrowserCallbackAdapter の代わりに WebViewClient を継承したクラスを、 WebView::setWebViewClient でセットしてあげれば良さそう。
というわけで、以下のように変更してみたところ無事動きました。
public class ContactView extends Activity {
(省略)
/**
* callback Class for loading url
* @author hal
*/
public class MyCallback extends android.webkit.WebViewClient{
(省略)
/**
* Handling method called when webkit loading url
* @param wv WebView class instance
* @param url loading url
*/
public boolean shouldOverrideUrlLoading(WebView wv,String url){
Log.d(TAG, "load start:" + url);
this.myLoadUrl(wv,url); // manual load
return false;
}
public void myLoadUrl(String url){
// HttpClient クラスを使ってデータを取得して、書き換えて data 変数に格納する処理
(省略)
wv.loadDataWithBaseURL(url,data, "text/html", method.getResponseCharSet(),null);
}
}
/**
* initialize
*/
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.view_contact);
// WebKitのViewを取得
wv = (WebView) findViewById(R.id.web);
// コールバック用クラスを設定
cb = new MyCallback();
wv.setWebViewClient(cb);
// JavaScript の利用を許可
wv.getSettings().setJavaScriptEnabled(true);
}
(省略)
}
実際には、バージョンアップに伴い AndroidManifest.xml やら layout 用の xml やらも修正する必要がありましたが本筋ではない部分なので割愛しました。
[Android] WebKit で JavaScript ブリッジ
- 2007-12-25 (火)
- Android
JavaScript ブリッジを使って、 Android のオブジェクトをリモート側で操作してみましたのでまとめ。
WebKit とは、Android 上で 動作する Webブラウザです。
これを使ってリモートサイトの HTML を表示し、Android 側のコンタクトリストを 表示させてみます。
WebKit を使うには、WebView を layout/main.xml に定義します。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello World, WebKitTest"
/>
<WebView
id="@+id/web"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
onCreate で、WebView に対して 読み込む URL を指定します。
wv.addJavascriptInterface(user, "user");
で、後ほど出てくる UserObj クラスのインスタンスを サーバ側の JavaScript で操作できるようにしています。
JavaScript では、user という変数名で参照できます。
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// レイアウト用の xml を設定
setContentView(R.layout.main);
// バインド用の xml ファイルを設定
UserObj user = new UserObj();
// WebKitのViewを取得
WebView wv = (WebView) findViewById(R.id.web);
// ブリッジ設定
wv.addJavascriptInterface(user, "user");
// JavaScript の利用を許可
wv.getSettings().setJavaScriptEnabled(true);
// 最初のページを表示
wv.loadUrl("http://www.plants-web.jp/android/index.html");
}
UserObj クラスは、自分のUserID を返却する getUserId() というメソッドと、コンタクトリスト内の文字列を配列で返す getContactList() というメソッドが定義されています。
コンタクトリスト内のデータを取得する方法は省略しますが、以前のエントリを参考にしてください。
/**
* ユーザの情報をサーバ側へ渡すためのブリッジクラス
* @author hal
*
*/
class UserObj{
/**
* ユーザIDを返却する
* @return ユーザID
*/
public String getUserId(){
return "useridA";
}
/**
* コンタクトリストを返却する
* @return コンタクトリスト
*/
public List<String> getContactList(){
ArrayList<String> items = new ArrayList<String>();
// コンタクトリストから取得するカラムを指定。とりあえず全部取っとく
・・・(省略)・・・
return items;
}
}
サーバ側の処理を書きます。wv.addJavascriptInterface(user, "user"); でブリッジを設定していますので、UserObj クラスのメソッド、user.getUserId() などが利用できます。
<html>
<body>
test
<hr>
<script type="text/javascript">
document.write("UserID:" + user.getUserId());
document.write("<hr>[Contacts]<br>");
contacts=user.getContactList();
for(i = 0; i < contacts.size(); i++) {
document.write(contacts.get(i) + "<br>");
}
</script>
<hr>
<a href="page2.html">page2</a>
</body>
</html>
これを実行すると、以下のような画面が表示されます。
"test"行以下が、リモートサーバから取得されたHTML ファイル。
"UserID:UserIdA"部分がユーザID、Contacts から hr までが、実際にAndroid 内に入っていたコンタクトリストになります。

このように、JavaScript ブリッジを使うことにより、リモートサーバ側のロジックで Android 内のデータを操作することが可能となります。
また、page2.html へのリンクがありますが、これを選択すると、page2.html が表示されます。
現状のコードでは、全画面にpage2.html の内容が表示され、Hello World ... 部分などは消えてしまいます。
このまま、同じレイアウト内でページを遷移させる方法もありますが、長くなったのでそれは次回で。
Android の GUI デザインツール
- 2007-12-17 (月)
- Android
Android で、View 部分を作る為の ツールについて。
Android の View を作る際には、res/layout/ 以下に view 用の xml ファイルを作成し、Activity クラスから setContentView で呼び出します。
ただし、レイアウトを xml ファイルで作成するのは結構大変。
GUI でデザインレイアウトを作成する"DroidDraw"というサービスが公開されていますので、使ってみました。
使いかたは簡単。左にある Screen というキャンバスに、右にあるパレットから選んだ部品をドラッグしてレイアウトをしていくだけです。
部品には、ボタンなどの各種コンポーネントにあたる Widgets、 LinearLayout などのレイアウト用の部品である Layouts、の二種類があります。
配置した部品は、選択後 Properties タブを選択することで サイズや表示テキストなどの設定をすることができます。
レイアウト作成後 Generate ボタンを押すと、Output 欄に xml が出力されますので、それをそのまま レイアウト用の xml ファイルに利用することができます。
ダウンロードして利用できる java アプリもありますので、僕はそちらを使っています。
[Android] オリジナルの ContentProvider を作成する
前回は ContentProvider クラスを使って コンタクトリスト から電話帳データ を取得してみました。
今回はオリジナルの ContentProvider を作ってみようと思います。
ContentProvider クラスを使い実装することで、他のアプリケーションからもデータを利用することが可能となります。
データへのアクセスは、ContentURI という URI を用い、

という形式でアクセスすることになります。
A :固定の プリフィックス
B :Authority パート。一意であることを保証する為、ContentProvider のパッケージ名を含んだクラス名とします。
AndroidManifest.xml の
C :ContentProvider が操作するデータの種類を示します。
D :データのIDを示します。省略すると全てのレコードを取得します。
となっています。
今回はGoogle が提供しているチュートリアルで作ったNotepadv3 アプリを改造します。
Notepadv3ではデータの保存に SQLite を使っていますが、これを ContentProvider を使うように変更してみましょう。
Notepadv3 プロジェクトをコピーして Notepadv4 プロジェクトを作成
既存のものをディレクトリ毎複製したあと "v3" となっている部分を "v4" に書き換えて "create project from existing source" を選択してプロジェクト作成しました。 この時点で、エミュレータ で動作させてちゃんと表示されればOK。NotesProvider クラスで操作するデータを表現するクラスを作成
Provider を作る前に、データを表現する為の NotePad クラスを作成しておきましょう。 Notes は、DB 上では _id,title,body という3つのカラムを持ちます。com.google.android.demo.provider パッケージを新しく作り、そこに以下のクラスを作ります。
クラス内には、データのパスを示す ContentURI やカラムを示す 定数を指定します。
BaseColumns を継承することにより、_id にあたる部分は省略できます。
package com.google.android.demo.provider;
import android.net.ContentURI;
import android.provider.BaseColumns;
public final class NotePad {
public static final class Notes implements BaseColumns {
/**
* このデータへアクセスする為の URI
*/
public static final ContentURI CONTENT_URI
= ContentURI.create("content://com.google.android.demo.provider.NotePad/notes");
/**
* デフォルトのソート順
*/
public static final String DEFAULT_SORT_ORDER = "_id DESC";
/**
* タイトル
*/
public static final String TITLE = "title";
/**
* ノート
*/
public static final String BODY = "body";
}
}
ContentProvider クラスを拡張して、NotesProvider クラスを作成
これまでデータの保存に利用していた DBHelper の代わりになる、NotesProvider クラスを作ります。com.google.android.demo.notepad4 パッケージ内に NotesProvider クラスを作りましょう。
下の図のように、ContentProvider クラスを親クラスにします。

生成された NotesProvider クラスのソースを見てみると、オーバーライドしなくてはいけない関数がいくつかるようです。
public int delete(ContentURI uri, String selection, String[] selectionArgs) public String getType(ContentURI uri) public ContentURI insert(ContentURI uri, ContentValues values) public boolean onCreate() public Cursor query(ContentURI uri, String[] projection, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder) public int update(ContentURI uri, ContentValues values, String selection, String[] selectionArgs)
onCreate :この Provider が生成された時に実行されるメソッドです。
query :データを取得する為のメソッドです。
getType :メソッドは、この Provider で取得できる ContentURI の META タイプを返します。
insert,update,delete :それぞれ データの新規作成時、更新、削除時のメソッドです。
これらの実装は後述します。
DBへのアクセス方法を変更
これまで使っていた DBHelper クラスから、必要な処理を移植します。 まずは、インスタンスが生成された時の初期化処理です。 DBHelper クラスでは、public DBHelper(Context ctx) {
db = ctx.openDatabase(DATABASE_NAME, null);
とあるように、コンストラクタの中から DB へアクセスする為のインスタンスが取得できました。
ContentProvider では、ContentProviderDatabaseHelper を拡張したクラスを使ってDB へのアクセスを行います。
/**
* DB アクセス用のHelper クラス
* @author hal
*/
private static class DatabaseHelper extends ContentProviderDatabaseHelper {
/**
* テーブルのCreate文。Helper が最初に生成された際に発行される
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table notes (_id integer primary key autoincrement, "
+ "title text not null, body text not null);");
}
/**
* アップグレード時に実行される。
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
Notepadv3 では、Database が存在していない場合に Create Database 文を発行していましたが、ContentProviderDatabaseHelper では自動でやってくれるので必要ありません。
残りのメソッドを実装
query,insert,updateなどを実装します。 流れとしては、ContentURIParser クラスの URL_MATCHER を使って渡されてきた URI をチェックし、onCreate で生成した mDB にクエリを発行しています。 query() で使っている QueryBuilder は、 DB へ発行するクエリを生成する為のクラスです。 getType() メソッドは、この Provider を利用して取得できるデータの METAタイプを返します。 NotesProvider クラス全体は以下のようになります。package com.google.android.demo.notepad4;
import java.util.HashMap;
import com.google.android.demo.provider.NotePad;
import android.content.ContentProvider;
import android.content.ContentProviderDatabaseHelper;
import android.content.ContentURIParser;
import android.content.ContentValues;
import android.content.QueryBuilder;
import android.content.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.ContentURI;
import android.provider.BaseColumns;
import android.text.TextUtils;
public class NotesProvider extends ContentProvider {
private SQLiteDatabase mDB;
// URI のパース用
private static final ContentURIParser URL_MATCHER;
/**
* DB アクセス用のHelper クラス
* @author hal
*/
private static class DatabaseHelper extends ContentProviderDatabaseHelper {
/**
* テーブルのCreate文。Helper が最初に生成された際に発行される
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table notes (_id integer primary key autoincrement, "
+ "title text not null, body text not null);");
}
/**
* アップグレード時。
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
}
// DB名
private static final String DATABASE_NAME = "notepad.db";
// テーブル名
private static final String DATABASE_TABLE = "notes";
// DB バージョン
private static final int DATABASE_VERSION = 1;
private static final int NOTES = 1;
private static final int NOTE_ID = 2;
private static final HashMap<String, String> NOTES_LIST_PROJECTION_MAP;
// URL_MATCHER と NOTES_LIST_PROJECTION_MAP を作成
static {
// URI パース用
URL_MATCHER = new ContentURIParser(ContentURIParser.NO_MATCH);
URL_MATCHER.addURI("com.google.android.demo.provider.NotePad", "notes", NOTES);
URL_MATCHER.addURI("com.google.android.demo.provider.NotePad", "notes/#", NOTE_ID);
NOTES_LIST_PROJECTION_MAP = new HashMap<String, String>();
NOTES_LIST_PROJECTION_MAP.put(BaseColumns._ID, "_id");
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.TITLE, "title");
NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.BODY, "body");
}
/**
* 初期化処理
*/
@Override
public boolean onCreate() {
DatabaseHelper dbHelper = new DatabaseHelper();
mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, DATABASE_VERSION);
return (mDB == null) ? false : true;
}
/**
* 削除処理
*/
@Override
public int delete(ContentURI uri, String selection, String[] selectionArgs) {
int count;
switch (URL_MATCHER.match(uri)) {
case NOTES:
count = mDB.delete(DATABASE_TABLE, selection, selectionArgs);
break;
case NOTE_ID:
String segment = uri.getPathSegment(1);
count = mDB
.delete(DATABASE_TABLE, "_id="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* この URIが返すデータ型
*/
@Override
public String getType(ContentURI uri) {
switch (URL_MATCHER.match(uri)) {
case NOTES:
return "vnd.android.cursor.dir/vnd.google.note";
case NOTE_ID:
return "vnd.android.cursor.item/vnd.google.note";
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
@Override
/**
* データの挿入
*/
public ContentURI insert(ContentURI uri, ContentValues initialValues) {
long rowID;
ContentValues values;
// データがなければ新規に作る
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
// 正しいURI かどうかチェック
if (URL_MATCHER.match(uri) != NOTES) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
Resources r = Resources.getSystem();
// タイトル設定
if (values.containsKey(NotePad.Notes.TITLE) == false) {
values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled));
}
// 本文設定
if (values.containsKey(NotePad.Notes.BODY) == false) {
values.put(NotePad.Notes.BODY, "");
}
// 挿入
rowID = mDB.insert(DATABASE_TABLE, "note", values);
if (rowID > 0) {
ContentURI newuri = NotePad.Notes.CONTENT_URI.addId(rowID);
// 変更を通知
getContext().getContentResolver().notifyChange(uri, null);
return newuri;
}
throw new SQLException("Failed to insert row into " + uri);
}
/**
* データの取得
*/
@Override
public Cursor query(ContentURI uri, String[] projection, String selection,
String[] selectionArgs, String groupBy, String having,
String sortOrder) {
QueryBuilder qb = new QueryBuilder();
switch (URL_MATCHER.match(uri)) {
case NOTES:
qb.setTables(DATABASE_TABLE);
qb.setProjectionMap(NOTES_LIST_PROJECTION_MAP);
break;
case NOTE_ID:
qb.setTables(DATABASE_TABLE);
qb.appendWhere("_id=" + uri.getPathSegment(1));
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
// ソート順がなければデフォルトのものを使う
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = NotePad.Notes.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// カーソル作成
Cursor c = qb.query(mDB, projection, selection, selectionArgs, groupBy,
having, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
/**
* 更新処理
*/
@Override
public int update(ContentURI uri, ContentValues values, String selection,
String[] selectionArgs) {
int count;
switch (URL_MATCHER.match(uri)) {
// 全て
case NOTES:
count = mDB.update(DATABASE_TABLE, values, selection, selectionArgs);
break;
// ID 指定
case NOTE_ID:
String segment = uri.getPathSegment(1);
count = mDB.update(DATABASE_TABLE, values, "_id="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
// 更新を通知
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}
Naotepadv4 クラスの修正
DBHelper ではなく、ContentProvider クラスを利用するように変更します。データを取得するのに、前回の例と同じように managedQuery メソッド が使えるようになっていると思います。
getIntent().setData(NotePad.Notes.CONTENT_URI);
cur = managedQuery(getIntent().getData(), PROJECTION, null, null);
の部分ですね。
また、編集時に編集画面へデータを渡すには、
ContentURI uri = getIntent().getData().addId({編集したい行の_ID});
startSubActivity(new Intent(Intent.EDIT_ACTION, uri), ACTIVITY_EDIT);
のように、編集したい行の URI を示す ContentURI クラスを渡します。
今回から、startSubActivity へ渡す Intent には、Intent.INSERT_ACTION や Intent.EDIT_ACTION という定数を渡しています。
このように、Intent で ACTION を指定する場合には、AndroidManifest.xml への記述が必要となります。
AndroidManifest.xml については、後述します。
package com.google.android.demo.notepad4;
import java.util.ArrayList;
import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.net.ContentURI;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.view.Menu;
import android.view.View;
import android.view.Menu.Item;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.google.android.demo.provider.NotePad;
public class Notepadv4 extends ListActivity
{
private static final int ACTIVITY_CREATE=0;
private static final int ACTIVITY_EDIT=1;
private static final int INSERT_ID = Menu.FIRST;
private static final int DELETE_ID = Menu.FIRST + 1;
private Cursor cur;
private ArrayList<Long> mItems ;
/**
* DB から取得したい 項目
*/
private static final String[] PROJECTION = new String[] {
BaseColumns._ID, NotePad.Notes.TITLE};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.notes_list);
fillData();
}
// データ表示
private void fillData() {
// クエリ発行
getIntent().setData(NotePad.Notes.CONTENT_URI);
cur = managedQuery(getIntent().getData(), PROJECTION, null, null);
// ID保持用
mItems = new ArrayList<Long>();
// タイトル保持用
ArrayList<String> items = new ArrayList<String>();
int titleIndex = cur.getColumnIndex(NotePad.Notes.TITLE);
// データ取得
while (cur.next()){
mItems.add(cur.getLong(cur.getColumnIndex(NotePad.Notes._ID)));
items.add(cur.getString(titleIndex));
}
// リスト作成
ArrayAdapter<String> notes =
new ArrayAdapter<String>(this, R.layout.notes_row, items);
setListAdapter(notes);
}
/**
* オプションメニュー生成
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, INSERT_ID, R.string.menu_insert);
menu.add(0, DELETE_ID, R.string.menu_delete);
return true;
}
/**
* メニュー選択時
*/
@Override
public boolean onMenuItemSelected(int featureId, Item item) {
super.onMenuItemSelected(featureId, item);
switch(item.getId()) {
case INSERT_ID:
Intent i = new Intent(Intent.INSERT_ACTION, getIntent().getData());
startSubActivity(i, ACTIVITY_CREATE);
break;
case DELETE_ID:
// データ削除
cur.moveTo(getSelection());
cur.deleteRow();
fillData();
break;
}
return true;
}
/**
* リストアイテムクリック
*/
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
ContentURI uri = getIntent().getData().addId(mItems.get((int)getSelectionRowID()));
startSubActivity(new Intent(Intent.EDIT_ACTION, uri), ACTIVITY_EDIT);
}
/**
* SubActivity 終了時
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, String data, Bundle extras) {
super.onActivityResult(requestCode, resultCode, data, extras);
fillData();
}
}
NoteEdit.java の変更
NoteEdit.java 側も変更を加えます。package com.google.android.demo.notepad4;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.ContentURI;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.google.android.demo.provider.NotePad;
public class NoteEdit extends Activity {
private EditText titleText;
private EditText bodyText;
private Cursor cur ;
private ContentURI mURI;
private static final String[] PROJECTION = new String[] {
BaseColumns._ID, NotePad.Notes.TITLE, NotePad.Notes.BODY};
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.note_edit);
titleText = (EditText) findViewById(R.id.title);
bodyText = (EditText) findViewById(R.id.body);
Button confirmButton = (Button) findViewById(R.id.confirm);
final Intent intent = getIntent();
final String action = intent.getAction();
if (action.equals(Intent.INSERT_ACTION)) {
// ContentResolver にURI を渡し、新規のカーソルを作成する
mURI = getContentResolver().insert(intent.getData(), null);
// エラー
if (mURI == null) {
Log.e("Notes", "Failed to insert new note into "
+ getIntent().getData());
finish();
return;
}
// キャンセルボタンなどを実装したい場合、setResult には RESULT_CANCEL を渡すこと
setResult(RESULT_OK, mURI.toString());
}else if (action.equals(Intent.EDIT_ACTION)) {
// 編集時
mURI = intent.getData();
setResult(RESULT_OK, mURI.toString());
}
// クエリを発行
cur = managedQuery(mURI, PROJECTION, null, null);
// データ表示
populateField();
// Submit ボタンクリック時のイベント
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
setResult(RESULT_OK);
finish();
}
});
}
/**
* アクティビティの終了、一時停止時
*/
@Override
protected void onPause() {
// 使っているリソースを開放する
super.onPause();
saveState();
}
/**
* Pause 状態からの復旧時
*/
@Override
protected void onResume() {
// データを復旧
super.onResume();
populateField();
}
/**
* データ保存用のメソッド
*/
private void saveState(){
String title = titleText.getText().toString();
String body = bodyText.getText().toString();
// cur.updateString で、データを更新
if (cur != null) {
cur.updateString(cur.getColumnIndex(NotePad.Notes.TITLE), title);
cur.updateString(cur.getColumnIndex(NotePad.Notes.BODY), body);
managedCommitUpdates(cur);
}
}
/**
* 編集用のフィールド表示
*/
private void populateField() {
if (cur != null) {
cur.first();
String title = cur.getString(cur.getColumnIndex(NotePad.Notes.TITLE));
titleText.setText(title);
String body = cur.getString(cur.getColumnIndex(NotePad.Notes.BODY));
bodyText.setText(body);
}
}
}
AndroidManifest.xml の更新
最後に、IntentFilter や provider の設定を、AndroidManifest.xml に記述します。<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.demo.notepad4">
<application android:icon="@drawable/icon"
android:theme="@android:style/Theme.Dark">
<provider class="NotesProvider" android:authorities="com.google.android.demo.provider.NotePad" />
<activity class=".Notepadv4" android:label="@string/app_name">
<intent-filter>
<action android:value="android.intent.action.MAIN" />
<action android:value="android.intent.action.INSERT" />
<action android:value="android.intent.action.EDIT" />
<category android:value="android.intent.category.LAUNCHER" />
<type android:value="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
</activity>
<activity class=".NoteEdit" android:label="@string/edit_note">
<intent-filter>
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.dir/vnd.google.note" />
<type android:value="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
</activity>
</application>
</manifest>
<provider class="NotesProvider" android:authorities="com.google.android.demo.provider.NotePad" />
の部分が、今回作った ContentProvider を使う為の記述です。authorities 属性の value 値が、ContentURI に対応しているのがわかると思います。
<intent-filter>内の
<action android:value="android.intent.action.INSERT" />
<action android:value="android.intent.action.EDIT" />
は、Intent.EDIT_ACTION などを 別の Activity に渡す際に必要な記述になります。
<type android:value="vnd.android.cursor.dir/vnd.google.note" />
部分は、Provider の getType() に対応しています。
以上の変更を加えることで、オリジナルの ContentProvider を利用できるようになっています。
RUN してみると、これまでと同じように NotePad アプリケーションが動作すると思います。
今回はソースをまるまる貼り付けているので、かなりエントリが長くなってしまいました。
[Android] ContentProvider を使って コンタクトリストのデータを取得する
Android の ContentProvider クラス について。
チュートリアルにある NotePad アプリケーションではデータの保存に SQLite を使っていますが、
For this exercise, we are just going to use a SQLite database directly to store our data, but in a real application it would be much better to write a proper ContentProvider to encapsulate this behavior instead.If you are interested, you can find out more about content providers or the whole subject of Storing, Retrieving, and Exposing Data.
とあるように、実際アプリケーションを作る場合は ContentProvider を使う方がいいようです。
というわけで、以下のURL を参考に ContentProvider を使ってみました。
http://code.google.com/android/devel/data/contentproviders.html
まずは、サンプルコードにあるように、アドレス帳のデータを取り出してリスト形式で表示してみることにします。
1. 新しい Android プロジェクトを作る。
プロジェクト名、アプリケーション名は ContactView にしました。
2. layout 用の xml ファイルを作成。
main.xml に ListView を追加
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView id="@+id/android:list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView id="@+id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Contacts!"/>
</LinearLayout>
リスト内の表示用に、contacts_row.xml を追加
<?xml version="1.0" encoding="utf-8"?>
<TextView id="@+id/text1" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
3. コンタクトリストからデータを表示するプログラムを書きます。
package jp.plantsweb.android.contact;
import java.util.ArrayList;
import java.util.List;
import android.app.ListActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.ArrayAdapter;
public class ContactList extends ListActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
fillData();
}
/**
* コンタクトデータを取得して表示
*/
private void fillData(){
List items = new ArrayList();
// コンタクトリストから取得するカラムを指定
String[] projection = new String[] {
android.provider.BaseColumns._ID,
android.provider.Contacts.PeopleColumns.NAME
};
// クエリを発行する
Cursor cur = managedQuery( android.provider.Contacts.Phones.CONTENT_URI,
projection, // 取得するカラムを指定
null, // WHERE句にあたる部分。全部必要なのでnull
android.provider.Contacts.PeopleColumns.NAME + " ASC"); // Order by
String name;
// データ取得用のインデックスを取得する
int nameColumn = cur.getColumnIndex(android.provider.Contacts.PeopleColumns.NAME);
// データ取得開始
while (cur.next()) {
// データ取得
name = cur.getString(nameColumn);
// 表示用の配列に追加
items.add(name);
}
// アダプタを介してデータ表示
ArrayAdapter contacts = new ArrayAdapter(
this, R.layout.contacts_row, items);
setListAdapter(contacts);
}
}
Run してみたところ、SecurityException が発生しました。
デフォルトではコンタクトリストには許可されたアプリケーションしかアクセスできない模様です。
4. パーミッションの設定をする
セキュリティの章に答えがありました。 http://code.google.com/android/devel/security.html
保護されているデータにアクセスする場合、AndroidManifest.xml に、
<uses-permission id="android.permission.RECEIVE_SMS" />
というような宣言を書く必要があるようです。
パーミッションの一覧が以下の URL にあり、
http://code.google.com/android/reference/android/Manifest.permission.html
READ_CONTACT というパーミッションがありました。AndoroidManifest.xml に
<uses-permission id="android.permission.READ_CONTACTS" />
という行を追加して RUN してみたところ、無事コンタクトリストの内容が表示されました!
しかし、コンタクトリストには2名しか登録していないはずですが、4行になっていますね。調べてみたところ、ContactList 内のデータは、携帯と自宅と登録されている場合は2行分に分かれているようです。
次回はオリジナルの ContentProvider を作ってみようと思います。
Home > Android
