ListView+SimpleCursorAdapter練習

練習に郵便番号をリストビューに表示する。
元データはこちら。
http://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html

ListView, SimpleCursorAdapter, SQLiteOpenHelper, AssetManager, CSVReaderあたり。

プロジェクト生成

プロジェクト名zipcodeとした。

$ mvn archetype:generate -DarchetypeArtifactId=android-quickstart -DarchetypeGroupId=de.akquinet.android.archetes
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] Archetype [de.akquinet.android.archetypes:android-quickstart:1.0.6] found in catalog remote
Define value for property 'groupId': : jp.ousttrue.postcode
Define value for property 'artifactId': : postcode
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  jp.ousttrue.postcode: :
[INFO] Using property: android-plugin-version = 2.8.4
[INFO] Using property: emulator = not-specified
[INFO] Using property: platform = 7
Confirm properties configuration:
groupId: jp.ousttrue.zipcode
artifactId: zipcode
version: 1.0-SNAPSHOT
package: jp.ousttrue.zipcode
android-plugin-version: 2.8.4
emulator: not-specified
platform: 7
 Y: :

適当にgitとかに登録しながら進める。

レイアウト

レイアウト。リストがあれば十分。
res/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"
    >
<ListView  
    android:id="@+id/list" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    />
</LinearLayout>

行のレイアウト。郵便番号と住所を表示する想定。
res/layout/row.xml

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

とりあえずビルド。

$ mvn install android:deploy

まだ空のリストがあるだけなので真っ黒になるだけ。

DBのフィールドを決める

http://www.post.japanpost.jp/zipcode/dl/readme.html
を見て1列から9列までを格納することにした。

CREATE TABLE IF NOT EXISTS zipcode (
    _id integer primary key autoincrement,
    jis integer,
    old_zipcode text,
    zipcode text,
    prefectureKana text,
    cityKana text,
    townKana text,
    prefecture text,
    city text,
    town text
);

実装

src/main/java/jp/ousttrue/zipcode/HelloAndroidActivity.java

src/main/java/jp/ousttrue/zipcode/ZipcodeActivity.java
に変える。
AndroidManifest.xml

<activity android:name=".ZipcodeActivity">

ZipcodeActivity.java

package jp.ousttrue.zipcode;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.Cursor;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.content.Context;
import android.content.res.AssetManager;
import au.com.bytecode.opencsv.CSVReader;
import android.content.ContentValues;


public class ZipcodeActivity extends Activity {

    private static String TAG = "zipcode";

