GoogleのAPK翻訳サービス

作るアプリで使用する文字列は基本res/valueフォルダにstrings.xmlとして保存しているが、せっかく定義しているんだから多言語対応したいと思ってるんだけど、こんなサービスがあった。「Google Play App Translation Service」金払えば翻訳してくれるらしい。申し込み方法はandroid developer consoleから左側の「APK」を選択、下に「APK翻訳サービス」というのがる。

translate

今度試してみようと思う。

階層構造をリストフラグメントで作ってみた

アプリで階層構造のリストを作成する機会があって、せっかくなのでサンプルとして残しておこうと思う。おそらく今後使用することはないかもだけど。やりたいこととしては、階層をDBとリストフラグメントを使って表示したい。作成したサンプルアプリは単純にタイトルをリストで表示するだけのもの。作成したものは以下の通り

何がやりたいかというと、下の図みたいなのをリストで表示したい。
hierarchy

DB

DBは「HierarchyList.db」というDBをあらかじめ作成しておき、「assets」フォルダにおいて、そこからコピーして使用する。テーブルレイアウトは以下のレイアウト。「Id」はユニークないID、親の階層を指定するための「ParentId」、一番上の階層の場合は「0」を指定。

CREATE TABLE HierarchyList (
	Id 		INTEGER PRIMARY KEY AUTOINCREMENT,
	ParentId	INTEGER,
	Title		TEXT
);

テーブルのデータは以下。取りあえず3階層まで作ってみた。

Id ParentId Title
1 0 1
2 0 2
3 0 3
4 0 4
5 0 5
6 1 1-1
7 1 1-2
8 1 1-3
9 1 1-4
10 1 1-5
11 2 2-1
12 2 2-2
13 2 2-3
14 2 2-4
15 2 2-5
16 3 3-1
17 3 3-3
18 3 3-3
19 3 3-4
20 3 3-5
21 4 4-1
22 4 4-4
23 4 4-4
24 4 4-4
25 4 4-5
26 5 5-1
27 5 5-5
28 5 5-5
29 5 5-5
30 5 5-5
31 6 1-1-1
32 6 1-1-2
33 6 1-1-3
34 6 1-1-4
35 6 1-1-5
36 7 1-2-1
37 7 1-2-2
38 7 1-2-3
39 7 1-2-4
40 7 1-2-5
41 8 1-3-1
42 8 1-3-2
43 8 1-3-3
44 8 1-3-4
45 8 1-3-5
46 9 1-4-1
47 9 1-4-2
48 9 1-4-3
49 9 1-4-4
50 9 1-4-5
51 10 1-5-1
52 10 1-5-2
53 10 1-5-3
54 10 1-5-4
55 10 1-5-5

プログラム

  • HierarchyListFragment.java
  • ListItem.java
  • DatabaseHelper.java
  • MainActivity.java

MainActivity.java

import android.app.Activity;
import android.app.FragmentTransaction;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// リストフラグメントへのパラメータ(ルート)
		Bundle bundle = new Bundle();
		bundle.putInt("parent_id", 0);
		
		// リストフラグメントのセット
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        HierarchyListFragment listFragment = new HierarchyListFragment();
		listFragment.setArguments(bundle);
        ft.replace(R.id.fragment_container, listFragment);
        ft.commit();
	}
	
	/**
     * リストフラグメントアイテムクリックイベント
     */	
	public void onListItemClick(int itemId) {
		// 下階層のチェック
		DatabaseHelper dbHelper = new DatabaseHelper(this);
		SQLiteDatabase db = dbHelper.getReadableDatabase();
	    String query = "SELECT * FROM HierarchyList WHERE ParentId = " + itemId + ";";
	    Cursor c = db.rawQuery(query, null);            
	    if (! c.moveToFirst()) {
	    	Toast.makeText(this, "これより下の階層はありません", Toast.LENGTH_SHORT).show();
	    	c.close();
	    	return;
	    }
	    c.close();
		
		// リストフラグメントへのパラメータ
		Bundle bundle = new Bundle();
		bundle.putInt("parent_id", itemId);
					
		// 次の階層のリストフラグメントの生成
		FragmentTransaction ft = getFragmentManager().beginTransaction();
		HierarchyListFragment listFragment = new HierarchyListFragment();
		listFragment.setArguments(bundle);
        ft.replace(R.id.fragment_container, listFragment);
        ft.addToBackStack(null);
        ft.commit();
	}
}

HierarchyListFragment.java

