株式会社ライブキャストロゴ 株式会社ライブキャスト

Android Market公開を目指してAndroidアプリを開発する!(翻訳履歴保存編)では、翻訳結果をSQLiteに保存するようにしたので、今回は、その結果を参照する機能を実装していきたいと思います。

データベースは前回のサンプルで作成したものを利用します。
今回のサンプルは、ArrayAdapterListViewコントロールを使って、翻訳履歴を一覧表示するだけという単純なのものにしました。

まず、前回作ったData Access Object(DAO)に、翻訳履歴を取得する処理を追加します。

1
2
3
4
5
6
public Cursor find(String[] columns, String order, String limit) {
    SQLiteDatabase db = connection.getWritableDatabase();
    Cursor cursor = db.query(false, ITranslate.TABLE_NAME, columns, null, null, null, null, order, limit);
 
    return cursor;
}


次に、翻訳結果の情報を格納しているTranslateModelクラスにtoString()関数をオーバーライドします。

1
2
3
4
@Override
public String toString() {
    return translated;
}

後は、SQLiteから取得した翻訳結果をArrayAdapterクラスに格納して、それをListViewコントロールにセットします。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package jp.flashcast.translator.android;
 
import jp.flashcast.translator.android.dao.TranslateDao;
import jp.flashcast.translator.android.model.TranslateModel;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.widget.ArrayAdapter;
import android.widget.ListView;
 
public class TranslatorSample5 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 
        TranslateDao dao = new TranslateDao(this);
        ArrayAdapter<TranslateModel> adapter = new ArrayAdapter<TranslateModel>(this, android.R.layout.simple_list_item_1);
 
        addTranslateModel(adapter, dao.find(null, BaseColumns._ID + " DESC", null));
 
        ListView view = (ListView)findViewById(R.id.histories);
        view.setAdapter(adapter);
    }
 
    private void addTranslateModel(ArrayAdapter<TranslateModel> adapter, Cursor cursor) {
        if (cursor != null) {
            if (cursor.getCount() > 0) {
                while (cursor.moveToNext()) {
                    TranslateModel model = new TranslateModel();
                    model.setId(cursor.getInt(0));
                    model.setOriginal(cursor.getString(1));
                    model.setTranslated(cursor.getString(2));
                    model.setSrcLanguage(cursor.getString(3));
                    model.setDstLanguage(cursor.getString(4));
                    model.setDt(cursor.getString(5));
                    adapter.add(model);
                }
            }
        }
    }
}

TranslateModelクラスにtoString()関数をオーバーライドしたことで、翻訳結果だけが一覧に表示されるようになります。

画面レイアウトのXMLはこんな感じです。

1
2
3
4
5
6
7
8
9
10
11
12
<?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/histories"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
</LinearLayout>

実行結果はこちら。

listview

あまりにシンプルすぎるので、もうちょっと工夫したいと思います。

  • ListViewの1行に翻訳前と翻訳後、翻訳した時間を表示し、削除ボタンもつける。
  • 件数が増えてくると画面が長くなりすぎるので、一覧の下に「もっと読む」みたいな機能をつけて、はじめは特定の件数だけ表示する。

では、これを実現するにはどうするか?
ArrayAdapterの親クラスであるBaseAdapterを継承したクラスと、ListViewクラスを継承したクラスを実装して実現します。

前者については、こちらが非常に参考になりました。
ListViewを拡張する方法 | public static void main

まず、1行のレイアウトにあたるXMLを用意します。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  android:layout_width="wrap_content"
  android:layout_height="wrap_content">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        >
        <TextView
            android:id="@+id/original"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:layout_marginLeft="10sp"
            android:singleLine="true"
            android:textSize="15sp"
            android:textStyle="bold"
            />
        <TextView
            android:id="@+id/translated"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:layout_marginLeft="10sp"
            android:singleLine="true"
            android:textSize="12sp"
            />
        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/dt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginRight="10sp"
                android:textSize="12sp"
                />
        </RelativeLayout>
    </LinearLayout>
    <ImageView
        android:id="@+id/delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical|center_horizontal"
        android:layout_marginRight="5sp"
        />
