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

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

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

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

	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()関数をオーバーライドします。

	@Override
	public String toString() {
		return translated;
	}

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

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はこんな感じです。

<?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を用意します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  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行目)します。

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を継承したクラスを作ります。

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

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

サンプルソース