import java.util.ArrayList;
import java.util.List;
import android.app.ListFragment;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class HierarchyListFragment extends ListFragment {
	private ArrayAdapter<ListItem> adapter;
	private int parentId = 0;
	
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		
		// パラメータのセット
		parentId = getArguments().getInt("parent_id");
		
		// リストのセット
		setListView();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
		return inflater.inflate(R.layout.list_fragment, container, false);
	}
	
	@Override
	public void onListItemClick(ListView listView, View v, int position, long id) {
    	super.onListItemClick(listView, v, position, id);
    	
    	// Activityに処理を渡す
    	MainActivity activity = (MainActivity)getActivity();
    	activity.onListItemClick(adapter.getItem(position).getId());
	}
	
	/**
	 * リストのセット
	 */
	public void setListView() {
		// リストのセット
		List<ListItem> list = new ArrayList<ListItem>();
		DatabaseHelper dbHelper = new DatabaseHelper(getActivity());
		SQLiteDatabase db = dbHelper.getReadableDatabase();
	    String query = "SELECT * FROM HierarchyList"
	    					+ " WHERE ParentId = " + parentId + " ORDER BY Id;";
	    Cursor c = db.rawQuery(query, null);            
	    if (c.moveToFirst()) {
	    	do {
	    		ListItem listItem = new ListItem();
	    		listItem.setId(c.getInt(c.getColumnIndex("Id")));
	    		listItem.setTitle(c.getString(c.getColumnIndex("Title")));
	    		listItem.setParentId(c.getInt(c.getColumnIndex("ParentId")));
	    		list.add(listItem);
	    	} while (c.moveToNext());
		}
	    c.close();
		
	    // アダプターのセット
	    adapter = new ListAdapter(getActivity(), list);
	    setListAdapter(adapter);
	}
	
	/**
	 * ListAdapter Class
	 */
	private class ListAdapter extends ArrayAdapter<ListItem> {
		private LayoutInflater mInflater;
		
		public ListAdapter(Context context, List<ListItem> objects) {
			super(context, 0, objects);
			mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		}
	
		public View getView(int position, View convertView, ViewGroup parent) {
			final ViewHolder holder;
			
			if (convertView == null) {
				convertView = mInflater.inflate(R.layout.listfragment_item, parent, false);
				holder = new ViewHolder();
				holder.tvTittle = (TextView)convertView.findViewById(R.id.title);
				convertView.setTag(holder);
			} else {			
				holder = (ViewHolder)convertView.getTag();
			}
			final ListItem item = this.getItem(position);
			
			holder.tvTittle.setText(item.getTitle());
			
			return convertView;
		}
	} 
	
	/**
	 * ViewHolder for ListView
	 */	
	private class ViewHolder {
		TextView tvTittle;
	}
}

後は省略

レイアウトファイル

作成したレイアウトファイルは以下の通り

  • activity_main.xml
  • fragment_main.xml
  • list_fragment.xml
  • listfragment_item.xml

プログラムの実行

以下のようになる。リストをクリックすると下の階層に行く。
device-2014-06-21-230946device-2014-06-21-230958device-2014-06-21-231007

作成したサンプルプログラム
HierarchyListFragmentSample

Android Holo Colors Generatorを使ってみる

Androidのアイコンやテーマの作成をする作業をいろいろやってくれるサイトを見つけた。
ここ。その中でボタンなどの色を変えてくれる「Android Action Bar Style Generator」というものがある。

使い方は色を変えたいコントロールを指定して、色を指定し、下の方にある「DOWNLOAD ZIP」をクリックする。
ダウンロードしたものをAndroidプロジェクトの「res」フォルダに上書きする。セレクターのxmlファイルまで作成してくれるのですごい便利。

これが(標準デザイン)

device-2014-04-21-211048

 

こうなる。

device-2014-04-21-211519

すごく便利です。

androidでDBを使ってみる

DBを使ってみる。androidのDBにはsqliteを使用する。sqlite用にクラスが用意されており、これを使って追加、訂正、削除の簡単なサンプルを作成してみる。

SQLiteOpenHelper

まず「SQLiteOpenHelper」を継承したクラスを作成する。ここでは「DatabaseHelper」とする。
定数「DATABASE_NAME」にデータベース名をセット。実際端末にこの名前でDBが作成される。
定数「DATABASE_VERSION」にはバージョンをセットする。最初に1をセットしておき、バージョンアップ時にこの数字を上げる。上げると、「onUpgrade」メソッドが走るので、ここに列追加やテーブル作成のバージョンアップ時処理を記述する。テーブル作成のsqlは「onCreate」メソッド内に記述する。