    /**
     * Called when the activity is first created.
     * @param savedInstanceState If the activity is being re-initialized after
     * previously being shut down then this Bundle contains the data it most
     * recently supplied in onSaveInstanceState(Bundle). <b>Note: Otherwise it is null.</b>
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "onCreate");
        setContentView(R.layout.main);

        // db
        ZipcodeOpenHelper helper =
            new ZipcodeOpenHelper(getApplicationContext(), "zpicode.db", 6);
        SQLiteDatabase db = helper.getReadableDatabase();

        // adapter
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), R.layout.row,
                db.query("zipcode", null, null, null, null, null, null),
                new String[]{"zipcode", "prefecture"},
                new int[]{R.id.zipcode, R.id.address});
        adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
            @Override
            public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
                Log.d(TAG, "setViewValue: "+columnIndex);
                switch (columnIndex) {
                    case 7:
                        ((TextView)view).setText(
                            cursor.getString(7)+cursor.getString(8)+cursor.getString(9));
                        return true;
                    default:
                        return false;
                }
            }
        });
        ((ListView)findViewById(R.id.list)).setAdapter(adapter);
    }

    class ZipcodeOpenHelper extends SQLiteOpenHelper {
        public ZipcodeOpenHelper(Context context, String name, int version){
            super(context, name, null, version);
        }

        @Override
        public void onCreate(SQLiteDatabase db){
            Log.w(TAG, "create zipcode...");
            db.execSQL(
                    "CREATE TABLE IF NOT EXISTS zipcode ("+
                    "  _id integer primary key autoincrement,"+
                    "  jis integer,"+
                    "  old_zipcode text,"+
                    "  zipcode text,"+
                    "  prefectureKana text,"+
                    "  cityKana text,"+
                    "  townKana text,"+
                    "  prefecture text,"+
                    "  city text,"+
                    "  town text"+
                    ");"
                    );

        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
            Log.w(TAG, "drop zipcode");
            db.execSQL("DROP TABLE IF EXISTS zipcode");
            onCreate(db);
        }
    }
}

とりあえずビルド。

$ mvn install android:deploy

まだDBが空なので真っ黒になるだけ。

assetsからのcsv読み込み

assets/13TOKYO.CSVを追加
はじめはKEN_ALL.CSVを読み込むつもりだったのだが、assetsには1M以上のファイルをおけないそうなので
とりあえずTOKYO13.CSVにした。


pom.xmlにCSVReader追加

        <dependency>
            <groupId>net.sf.opencsv</groupId>
            <artifactId>opencsv</artifactId>
            <version>2.0</version>
        </dependency>
// 追加
import android.content.res.AssetManager;
import au.com.bytecode.opencsv.CSVReader;
import android.content.ContentValues;

// onCreate(SQLiteDatabase db)のCREATE TABLEの後に追記
            AssetManager as = getResources().getAssets();
            java.io.InputStream is;
            try{
                is = as.open("13TOKYO.CSV");
            }
            catch(java.io.IOException e){
                Log.e(TAG, "fail to open csv");
                return;
            }
            CSVReader csv;
            try{
                csv = new CSVReader(new java.io.InputStreamReader(is, "SJIS"));
                Log.d(TAG, "each lines...");
                String[] line;
                while ((line = csv.readNext()) != null) {
                    ContentValues cv = new ContentValues();
                    cv.put("jis", line[0]);
                    cv.put("old_zipcode", line[1]);
                    cv.put("zipcode", line[2]);
                    cv.put("prefectureKana", line[3]);
                    cv.put("cityKana", line[4]);
                    cv.put("townKana", line[5]);
                    cv.put("prefecture", line[6]);
                    cv.put("city", line[7]);
                    cv.put("town", line[8]);
                    db.insert("zipcode", "", cv);
                }
                csv.close();
                is.close();
            }
            catch(java.io.UnsupportedEncodingException e){
                Log.e(TAG, "UnsupportedEncodingException");
                return;
            }
            catch(java.io.IOException e){
                Log.e(TAG, "csv error");
                return;
            }

ビルド。

$ mvn install android:deploy

リスト表示成功。

(追記)assetsからzipファイルの読み込み

import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

            AssetManager as = getResources().getAssets();

            try{
                java.io.InputStream is = as.open("13tokyo.zip");
                Log.d(TAG, "open 13tokyo.zip");
                ZipInputStream zip=new ZipInputStream(is);
                ZipEntry zipEntry;
                while( (zipEntry = zip.getNextEntry()) != null ){
                    Log.d(TAG, "zipEntry: "+zipEntry.getName());
                    CSVReader csv=new CSVReader(new InputStreamReader(zip, "SJIS"));
                    Log.d(TAG, "each lines...");
                    String[] line;
                    while ((line = csv.readNext()) != null) {
                        ContentValues cv = new ContentValues();
                        cv.put("jis", line[0]);
                        cv.put("old_zipcode", line[1]);
                        cv.put("zipcode", line[2]);
                        cv.put("prefectureKana", line[3]);
                        cv.put("cityKana", line[4]);
                        cv.put("townKana", line[5]);
                        cv.put("prefecture", line[6]);
                        cv.put("city", line[7]);
                        cv.put("town", line[8]);
                        db.insert("zipcode", "", cv);
                    }
                    csv.close();
                }
                zip.close();
            }
            catch(java.io.UnsupportedEncodingException e){
                Log.e(TAG, e.getMessage());
                return;
            }
            catch(java.io.IOException e){
                Log.e(TAG, e.getMessage());
                return;
            }