Archive for the ‘Android’ Category

7月
27

Android Market公開を目指してAndroidアプリを開発する!(TextToSpeech編)で、TextToSpeechクラスを使って翻訳結果をしゃべる機能を実装しました。

日本語には対応していないので、日本語に翻訳した結果を読み上げるさせることは出来ませんでした。

対応している言語は、

  • 英語
  • フランス語
  • ドイツ語
  • イタリア語
  • スペイン語

の五ヶ国語のようです。

が、いろいろ調べていると、

Android JaTTS – namespace gimiteという、日本語を読み上げることの出来るライブラリを発見しました!

修正BSDライセンスということですので、早速利用させていただくことにしました。

2010/7/27時点の最新版はandroid-jatts-0.0.1.jarのようです。

ダウンロードしたら、プロジェクトに登録します。Eclipseの「Project」メニューより「Properties」を選択すると、以下のような画面が表示されます。

jar

左ペインより「Java Build Path」を選択し、「Add JARs」ボタンを押して、「libraries」に追加します。

準備ができたので、前回のサンプルソースを変更していきます。

まずは、パッケージをインポートします。

import net.gimite.jatts.JapaneseTextToSpeech;

それから、TextToSpeechが利用可能か判断しているところに、処理を追加して、以下のようにしました。

    	try {
    		Intent intent = new Intent();
    		intent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
    		startActivityForResult(intent, TTS_DATA_CHECK_REQUEST_CODE);
    	}
		catch (Exception ex) {
			playable = false;
		}

    	if (jatts == null) {
        	try {
        		jatts = new JapaneseTextToSpeech(getApplicationContext(), this);
         	   	playable_ja = true;
    		}
    		catch (Exception ex) {
         	   	playable_ja = false;
    		}
    	}

※ 10~18行目が追加した処理になります。

次に、翻訳をした後に、読み上げ可能なLocaleかどうかにより、「play」ボタンを制御している部分を以下のように変更しました。

●変更前

				if (playable) {
					Locale locale = getLocale(gmodel.getDstLanguage());

					if (tts.isLanguageAvailable(locale) == TextToSpeech.LANG_AVAILABLE) {
						btnPlay.setEnabled(true);
					}
					else {
						btnPlay.setEnabled(false);
					}
				}

●変更後

				if (gmodel.getSrcLanguage().equals("ja")) {
					btnPlay.setEnabled(playable_ja);
				}
				else {
					if (playable) {
						Locale locale = getLocale(gmodel.getDstLanguage());

						if (tts.isLanguageAvailable(locale) == TextToSpeech.LANG_AVAILABLE) {
							btnPlay.setEnabled(true);
						}
						else {
							btnPlay.setEnabled(false);
						}
					}
				}

また、「play」ボタンが押された時の処理は、以下のような変更になります。

●playボタンクリック処理変更前

    					if (tts.isSpeaking()) {
    						tts.stop();
    					}

    					tts.setLanguage(getLocale(gmodel.getSrcLanguage()));
    					tts.speak(gmodel.getTranslated(), TextToSpeech.QUEUE_FLUSH, null);

●playボタンクリック処理変更後

    					if (tts.isSpeaking()) {
    						tts.stop();
    					}
    					else if (jatts.isSpeaking()) {
    						jatts.stop();
    					}

    					if (gmodel.getDstLanguage().equals("ja")) {
    						HashMap<String, String> params = new HashMap<String, String>();
    						params.put(JapaneseTextToSpeech.KEY_PARAM_SPEAKER, "female01");
	    					jatts.speak(gmodel.getTranslated(), TextToSpeech.QUEUE_FLUSH, params);
    					}
    					else {
        					tts.setLanguage(getLocale(gmodel.getSrcLanguage()));
        					tts.speak(gmodel.getTranslated(), TextToSpeech.QUEUE_FLUSH, null);
    					}

これで、日本語もしゃべるようになりました!前回同様、実行結果は、サンプルソースを試していただければと思います!

但し、テキストデータの読み上げに対応していない言語に翻訳した場合には、「play」ボタンは無効化され、押せない状態になりますので、ご注意を!

サンプルソース

サンプルソースには、libフォルダ配下にandroid-jatts-0.0.1.jarも含んでいますが、プロジェクトへの登録が再度必要ですので、ご注意ください。

7月
20

Android Market公開を目指してAndroidアプリを開発する!(音声認識機能実装編)の続きです。

We’ve introduced a new feature in version 1.6 of the Android platform: Text-To-Speech (TTS). Also known as “speech synthesis”, TTS enables your Android device to “speak” text of different languages.