今回のサンプルは以下のテーブルで作成する。

列名
_id Integer
Name String

以下ソース
DatabaseHelper.java

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

public class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "Database.db";
    public static final int DATABASE_VERSION = 1;
    
    public DatabaseHelper(Context context) {
    	super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db) {
    	// テーブル作成SQL
    	String sql = "CREATE TABLE Sample ("
				+ " _id INTEGER PRIMARY KEY AUTOINCREMENT,"
				+ " Name TEXT"
				+ ");";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

データの追加

データの追加するにはInsert文でもよいと思うが、ここでは「DatabaseHelper 」クラスと「ContentValues」クラスを使用してデータ追加する。
以下ソース

// 項目のセット
ContentValues values = new ContentValues();
values.put("Name", editText.getText().toString());

// DBに追加
DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.insert("Sample", null, values);

データの編集

同様に「DatabaseHelper 」クラスと「ContentValues」クラスを使用してデータ編集する。

// 項目のセット
ContentValues values = new ContentValues();
values.put("Name", editText.getText().toString());

// データの編集
DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update("Sample", values, "_id = " + id, null);

データの削除

同様に「」クラスを使用してデータを削除する。

DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.delete("Sample", "_id = " + id, null);

作成したサンプル

今回作成したサンプルは追加、訂正、削除をする簡単なもの。
device-2014-04-07-211101

以下メインのクラス。

import android.os.Bundle;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.database.sqlite.SQLiteDatabase;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {
	private DbSampleListFragment listFragment = new DbSampleListFragment();
	private EditText editText;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		// リストフラグメントのセット
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.fragment_container, listFragment, "list_fragment");
        ft.commit();
        
        // データの追加
        Button btAddData = (Button)this.findViewById(R.id.add_data);
        btAddData.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// 入力用EditText
				editText = new EditText(MainActivity.this);
        		editText.setText("");
        		
        		// ダイアログの表示
        		EditTextDialog dialogFragment = EditTextDialog
						.newInstance(R.string.app_name, R.string.dialog_message);
        		dialogFragment.setOnOkClickListener(new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						// 項目のセット
						ContentValues values = new ContentValues();
						values.put("Name", editText.getText().toString());
						
						// DBに追加
						DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
						SQLiteDatabase db = databaseHelper.getWritableDatabase();
						db.insert("Sample", null, values);
						
						Toast.makeText(MainActivity.this, "データを追加しました。", Toast.LENGTH_SHORT).show();
						
						listFragment.setListView();
					}
				});
				dialogFragment.setEditText(editText);
        		dialogFragment.show(getFragmentManager(), "dialog_fragment");
			}
		});
	}

	// データの編集
	public void onListItemEdit(final int id, String name) {
		// 入力用EditText
		editText = new EditText(MainActivity.this);
		editText.setText(name);
		
		// ダイアログの表示
		EditTextDialog dialogFragment = EditTextDialog
				.newInstance(R.string.app_name, R.string.dialog_message);
		dialogFragment.setOnOkClickListener(new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// 項目のセット
				ContentValues values = new ContentValues();
				values.put("Name", editText.getText().toString());
				
				// データの編集
				DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
				SQLiteDatabase db = databaseHelper.getWritableDatabase();
				db.update("Sample", values, "_id = " + id, null);
				
				Toast.makeText(MainActivity.this, "データを編集しました。", Toast.LENGTH_SHORT).show();
				
				listFragment.setListView();
			}
		});
		dialogFragment.setEditText(editText);
		dialogFragment.show(getFragmentManager(), "dialog_fragment");
	}
	
	// データの削除
	public void onListItemDelete(int id) {
		DatabaseHelper databaseHelper = new DatabaseHelper(MainActivity.this);
		SQLiteDatabase db = databaseHelper.getWritableDatabase();
		db.delete("Sample", "_id = " + id, null);
		
		Toast.makeText(MainActivity.this, "データを削除しました。", Toast.LENGTH_SHORT).show();
		
		listFragment.setListView();
	}
}

今回作成したサンプルプロジェクト
DBSample

DiarogFragmentをまとめてみた

DialogFragmentについて、アプリを作成していてDialogはそこそこ使用するので、共通化できそうなものを共通化してみた。
毎度インナークラスを作成し、Activity側にメソッドを作成し、Fragment側で呼び出すのは面倒なので、DialogFragmentクラスを単体で作成し、Activity側でイベントリスナーを登録するようにしてみた。(これが良いやり方かどうかはわかりません。)
作成したダイアログは以下の7つ。

  • OKダイアログ
  • OKCancelダイアログ
  • SingleChoiceダイアログ
  • MultiChoiceダイアログ
  • EditTextダイアログ
  • SpinningProgressダイアログ
  • HorizontalProgressダイアログ