</LinearLayout>

次に、ArrayAdapterの親クラスであるBaseAdapterを継承したクラスを作ります。1行のレイアウトはgetView()関数の中で設定(42行目)します。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package jp.flashcast.translator.android.adapter;
 
import java.util.ArrayList;
 
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import jp.flashcast.translator.android.R;
import jp.flashcast.translator.android.manager.HistoryManager;
import jp.flashcast.translator.android.model.TranslateModel;
import jp.flashcast.translator.android.view.TranslateListView;
 
public class TranslateAdapter extends BaseAdapter {
    private LayoutInflater inflater;
    private ArrayList<TranslateModel> list;
    private HistoryManager manager;
 
    public TranslateAdapter(Context context, ArrayList<TranslateModel> list, HistoryManager manager) {
        this.list = list;
        this.manager = manager;
        this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
 
    public int getCount() {
        return list.size();
    }
 
    public Object getItem(int position) {
        return list.get(position);
    }
 
    public long getItemId(int position) {
        return list.get(position).getId();
    }
 
    public View getView(int position, View convertView, ViewGroup parent) {
        convertView = inflater.inflate(R.layout.list_item, null);
 
        TextView original = (TextView)convertView.findViewById(R.id.original);
        TextView translated = (TextView)convertView.findViewById(R.id.translated);
        TextView dt = (TextView)convertView.findViewById(R.id.dt);
        ImageView image = (ImageView)convertView.findViewById(R.id.delete);
 
        original.setText(list.get(position).getOriginal());
        translated.setText(list.get(position).getTranslated());
        dt.setText(list.get(position).getDt());
        image.setImageResource(android.R.drawable.ic_menu_delete);
        image.setId(list.get(position).getId());
 
        image.setOnClickListener(new OnClickListener() {
 
            public void onClick(View v) {
                manager.delete(v.getId());
                int max = manager.getHistoriesCount();
                int len = manager.setTranslateAdapter(TranslateAdapter.this, true);
                int pos = HistoryManager.HISTORY_LIST_VIEW_COUNT * (manager.getPage() - 1);
                TranslateListView view = ((TranslateListView)v.getParent().getParent());
                view.setAdapter(TranslateAdapter.this);
                view.setFooterVisible(!(len == max));
                view.setSelection((max > pos) ? pos : max);
            }
 
        });
 
        return convertView;
    }
 
    public void add(TranslateModel model) {
        list.add(model);
    }
 
    public void clear() {
        list.clear();
    }
}

55~68行目が削除ボタンクリック時の処理になります。

続いて、後者のほうです。

件数が増えてくると画面が長くなりすぎるので、一覧の下に「もっと読む」みたいな機能をつけて、はじめは特定の件数だけ表示する。

こちらのサイトが非常に参考になりました。
tappli blog: [Android] ListViewとFooterView
サンプルソースがダウンロードできるのはとてもありがたいです!

ListViewを継承したクラスを作ります。

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package jp.flashcast.translator.android.view;
 
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;
import jp.flashcast.translator.android.R;
 
public class TranslateListView extends ListView {
    private View footer;
    private boolean visible;
 
    public TranslateListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public void addFooterView(View footer) {
        super.addFooterView(footer);
 
        if (this.footer != null) {
            removeFooterView(this.footer);
        }
 
        this.footer = footer;
        ImageView img = (ImageView)this.footer.findViewById(R.id.more);
        img.setImageResource(android.R.drawable.ic_menu_more);
    }
 
    public void setFooterVisible(boolean visible) {
        if (this.footer != null) {
            if (!visible) {
                removeFooterView(this.footer);
            }
        }
 
        this.visible = visible;
    }
 
    public boolean isFooterVisible() {
        return this.visible;
    }
}

実行結果はこちら。

●初期表示

normal

※ ここでは、翻訳履歴が少なくてもいいように、2件づつ表示されるようににしています。

●一覧の一番下の行(もっと読むのような)を押した後

more

●削除ボタンを押して履歴をすべて削除した後

nodata

さっきよりも、使い勝手も良くなったのではないかと思います!

サンプルソース