Android Developers Blog: An introduction to Text-To-Speech in Androidで、テキストデータの読み上げが可能になったのを知りました。

前回に引き続き、今回もTranslatAIRにはない機能、

翻訳結果をしゃべる機能

を実装していきたいと思います。

The TTS engine that ships with the Android platform supports a number of languages: English, French, German, Italian and Spanish. Also, depending on which side of the Atlantic you are on, American and British accents for English are both supported.

Android Developers Blog: An introduction to Text-To-Speech in Androidより引用。

対応している言語は、

  • 英語
  • フランス語
  • ドイツ語
  • イタリア語
  • スペイン語

の五ヶ国語のようです。
日本語には対応していないようですね。残念です。。。

前回サンプルの画面に「play」ボタンを追加しました。「voice」ボタンと「tweet」ボタンの間です。

activity

XMLには、以下の部分を追加しました。

		<Button
			android:id="@+id/play"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
	    	android:layout_weight="1"
		    android:text="play"
		    />

それでは、「play」ボタンが押された時の処理に入っていきたい思います。

TextToSpeechクラスを使用します。

音声認識機能(Voice Recognition)の時と同じように、TextToSpeechのエンジンが利用できるか判断し、初期化処理をしていきます。

    	try {
    		Intent intent = new Intent();
    		intent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
    		startActivityForResult(intent, TTS_DATA_CHECK_REQUEST_CODE);
    	}
		catch (Exception ex) {
			playable = false;
		}

コールバックで、TextToSpeechクラスのインスタンスを生成します。

        else if (requestCode == TTS_DATA_CHECK_REQUEST_CODE) {
        	// TextToSpeech初期化の結果
    		if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
    			// TextToSpeechが利用できる場合
    			if (tts == null) {
    				try {
    					tts = new TextToSpeech(getApplicationContext(), this);
    					playable = true;
    				}
    				catch (Exception ex) {
    					playable = false;
    				}
    			}
    		}
    		else {
    			// TextToSpeechのリソースがインストールされていない場合
    			try {
	    			Intent intent = new Intent();
	    			intent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
	    			startActivity(intent);
    			}
    			catch (Exception ex) {
    				playable = false;
    			}
    		}
        }

但し、TextToSpeechエンジンは音声読み上げのためのリソースが、Android端末に入っていないと機能しません。

インストールは、「設定」→「テキスト読み上げ」→「音声データをインストール」からできます。

設定

設定

テキスト読み上げ

テキスト読み上げ

音声データをインストール

音声データをインストール



ですが、リソースがインストールされていない場合には、18~20行のようにすると、インストールを促すように出来るのです。

tts

install

Android端末の初期状態ではインストールされていないようなので、これは実装することをオススメします。

前回同様、コールバックのリクエストコード(requestCode)は、メンバー変数で定数として定義しています。

    private static final int TTS_DATA_CHECK_REQUEST_CODE = 9998;

最後に、「play」ボタンのクリックイベントリスナーを実装します。

        Button btnPlay = (Button)findViewById(R.id.play);
        btnPlay.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				if (gmodel != null) {
    				if (!gmodel.getTranslated().equals("")) {
    					if (tts.isSpeaking()) {
    						tts.stop();
    					}

    					tts.setLanguage(getLocale(gmodel.getSrcLanguage()));
    					tts.speak(gmodel.getTranslated(), TextToSpeech.QUEUE_FLUSH, null);
    				}
				}
			}

        });

しゃべっている最中に、繰り返し「play」ボタンを押された場合には、一旦止めてからしゃべりなおさせるようにしておきました。

それと、今後対応される言語が増えていったときのことを想定して、Localeを設定するところは、五ヶ国語以外も事前に準備をしておきました。

    private String getLocale(LanguageModel model) {
    	String locale;

    	if (model.getValue().equals("en")) {
    		locale = Locale.ENGLISH.toString();
    	}
    	else if (model.getValue().equals("ja")) {
    		locale = Locale.JAPAN.toString();
    	}
    	else if (model.getValue().equals("zh")) {
    		locale = Locale.CHINA.toString();
    	}
    	else if (model.getValue().equals("it")) {
    		locale = Locale.ITALY.toString();
    	}
    	else if (model.getValue().equals("es")) {
    		locale = new Locale("spa", "ESP").toString();
    	}
    	else if (model.getValue().equals("fr")) {
    		locale = Locale.FRANCE.toString();
    	}
    	else if (model.getValue().equals("de")) {
    		locale = Locale.GERMAN.toString();
    	}
    	else if (model.getValue().equals("ko")) {
    		locale = Locale.KOREA.toString();
    	}
    	else if (model.getValue().equals("ru")) {
    		locale = new Locale("ru", "RUS").toString();
    	}
    	else {
    		locale = new Locale("OTHER").toString();
    	}

    	return locale;
    }