OKダイアログ

OKボタンのみが表示されるダイアログ、あんまり使用しないかなあ。ほとんどの場合Toast使うし。

device-2014-03-30-220205
以下ダイアログクラスのコード

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.os.Bundle;

public class OkDialog extends DialogFragment {
	public static OkDialog newInstance(int title, int message) {
		OkDialog fragment = new OkDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putInt("message", message);
		fragment.setArguments(args);

		return fragment;
	}

	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		int message = getArguments().getInt("message");

		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setTitle(title)
		.setMessage(message)
		.setPositiveButton("OK", null);

		return builder.create();
	}
}

以下使い方

OkDialog dialogFragment = OkDialog
		.newInstance(R.string.app_name, R.string.dialog_message);
dialogFragment.show(getFragmentManager(), "dialog_fragment");

OKCancelダイアログ

OK、Cancelボタンが表示されるダイアログ。

device-2014-03-30-220221

以下ダイアログクラスのコード

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;

public class OkCancelDialog extends DialogFragment {
	private DialogInterface.OnClickListener okClickListener = null;
	private DialogInterface.OnClickListener cancelClickListener = null;

	public static OkCancelDialog newInstance(int title, int message) {
		OkCancelDialog fragment = new OkCancelDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putInt("message", message);
		fragment.setArguments(args);

		return fragment;
	}

	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		int message = getArguments().getInt("message");

		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setTitle(title)
		.setMessage(message)
		.setPositiveButton("OK", this.okClickListener)
		.setNegativeButton("Cancel", this.cancelClickListener);

		return builder.create();
	}

	/**
     * OKクリックリスナーの登録
     */
	public void setOnOkClickListener(DialogInterface.OnClickListener listener) {
		this.okClickListener = listener;
	}

	/**
     * Cancelクリックリスナーの登録
     */
	public void setOnCancelClickListener(DialogInterface.OnClickListener listener) {
		this.cancelClickListener = listener;
	}
}

以下使い方

OkCancelDialog dialogFragment = OkCancelDialog
		.newInstance(R.string.app_name, R.string.dialog_message);
dialogFragment.setOnOkClickListener(new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "OKがクリックされました", Toast.LENGTH_SHORT).show();
	}
});
dialogFragment.setOnCancelClickListener(new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "Cancelがクリックされました", Toast.LENGTH_SHORT).show();
	}
});
dialogFragment.show(getFragmentManager(), "dialog_fragment");

SingleChoiceダイアログ

複数選択で1だけを選択するダイアログ、OKボタンはいらないかもしれないけど、一応。

device-2014-03-30-220234
以下ダイアログクラスのコード

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;

public class SingleChoiceDialog extends DialogFragment {
	private DialogInterface.OnClickListener singleChoiceClickListener = null;
	private DialogInterface.OnClickListener okClickListener = null;
	private DialogInterface.OnClickListener cancelClickListener = null;
	
	public static SingleChoiceDialog newInstance(int title, String[] items, int checkedItem) {
		SingleChoiceDialog fragment = new SingleChoiceDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putStringArray("items", items);
		args.putInt("checked_item", checkedItem);
		fragment.setArguments(args);
		
		return fragment;
	}
	
	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		String[] items = getArguments().getStringArray("items");
		int checkedItem = getArguments().getInt("checked_item");
		
		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setTitle(title);
		builder.setSingleChoiceItems(items, checkedItem, this.singleChoiceClickListener)
		.setPositiveButton("OK", this.okClickListener)
        .setNegativeButton("Cancel", this.cancelClickListener);
		
		return builder.create();
	}
	
	/**
     * SingleChoiceクリックリスナーの登録
     */	
	public void setOnSingleChoiceClickListener(DialogInterface.OnClickListener listener) {
		this.singleChoiceClickListener = listener;
	}
	
	/**
     * OKクリックリスナーの登録
     */	
	public void setOnOkClickListener(DialogInterface.OnClickListener listener) {
		this.okClickListener = listener;
	}
	
	/**
     * Cancelクリックリスナーの登録
     */	
	public void setOnCancelClickListener(DialogInterface.OnClickListener listener) {
		this.cancelClickListener = listener;
	}
}

以下使い方

inal int checkedItem = 1;
SingleChoiceDialog dialogFragment = SingleChoiceDialog
		.newInstance(R.string.app_name, items, checkedItem);
