Tips

Androidアプリの4大要素 ContentProvider 【Android Tips】

ContentProviderを利用したE-mailアドレス一覧取得アプリ
では、ContentProviderを利用して、コンタクトリストに登録されているE-mailアドレスを一覧表示してみましょう。


ListViewのアイテムをクリックすると、Toastが表示されます。

 

まずは、MainActivityのレイアウトXMLファイルです。
“res/layout/activity_main.xml”

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >
 
    <ListView
        android:id="@+id/listView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
    </ListView>
 
</LinearLayout>

ListViewを使用するため、LinearLayout内に定義しました。

 

次に、ListVIew内のアイテムに使用するレイアウトです。
“res/layout/list_item.xml”

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
    <TextView
        android:id="@+id/text1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp" />
 
</LinearLayout>

こちらもLinearLayoutの中に定義されているのはTextViewだけです。

 

次に、MainActivityのソースプログラムです。
“src/com.example.service/MainActivity.java”

package com.example.contentprovider;
// 中略
public class MainActivity extends Activity implements OnItemClickListener{
    private List<String> emails = new ArrayList<String>();
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        disp();
    }

    private void disp(){
        emails = new ArrayList<String>();
        ContentResolver resolver = getContentResolver();
 
        Uri uri = Email.CONTENT_URI;
        String[] projection = new String[] { Email.DATA1 };
        String order = Email.DATA1 + " ASC";
        Cursor cur = resolver.query(uri, projection, null, null, order);
 
        if (cur.moveToFirst()) {
            int emailColumn = cur.getColumnIndex(Email.DATA1);
 
            do {
                emails.add(cur.getString(emailColumn));
            } while (cur.moveToNext());
        }
 
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                R.layout.list_item, R.id.text1, emails);
 
        ListView listView1 = (ListView) findViewById(R.id.listView1);
        listView1.setAdapter(adapter);
        listView1.setOnItemClickListener(this);
    }
 
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(this, emails.get(position), Toast.LENGTH_SHORT).show();
    }
}

13行目でonResume()メソッドをオーバーライドしています。
このメソッドはActivityがフォアグラウンドになるときに実行されるメソッドで、この中でdisp()メソッドを呼び出しています。

18行目からはdisp()メソッドを定義しています。
インスタンス変数である“emails”を初期化した後、ContentProviderを用いてデータ取得を行います。
20行目から33行目までのプログラムコードはこの節で紹介した使用例とほとんど同じですね。
違うポイントとしては、31行目でContentProviderから取得したデータをdisp()メソッドの最初で初期化した“emails”に追加しているところです。
このArrayListがListViewに表示するデータとして使われます。

 

35行目から40行目まではlist_item.xmlと上で作成したArrayListを用いてAdapterを作成し、activity_main.xml上のListViewと紐付を行っています。

 

40行目ではListViewオブジェクトにOnItemClickListenerオブジェクトをリスナーとして設定しています。
MainActivityに対してOnItemClickListenerインタフェースが実装されているところにも注目してください。

43行目から46行目では、onItemClick()メソッドをオーバーライドし、ListViewのアイテムをクリックしたときの挙動を定義しています。
ここでは、クリックしたItemをToastで出力しているだけです。

 

最後にマニフェストファイルです。
“AndroidManifest.xml”

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.emailcontentprovider"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="15" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

追加しなくてはいけないのは9行目です。
コンタクトリストからデータを取得するには、パーミッションの設定が必要になります。

 

このアプリのポイントはコンタクトリストからデータを取得し、表示するロジック「disp()メソッド」がonResume()メソッドで呼び出されているという点です。

では、disp()メソッドをonCreate()メソッドで呼び出したらどうなるのでしょうか?
この場合、MainActivityが表示しているアドレス帳が最新状態であるという保証がなくなってしまいます。

 

disp()メソッドをonCreate()メソッド内に移動し、実行してみましょう。
そのあと、いったんアプリを終了し、“コンタクトリスト“アプリを起動させ、E-Mailデータの修正をします。
そして、このアプリに戻ってきてください。

 

コンタクトリストで修正した内容が反映していないですよね。
これは、Activityのライフサイクルによるものです。
Activityがフォアグラウンドに遷移した際に呼び出されるのはonResume()メソッドですから、修正したアプリではアプリに戻ってきたときにdisp()メソッドが再実行されません。
その結果として、コンタクトリストで修正した内容が反映していないという状態になってしまいます。
修正前のアプリのようにonResume()メソッド内でdisp()メソッドを実行することで、フォアグラウンドに戻った際にdisp()メソッドが再実行され、Cursorオブジェクトが最新化、Adapterに反映されることになります。

 

別の方法として、Contextクラスのquery()メソッドではなく、ActivityクラスのmanagedQuery()メソッドを利用するという方法もあります。
managedQuery()メソッドを使用すれば、自動的にCursorのアンロードと再読み込みを行ってくれます。
ですので、onCreate()メソッド内でdisp()メソッドを実行したとしてもCursorオブジェクトは常に最新状態となることが保障されます。
(この方法を用いればたしかにCursorは最新状態となりますが、Adapterは最新化されません。表示されるデータが常に最新となることを保証するには、ArrayAdapterではなくCursorAdapterを使うようにしなくてはなりません。)

Androidアプリ開発の必須知識!JAVAプログラミングを学べる連載リンク

はじめてのJAVA 連載

Recent News

Recent Tips

Tag Search