実行結果は、ここでは表現できなかったので、サンプルソースを試していただければと思います!

但し、テキストデータの読み上げに対応していない言語に翻訳した場合には、「play」ボタンは無効化され、押せない状態になりますので、ご注意を!

サンプルソース

7月
18

Android Market公開を目指してAndroidアプリを開発する!(翻訳履歴参照編)の続きです。

いよいよ、TranslatAIRにはない機能の実装に入りたいと思います。

Android端末に向かってしゃべった言葉をテキストに落とす機能(翻訳対象とする)

を実装します。

まず、画面下部に、「voice」ボタンを用意しました。このボタンを押すと、音声認識機能が実行されるようにしたいと思います。

activity

画面デザインの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"
    >
    <EditText
    	android:id="@+id/srctext"
	    android:layout_width="fill_parent"
	    android:layout_height="fill_parent"
	    android:layout_weight="1"
	    android:layout_margin="5dp"
	    android:inputType="textMultiLine"
	    android:gravity="top"
	    />
	<LinearLayout
		android:orientation="horizontal"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		>
		<Spinner
			android:id="@+id/srclanguage"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
		    android:layout_weight="1"
			android:layout_marginLeft="5dp"
			/>
		<ToggleButton
			android:id="@+id/vector"
		    android:layout_width="wrap_content"
			android:layout_height="fill_parent"
			android:textOff=">>"
		/>
		<Spinner
			android:id="@+id/dstlanguage"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
		    android:layout_weight="1"
			android:layout_marginRight="5dp"
			/>
	</LinearLayout>
    <EditText
    	android:id="@+id/dsttext"
	    android:layout_width="fill_parent"
	    android:layout_height="fill_parent"
    	android:layout_weight="1"
	    android:layout_margin="5dp"
	    android:inputType="textMultiLine"
	    android:gravity="top"
	    />
	<LinearLayout
		android:orientation="horizontal"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:gravity="right"
		>
		<Button
			android:id="@+id/voice"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
	    	android:layout_weight="1"
			android:layout_marginLeft="5dp"
		    android:text="voice"
		    />
		<Button
			android:id="@+id/tweet"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
	    	android:layout_weight="1"
		    android:text="tweet"
		    />
		<Button
			android:id="@+id/translate"
			android:layout_width="fill_parent"
			android:layout_height="fill_parent"
	    	android:layout_weight="1"
	    	android:layout_marginRight="5dp"
		    android:text="translate"
		    />
	</LinearLayout>
</LinearLayout>

それでは、「voice」ボタンを押された時の処理に、入っていきたいと思います。

音声認識機能(Voice Recognition)には、RecognizerIntentというクラスを利用します。

Android Developersサイトのサンプルソース
VoiceRecognition.java | Android Developers



音声認識(RecognizerIntent)を使用するには – 逆引きAndroid入門
を参考にさせていただきました。

事前に、RecognizerIntentが使えるかどうかを判断して、初期化処理をしておきます。

    	try {
    		PackageManager manager = getPackageManager();
    		List<ResolveInfo> activities = manager.queryIntentActivities(
    			new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);

        	if (activities.size() != 0) {
        		recognizable = true;
    		} else {
    			recognizable = false;
    		}
    	}
    	catch (Exception ex) {
    		recognizable = false;
    	}

RecognizerIntentが使えれば、「voice」ボタンのクリックイベントリスナーを設定します。「voice」ボタンを押したときには、まず、Android端末に向かって何語でしゃべるかを選択させるDialogを表示するようにします。

        if (recognizable) {
	        btnSpeak.setOnClickListener(new OnClickListener() {

				public void onClick(View v) {
    				final AlertDialog.Builder builder = new AlertDialog.Builder(TranslatorSample7.this);
    		    	final CharSequence[] labels = getResources().getStringArray(R.array.language_label);

    				builder.setTitle("選択してください。");
    				builder.setItems(labels, new DialogInterface.OnClickListener() {

						public void onClick(DialogInterface dialog, int which) {
							showVoiceRecognitionDialog(languages.getItem(which));
						}
    		    	});

    				builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {

						public void onClick(DialogInterface dialog, int which) {
							return;
						}

    				});

    				builder.setCancelable(true);
    				builder.create().show();
				}

	        });
        }