dialogFragment.setOnSingleChoiceClickListener(new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
    	singleSelectItem = items[item];
    }
});
dialogFragment.setOnOkClickListener(new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
    	Toast.makeText(MainActivity.this, singleSelectItem + "が選択されました", Toast.LENGTH_SHORT).show();
    }
});
dialogFragment.setOnCancelClickListener(new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "Cancelがクリックされました", Toast.LENGTH_SHORT).show();
	}
});
dialogFragment.show(getFragmentManager(), "dialog_fragment");

MultiChoiceダイアログ

複数選択で複数選択できるダイアログ。

以下ダイアログクラスのコード

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;

public class MultiChoiceDialog extends DialogFragment {
	private DialogInterface.OnMultiChoiceClickListener multiChoiceClickListener = null;
	private DialogInterface.OnClickListener okClickListener = null;
	private DialogInterface.OnClickListener cancelClickListener = null;
	
	public static MultiChoiceDialog newInstance(int title, String[] items, boolean[] checkedItems) {
		MultiChoiceDialog fragment = new MultiChoiceDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putStringArray("items", items);
		args.putBooleanArray("checked_items", checkedItems);
		fragment.setArguments(args);
		
		return fragment;
	}
	
	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		String[] items = getArguments().getStringArray("items");
		boolean[] checkedItems = getArguments().getBooleanArray("checked_items");
		
		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setTitle(title);
		builder.setMultiChoiceItems(items, checkedItems, this.multiChoiceClickListener)
		.setPositiveButton("OK", this.okClickListener)
        .setNegativeButton("Cancel", this.cancelClickListener);
		
		return builder.create();
	}
	
	/**
     * MultiChoiceクリックリスナーの登録
     */	
	public void setOnMultiChoiceClickListener(DialogInterface.OnMultiChoiceClickListener listener) {
		this.multiChoiceClickListener = listener;
	}
	
	/**
     * OKクリックリスナーの登録
     */	
	public void setOnOkClickListener(DialogInterface.OnClickListener listener) {
		this.okClickListener = listener;
	}
	
	/**
     * Cancelクリックリスナーの登録
     */	
	public void setOnCancelClickListener(DialogInterface.OnClickListener listener) {
		this.cancelClickListener = listener;
	}
}

以下使い方

MultiChoiceDialog dialogFragment = MultiChoiceDialog
		.newInstance(R.string.app_name, items, multiSelectItems);
dialogFragment.setOnMultiChoiceClickListener(new DialogInterface.OnMultiChoiceClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which, boolean isChecked) {
		multiSelectItems[which] = isChecked;
	}
});
dialogFragment.setOnOkClickListener(new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
    	String selectItem = "";
    	for (int i = 0; i < items.length; i++) {
    		if (multiSelectItems[i]) {
    			selectItem += items[i];
    		}
    	}
    	Toast.makeText(MainActivity.this, selectItem + "が選択されました", Toast.LENGTH_SHORT).show();	
    }
});
dialogFragment.setOnCancelClickListener(new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "Cancelがクリックされました", Toast.LENGTH_SHORT).show();
	}
});
dialogFragment.show(getFragmentManager(), "dialog_fragment");

EditTextダイアログ

EditTextを使用して、入力値を取得するダイアログ。

device-2014-03-30-220553

以下ダイアログクラスのコード

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.widget.EditText;

public class EditTextDialog extends DialogFragment {
	private DialogInterface.OnClickListener okClickListener = null;
	private DialogInterface.OnClickListener cancelClickListener = null;
	private EditText editText;
	
	public static EditTextDialog newInstance(int title, int message) {
		EditTextDialog fragment = new EditTextDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putInt("message", message);
		fragment.setArguments(args);
		
		return fragment;
	}
	
	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		int message = getArguments().getInt("message");
		
		AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
		builder.setTitle(title)
		.setMessage(message)
		.setView(this.editText)
		.setPositiveButton("OK", this.okClickListener)
		.setNegativeButton("Cancel", this.cancelClickListener);
		
		return builder.create();
	}
	
	/**
     * OKクリックリスナーの登録
     */	
	public void setOnOkClickListener(DialogInterface.OnClickListener listener) {
		this.okClickListener = listener;
	}
	
	/**
     * Cancelクリックリスナーの登録
     */	
	public void setOnCancelClickListener(DialogInterface.OnClickListener listener) {
		this.cancelClickListener = listener;
	}
	
	/**
     * EditTextの登録
     */	
	public void setEditText(EditText editText) {
		this.editText = editText;
	}
}

以下使い方

editText = new EditText(MainActivity.this);
editText.setText("デフォルト値");

EditTextDialog dialogFragment = EditTextDialog
		.newInstance(R.string.app_name, R.string.dialog_message);
dialogFragment.setOnOkClickListener(new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, editText.getText().toString() + "が入力されました", Toast.LENGTH_SHORT).show();
	}
});
dialogFragment.setOnCancelClickListener(new DialogInterface.OnClickListener() {
	@Override
	public void onClick(DialogInterface dialog, int which) {
		Toast.makeText(MainActivity.this, "Cancelがクリックされました", Toast.LENGTH_SHORT).show();
	}
});
dialogFragment.setEditText(editText);
dialogFragment.show(getFragmentManager(), "dialog_fragment");

SpinningProgressダイアログ

プログレスダイアログ、処理中であることを伝えるダイアログ。
device-2014-03-30-220612

以下ダイアログクラスのコード

import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.os.Bundle;

public class SpinningProgressDialog extends DialogFragment {
	private ProgressDialog progressDialog;
	
	public static SpinningProgressDialog newInstance(int title, int message) {
		SpinningProgressDialog fragment = new SpinningProgressDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putInt("message", message);
		fragment.setArguments(args);
		
		return fragment;
	}
	
	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		int message = getArguments().getInt("message");
		
		progressDialog = new ProgressDialog(getActivity());
		progressDialog.setTitle(title);
		progressDialog.setMessage(getResources().getText(message));
		progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		
		return progressDialog;
	}
}

以下使い方

final int interval = 5000;
final SpinningProgressDialog dialogFragment = SpinningProgressDialog
				.newInstance(R.string.app_name, R.string.dialog_message);
dialogFragment.show(getFragmentManager(), "dialog_fragment");

// 5秒後に終了
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
    	dialogFragment.dismiss();
    }
}, interval);

HorizontalProgressダイアログ

プログレスダイアログ、処理中であることを伝えるダイアログ。処理のパーセントも表示する。
device-2014-03-30-220635

以下ダイアログクラスのコード

import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.os.Bundle;

public class HorizontalProgressDialog extends DialogFragment {
	private ProgressDialog progressDialog;
	
	public static HorizontalProgressDialog newInstance(int title, int message, int max) {
		HorizontalProgressDialog fragment = new HorizontalProgressDialog();
		Bundle args = new Bundle();
		args.putInt("title", title);
		args.putInt("message", message);
		args.putInt("max", max);
		fragment.setArguments(args);
		
		return fragment;
	}
	
	@Override
	public Dialog onCreateDialog(Bundle safedInstanceState) {
		int title = getArguments().getInt("title");
		int message = getArguments().getInt("message");
		int max = getArguments().getInt("max");
		
		progressDialog = new ProgressDialog(getActivity());
		progressDialog.setTitle(title);
		progressDialog.setMessage(getResources().getText(message));
		progressDialog.setMax(max);
		progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		
		return progressDialog;
	}

	/**
     * プログレス値の取得
     */	
	public int getProgress() {
		return progressDialog.getProgress();
	}
	
	/**
     * プログレス値のセット
     */	
	public void setProgress(int value) {
		progressDialog.setProgress(value);
	}
}

以下使い方

// max 10, interval 1000(1秒)
final int maxProgress = 10;
final int interval = 1000;
final HorizontalProgressDialog dialogFragment = HorizontalProgressDialog
			.newInstance(R.string.app_name, R.string.dialog_message, maxProgress);
dialogFragment.show(getFragmentManager(), "dialog_fragment");

// 1秒づつ+1してmax値(10)になったら終了
Handler handler = new Handler() {
	@Override
	public void handleMessage(Message msg) {
		dialogFragment.setProgress(dialogFragment.getProgress() + 1);
		
		// max超えたら終了
		if (dialogFragment.getProgress() >= maxProgress) {
			dialogFragment.dismiss();
		}
		
		sendEmptyMessageDelayed(0, interval);
	}
};
handler.sendEmptyMessage(0);

今回作成したサンプルプロジェクト

DialogFragmentSample

少し時間を遅らせて処理をしたい

少し時間を遅らせて処理をしたい。何がしたいかというと、クイズみたなもので番号を入力させ、入力値を判定し、間違っていたら入力値をクリアしたいのだが、入力し終わってからすぐクリアすると、処理が早すぎて自分がどの番号を入力したかわかないままクリアされてしまう。なので1秒くらい待ってから処理をしたい。ということで調べてみた。