何語をしゃべるかが選択されたら、RecognizerIntentを起動させて、音声を認識するまで待ちます。

    private void showVoiceRecognitionDialog(LanguageModel locale) {
    	extraLanguage = getLocale(locale);

        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "TranslatorSample7");
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, extraLanguage);
        startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
    }

音声が認識されたときのコールバックで、結果を画面に反映させるようにします。

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == VOICE_RECOGNITION_REQUEST_CODE) {
        	// 音声認識初期化の結果
    		if (resultCode == RESULT_OK) {
    			ArrayList<String> matches = data.getStringArrayListExtra(
    				RecognizerIntent.EXTRA_RESULTS);

    			StringBuilder sb = new StringBuilder();

    			for (int i = 0; i < matches.size(); i++) {
    				sb.append(matches.get(i));
    			}

    			ToggleButton vector = (ToggleButton)findViewById(R.id.vector);

    			if (vector.isChecked()) {
    				((EditText)findViewById(R.id.dsttext)).setText(sb.toString());
    			}
    			else {
    				((EditText)findViewById(R.id.srctext)).setText(sb.toString());
    			}
    		}
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

そのときのリクエストコード(requestCode)は、メンバー変数で定義しています。値は、とりあえず9999にしましたが、0以上でアプリケーション内でユニークであればいくつでもよいと思います。

    private static final int VOICE_RECOGNITION_REQUEST_CODE = 9999;

実行結果はこちら。

●「voice」ボタンを押した後

language

●Android端末に向かってしゃべるところ

recoginize

●音声認識された後、翻訳までしてみたところ

hungry

サンプルソースのアプリでも、十分遊べると思います。是非、試してみてください!

サンプルソース

7月
12

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

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

サンプルソース

7月
08

Android Market公開を目指してAndroidアプリを開発する!(Tweet機能実装編)の続きです。

今回はAndroid Market公開を目指してAndroidアプリを開発する!(翻訳機能実装編)の翻訳結果を保存する機能を実装したいと思います。

保存先にはSQLiteを利用します。

AndroidアプリでSQLiteを利用する方法については、こちらを参考にさせていただきました。
Java/Android/基礎編/SQLite | PukiWiki

SQLiteOpenHelper | Android Developersというクラスを使うと、データベースアクセスの実装を容易にしてくれます。

例えば、、、

  • データベースやテーブルが存在しなかった時に生成してくれる処理(今回は、翻訳結果を保存するテーブルを作成するようにしました)
  • データベースやテーブルが存在した場合で、バージョンが変更されている時の処理(今回は、この部分は実装しませんでした)

これらの関数が抽象メソッドとして用意されており、オーバライドすれば良いだけなので、非常に便利です。

ということで、SQLiteOpenHelperを継承した以下のようなクラスを作成しました。

package jp.flashcast.translator.android.manager;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;

public class ConnectionManager extends SQLiteOpenHelper {
	private static final String DATABASE_NAME = "translator.db";
	private static final int DATABASE_VERSION = 1;
	private static ConnectionManager connection;

	public ConnectionManager(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	public static ConnectionManager getInstance(Context context) {
		if (connection == null) {
			connection = new ConnectionManager(context);
		}

		return connection;
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		final String histories = "CREATE TABLE IF NOT EXISTS histories (" +
			BaseColumns._ID + " INTEGER PRIMARY KEY, " +
			"original TEXT, " +
			"translated TEXT, " +
			"srclanguage TEXT, " +
			"dstlanguage TEXT, " +
			"dt TEXT);";

		db.execSQL(histories);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub

	}

}

次に、翻訳結果を保存するテーブルのフィールド名を定義しただけのクラスインターフェイスを作成しました。

package jp.flashcast.translator.android.schema;

public interface ITranslate {
	String TABLE_NAME = "histories";
	String ID = "id";
	String ORIGINAL = "original";
	String TRANSLATED = "translated";
	String SRC_LANGUAGE = "srclanguage";
	String DST_LANGUAGE = "dstlanguage";
	String DATE_TIME = "dt";
}

続いて、デザインパターンのData Access Object(DAO)となるクラスを作成します。

package jp.flashcast.translator.android.dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import jp.flashcast.translator.android.manager.ConnectionManager;
import jp.flashcast.translator.android.model.TranslateModel;
import jp.flashcast.translator.android.schema.ITranslate;

public class TranslateDao {
	private ConnectionManager connection;

	public TranslateDao(Context context) {
		if (connection == null) {
			connection = ConnectionManager.getInstance(context);
		}
	}

	public void insert(TranslateModel model) {
		SQLiteDatabase db = connection.getWritableDatabase();
		db.insertOrThrow(ITranslate.TABLE_NAME, null, setValues(model));
		db.close();
	}

	private ContentValues setValues(TranslateModel model) {
		ContentValues values = new ContentValues(5);
		values.put(ITranslate.ORIGINAL, model.getOriginal());
		values.put(ITranslate.TRANSLATED, model.getTranslated());
		values.put(ITranslate.SRC_LANGUAGE, model.getSrcLanguage());
		values.put(ITranslate.DST_LANGUAGE, model.getDstLanguage());
		values.put(ITranslate.DATE_TIME, model.getDt());

		return values;
	}
}

ここでは、今までも使っていたTranslateModelというモデルクラスをData Transfer Object(DTO)としています。クラス名の命名がちょっと微妙な気がしますが(本当はTranslateDtoとか?)、役割は同じなので、あまり気にしないことにしました。

最後に、翻訳処理の後処理の部分で、翻訳結果をinsertするようにしておしまいです。

		if (translating) {
			if (gmodel.isSuccess()) {
				dao.insert(gmodel);

				((EditText)findViewById(
					(!((ToggleButton)findViewById(R.id.vector)).isChecked()) ? R.id.dsttext : R.id.srctext))
					.setText(gmodel.getTranslated());
			}
			else {
				Toast.makeText(this, "Failed to translate...", Toast.LENGTH_SHORT).show();
			}

			translating = false;
		}

sample4

実行結果をコンソールから確認してみます。
※ 実機では、権限がないということでコンソールからの確認ができませんでしたので、とりあえずemulator上で確認しています。

macbook:~ mojamoja$ adb shell # sqlite3 data/data/jp.flashcast.translator.android/databases/
translator.db
SQLite version 3.5.9
Enter “.help” for instructions
sqlite> select * from histories;
1|hungry |飢えた|en|ja|2010 6 28 23:08:10
2|journal |ジャーナル|en|ja|2010 6 28 23:09:03
3|various |さまざまな|en|ja|2010 6 28 23:09:32
sqlite>

これで、翻訳結果を保存するところまでできました。
次回は、その結果を閲覧する機能をつくろうと思います!

サンプルソース

  • Search:
  • flashcastとは?

    東京を中心に、現在フリーランスとして活動しているITエンジニア、および、かつてフリーランスとして活動していた起業家達が立ち上げたコミュニティーです。

    みんなで集まって面白いことをやろう!形に残そう!ということで、ブログをはじめました。

    技術情報や、フリーエンジニアに役立つ情報などを、ご紹介できたらと思っています。

    お問い合わせ:
    info@flashcast.jp
  • カレンダー

    2010年7月
    日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日
    « 6月    
     123
    45678910
    11121314151617
    18192021222324
    25262728293031
  • メンバー紹介

    もじゃもじゃ
    flashcastのリーダー

    3年ほどフリーのITエンジニアとして活動。現在は、社員2名の株式会社を経営しています。

    一攫千金を夢見る野心家です。

    ライブキャスト

    yasu
    ダイバー

    自宅サーバーでホームページを作り始めました。

    少しずつ記事を増やしていきますので足を運んでください。

    よろしくお願いします。

    sa-sa-ki.jp

    のら
    たびびと

    ねこ好きに悪人はいなーいっ!!

    バイクや車も好きです。

    めぐ
    デザイナーのたまご

    音楽とデザインとお酒をこよなく愛する永遠のダイエッター。

    現在ペンタブレットでイラストを勉強中。

    Hiro
    コンサル

    PMやSEの案件を業務委託で請けることが多いですが、小規模案件も受託でやっています。

    得意な分野はマイクロソフト製品や関連技術によるシステム構築です。

    KEI
    取締役の風格

    最年少なのに、メンバーで1番の貫禄の持ち主!?

    C#や.netなどサーバ側の開発が得意。

    ろっきー
    美食家★パパ

    自分にとっての息抜きは、ドライブして温泉に入って、美味しいご飯を食べる事。

    ココロとカラダのリフレッシュを大切にし、日々の仕事に励む一児の父親です。

    郵便番号検索

    my-hobby

    とのさま
    げーむのおうさま

    大人なのに好きなことしかやらない駄目人間。

    Web系が得意、アクセスは苦手><

    tonosamart.com

    セクレタリアト
    ギャンブラー

    フリーランス時代は仲間の現場を探すことが多く、それをきっかけに会社を設立。

    現在はSI業に特化せず、他の業種にも興味を持ち始めています。

    メドレー株式会社

  • 広告