Handlerを使えば処理できるみたい。
下のコードは1秒後にToastでメッセージを表示させるサンプル。

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(MainActivity.this, "テスト", Toast.LENGTH_SHORT).show();
    }
}, 1000);

TextViewをスライドさせたい

TextViewをスライドするアニメーションを作りたい。ということで調べてみた。
やりたいのは、TextViewを左から右へスライドさせたい。

xmlファイルを使用するやり方

「res」フォルダに「anim」というフォルダを作成する。そこに「left_in.xml」と「right_out.xml」ファイルをそれぞれ作成する。
left_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set 
	xmlns:android="http://schemas.android.com/apk/res/android">
	<translate
		android:fromXDelta="-100%p"
		android:toXDelta="0"
		android:duration="500"/>
</set>

right_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set 
	xmlns:android="http://schemas.android.com/apk/res/android">
	<translate
		android:fromXDelta="0"
		android:toXDelta="100%p"
		android:duration="500"/>
</set>

メインのjavaのコード

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
	private Animation inFromRightAnimation;
	private Animation outToLeftAnimation;
	private TextView textView1;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		textView1 = (TextView)this.findViewById(R.id.textView1);
		inFromRightAnimation = AnimationUtils.loadAnimation(this, R.anim.right_in);
 		outToLeftAnimation = AnimationUtils.loadAnimation(this, R.anim.left_out);
 		
		Button btButton1 = (Button)findViewById(R.id.button1);
		btButton1.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		textView1.startAnimation(inFromRightAnimation);
        	}
		});
		
		Button btButton2 = (Button)findViewById(R.id.button2);
		btButton2.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		textView1.startAnimation(outToLeftAnimation);
        	}
		});
	}
}

xmlをしないやり方

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
	private TranslateAnimation inFromRightAnimation;
	private TranslateAnimation outToLeftAnimation;
	private TextView textView1;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		textView1 = (TextView)this.findViewById(R.id.textView1);
		
		inFromRightAnimation = new TranslateAnimation(
				Animation.RELATIVE_TO_SELF, 3,
				Animation.RELATIVE_TO_SELF, 0,
				Animation.RELATIVE_TO_SELF, 0,
				Animation.RELATIVE_TO_SELF, 0);
		inFromRightAnimation.setDuration(1000);
		inFromRightAnimation.setFillEnabled(true);
		inFromRightAnimation.setFillAfter(true);
		
		outToLeftAnimation = new TranslateAnimation(
				Animation.RELATIVE_TO_SELF, 0,
				Animation.RELATIVE_TO_SELF, -3,
				Animation.RELATIVE_TO_SELF, 0,
				Animation.RELATIVE_TO_SELF, 0);
		outToLeftAnimation.setDuration(1000);
		outToLeftAnimation.setFillEnabled(true);
		outToLeftAnimation.setFillAfter(true);
		
		Button btButton1 = (Button)findViewById(R.id.button1);
		btButton1.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		textView1.startAnimation(inFromRightAnimation);
        	}
		});
		
		Button btButton2 = (Button)findViewById(R.id.button2);
		btButton2.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		textView1.startAnimation(outToLeftAnimation);
        	}
		});
	}
}

assetsにDBを置いてコピーしたい

やりたいことはあらかじめ作成していおいたDBを使用したい。サンプルを作成してみた。
「MyDatabase.db」というDBを作成しておいて、プロジェクトの「assets」フォルダに置く。アプリインストール時にそれをコピーし、使用する。

1.DBを用意する。

今回は「MyDatabase.db」というDBを作成して、「assets」フォルダに置いた。
中身はこんな感じ。

_id Name
 1  田中
 2  高橋
 3  斉藤

2.「SQLiteOpenHelper」クラスを継承したクラスを作成する。

以下ソース

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "MyDatabase.db";
    public static final int DATABASE_VERSION = 1;
    private Context context = null;

    public DatabaseHelper(Context context) {
    	super(context, DATABASE_NAME, null, DATABASE_VERSION);
    	this.context = context;

    	try {
    		if (! databaseExists()) {
				copyDb();
    		}
		} catch (IOException e) {
			e.printStackTrace();
		}
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    	try {
			copyDb();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }

    /**
   	 * dbファイルの存在チェック
   	 */
    private boolean databaseExists() {
    	File dbFile = new File(this.context.getFilesDir().getParent() + "/databases/" + DATABASE_NAME);

    	return dbFile.exists();
    }

    /**
	 * assetのdbファイルをコピーする
	 */
    private void copyDb() throws IOException {
    	// コピー先フォルダ
    	String copyFolder = this.context.getFilesDir().getParent() + "/databases/";

    	// フォルダがない場合は作成
    	File checkDirectory = new File(copyFolder);
    	if (! checkDirectory.exists()) {
    		checkDirectory.mkdir();
    	}

    	// ファイルのインプット、アウトプットのセット
    	InputStream in = this.context.getAssets().open(DATABASE_NAME);
    	OutputStream out = new FileOutputStream(copyFolder + DATABASE_NAME);

    	// ファイルのコピー
    	byte[] buf = new byte[1024];
    	int len = 0;
    	while ((len = in.read(buf)) > 0) {
    		out.write(buf, 0, len);
    	}

    	// インプット、アウトプットのクローズ
    	in.close();
    	out.close();
    }
}

3.メインのソース

2のクラスを使用し、DBを使用すればDBはコピーされて使用できるようになる。
以下ソース

import android.os.Bundle;
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button button1 = (Button)this.findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				DatabaseHelper dbHelper = new DatabaseHelper(MainActivity.this);
				SQLiteDatabase db = dbHelper.getReadableDatabase();
				Cursor c = db.rawQuery("SELECT * FROM Sample;", null);
				if (c.moveToFirst()) {
					do {
						Log.v("mytag", c.getString(c.getColumnIndex("Name")));
					} while (c.moveToNext());
				}
				c.close();
			}
		});
	}
}

4.実行

実行してみると、以下のログが出力されて、DBがコピーされているのがわかる。

03-02 20:31:32.098: V/mytag(7176): 田中
03-02 20:31:32.098: V/mytag(7176): 高橋
03-02 20:31:32.098: V/mytag(7176): 斉藤

5.DBのアップデート

DBをアップデートしたい場合は、2で作成したクラスの「DATABASE_VERSION 」を1上げて2にし、「assets」フォルダに新しいDBをコピーする。試しに、「MyDatabase.db」の項目を追加し実行してみる。以下DBの中身。

_id Name
 1  田中
 2  高橋
 3  斉藤
 4  白石

実行してみると、以下のログが出力され、アップデートされているのがわかる。

03-02 20:39:05.088: V/mytag(9921): 田中
03-02 20:39:05.088: V/mytag(9921): 高橋
03-02 20:39:05.088: V/mytag(9921): 斉藤
03-02 20:39:05.088: V/mytag(9921): 白石

正直このやり方でいいのかはわからない、あとアプリで静的でないDBを使用したい場合は、別途「SQLiteOpenHelper 」を継承するクラスを作成する必要がある。

サンプルファイル
SqLiteDbCopySample

PreferenceFragmentでクリックイベントを登録したい

PreferenceFragmentでクリックのみの設定項目を追加したい。ということで調べてみた。

以下ソース。

import android.app.Activity;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceFragment;
import android.widget.Toast;

public class Preferences extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getFragmentManager()
        	.beginTransaction()
        	.replace(android.R.id.content, new PrefsFragment())
        	.commit();
    }

    public static class PrefsFragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.preferences);

            Preference preference = (Preference) findPreference("preference_key");
            preference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                public boolean onPreferenceClick(Preference preference) {
                	Toast.makeText(getActivity(), "Preferenceクリック", Toast.LENGTH_SHORT).show();
                	return true;
                }
            });
        }
    }
}

以下レイアウトファイル

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
	xmlns:android="http://schemas.android.com/apk/res/android">
	
	<PreferenceCategory android:title="設定">
	    
	    <Preference
    		android:key="preference_key"
    		android:title="Prefereceクリックのみ"
    		android:summary="Preferenceクリックで何かを処理したい" />
	    
	</PreferenceCategory>
	
</PreferenceScreen>

実行するとこんな感じ
device-2014-02-23-2100212

戻るボタンを制御したい

戻るボタンを制御したい。ということで調べてみた。いろいろやり方はあるみたい。

onKeyDownイベント

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
	if (keyCode == KeyEvent.KEYCODE_BACK){
		return;
	}
	
	return super.onKeyDown(keyCode, event);
}

dispatchKeyEventイベント

@Override
public boolean dispatchKeyEvent(KeyEvent e) {
   if (e.getAction() == KeyEvent.ACTION_DOWN) {
       return;
   }

   return super.dispatchKeyEvent(e);
}

onBackPressedイベント

@Override
public void onBackPressed(){
	super.onBackPressed();
}

onBackPressedを使用した方が簡単そうなので、これを使用することにする。