設定画面と、設定値のファイル保存が一体となっているAndroidのPreferenceの使い方メモ
Preferenceの保存先
Preferenceの保存は、/data/data/com.example.test_app/shared_prefs/○○○.xml
にXML形式で保存されている。なお、ファイラーなどで確認する場合は、ルート権限が必要。xmlは次のような内容となっている。
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <boolean name="keyCheckboxPref" value="true" /> <string name="keyEditTextPref">test</string> <string name="keyListPref">2</string> <string name="ringtone_pref">content://settings/system/ringtone</string> </map>
値をPreferenceに格納、読出する単純例
書き込みは次のように行う。書き込む変数の種類により、putBoolean, putInt, putFloat, putLong, putString, putStringSet などがあるので適宜使い分ける。
SharedPreferences pref = getSharedPreferences("SAMPLE_DIR_1", Context.MODE_PRIVATE); SharedPreferences.Editor editor = pref.edit(); editor.putString("TEST_ITEM_1", "テスト文字列"); editor.commit();
読み込みはこのような感じで
SharedPreferences pref = getSharedPreferences("SAMPLE_DIR_1", Context.MODE_PRIVATE); String str = pref.getString("TEST_ITEM_1", "デフォルト値");
プリファレンス・データは、
/data/data/com.example.test_app/shared_prefs/SAMPLE_DIR_1.xml
に書き込まれる。
プリファレンス画面を表示する単純例(API 10以下も対応)
■ プリファレンス画面 XML の新規作成
Eclipseのパッケージ・エクスプローラまたはナビゲーターで、プロジェクトを選択した状態で、「ファイル」ー「新規」ー「その他」を選択して、「Android XMLファイル」の生成を選択する。
「リソース・タイプ」を“Preference”とし、「ルート要素」は“PreferenceScreen”として、「完了」ボタンを押す。
開発環境で生成されたres/xmlファイルを開き、
作成したいリソース、今回はチェックボックスを追加し、“Attribute from Preference”カテゴリーの“Key”と“Title”を記述する。“Key”は実際にXMLに書き込まれる時のキー名になるので、他のプリファレンス画面と重複しないようにする。
このようにして完成したプリファレンスxmlファイルは次のようなものになる
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <CheckBoxPreference android:key="TEST_CHECKBOX" android:title="チェックボックス"/> </PreferenceScreen>
■ プリファレンス画面のクラスの新規作成
次に、このプリファレンスxmlを実装するクラスを作成する。
Eclipseのパッケージ・エクスプローラまたはナビゲーターで、プロジェクトを選択した状態で、「ファイル」ー「新規」ー「クラス」の新規作成を行う。
赤で囲った部分を設定していく。「スーパークラス」は“PreferenceActivity”クラスを指定する。クラスを作成したら、それを開いて、onCreate関数を作成する。パッケージ・エクスプローラまたはナビゲーターで、今回作成したプリファレンスのクラスを選択した状態で、「ソース」ー「メソッドのオーバーライド/実装」を選択する。
メソッド一覧の所で、キーボードから関数名を入力すると、インクリメントサーチが掛かる。onCreateのチェックボックスをONにして、OKボタンを押す。
最終的に、プリファレンスのクラスファイルは次のようになる。
package com.example.android_test_prefs;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class PreferenceTest extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO 自動生成されたメソッド・スタブ
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preference_test);
}
}
赤で着色した部分のみ、手入力している。ここで、プリファレンス画面設計のxmlを紐付けている。プリファレンス・データの保存のためのコーディング無いのは、これが全て自動で行われてしまうから。さすが、便利だ…
プリファレンス・データの実体ファイルは
/data/data/com.example.test_app/shared_prefs/com.example.test_app_preferences.xml
に保存される。
■ プリファレンス画面の呼び出しと、値の取り出しの実装 (MainActivity)
そして、MainActivityからプリファレンス画面を呼び出す処理などを追加すれば、サンプルコードの完成だ。
package com.example.android_test_prefs; import android.support.v7.app.ActionBarActivity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; public class MainActivity extends ActionBarActivity { final int REQUEST_PREF_MENU = 0x1010; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if(id == R.id.menu_pref_old){ Intent intent = new Intent(this, PreferenceTest.class); startActivityForResult(intent, REQUEST_PREF_MENU); return true; } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int arg0, int arg1, Intent arg2) { // TODO 自動生成されたメソッド・スタブ super.onActivityResult(arg0, arg1, arg2); if(arg0 == REQUEST_PREF_MENU){ SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); Boolean bool_checkbox = pref.getBoolean("TEST_CHECKBOX", false); if(bool_checkbox) Toast.makeText(MainActivity.this, "チェックボックスは ON", Toast.LENGTH_LONG).show(); else Toast.makeText(MainActivity.this, "チェックボックスは OFF", Toast.LENGTH_LONG).show(); } } }
緑で着色した部分はEclipseで自動生成した所、赤で書かれた部分がて入力した所。intentの受け取り側関数onActivityResultも、プリファレンスのonCreate関数作成と同じく、ほぼ自動生成に頼っている。
■ 新規作成したプリファレンス画面クラスのjavaファイルをプロジェクトに追加する
プロジェクトを実行する前に、作成したプリファレンス・クラスを読み込むよう、Manifestファイルに追加してやる。AndroidManifest.xmlを開いて、アプリケーション画面の一番下に追加位置がある。
実際のxmlファイルは次のようになる
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android_test_prefs"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="PreferenceTest"></activity>
</application>
</manifest>
プリファレンス画面を表示する単純例(API 11以上の推奨例)
API 11位上ではPreferenceFragmentクラスを使うことが推奨されるため、Android 4以降でしか実行しないプログラムであれば、次のようにコーディングする。
■ プリファレンス画面のクラスの新規作成
Activity クラスでプリファレンス画面のクラスを新規作成し、実際のxmlはその内部クラスにPreferenceFragmentクラスを承継した新規クラスを作成し xml を紐付けるという、ひと手間掛かるめんどくささ…
上の例(API10版)のように、プリファレンス画面の新規クラスを作成し(但し、今回はActivityから派生する)、その後、下に示すような設定で「内部クラス」としてプリファレンス・フラグメントのクラスを作成する。
最終的に、プリファレンス画面のクラスは次のようになる。手入力したのは、赤で着色した部分のみ。
package com.example.android_test_prefs; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import android.preference.PreferenceFragment; @SuppressLint("NewApi") public class PreferenceTestNew extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO 自動生成されたメソッド・スタブ super.onCreate(savedInstanceState); getFragmentManager().beginTransaction() .replace(android.R.id.content, new PreferenceTestFragment()) .commit(); } public class PreferenceTestFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { // TODO 自動生成されたメソッド・スタブ super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preference_test); } } }
■ プリファレンス画面の呼び出しと、値の取り出しの実装 (MainActivity)
API10での例と同じだが、Android 2系で実行された場合の異常終了を避けるため、下の例のようにチェックを設けてもよい。
if(Build.VERSION.SDK_INT >= 11){
Intent intent = new Intent(this, PreferenceTestNew.class);
startActivityForResult(intent, REQUEST_PREF_MENU);
}
この部分以外はAPI10のサンプルコードと同じ。
PreferenceActivityやAlertDialogを使った画面例
メイン画面(Preference現在値を表示)とメニュー表示
Preference設定画面
Preferenceテキスト入力ダイアログ
Preferenceリスト選択ダイアログ
XMLでPreference設定画面を定義し、表示する場合
■ プリファレンス画面の XML
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <CheckBoxPreference android:key="keyCheckboxPref" android:summary="現在の値:-" android:title="チェックボックス" /> <EditTextPreference android:key="keyEditTextPref" android:summary="現在の値:-" android:title="テキスト入力" android:inputType="text" android:maxLength="10" /> <!-- inputType=text : 改行禁止 --> <ListPreference android:key="keyListPref" android:entries="@array/listOptions" android:entryValues="@array/listValues" android:summary="現在の値:-" android:title="リスト選択" /> <PreferenceCategory android:key="pref_category_2" android:title="第2階層の設定画面を表示" > <RingtonePreference android:key="keyRingtonePref" android:title="Ringtone" android:summary="現在の値:-" android:ringtoneType="ringtone" /> </PreferenceCategory> </PreferenceScreen>
■ リスト項目定義のXML
<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="listOptions"> <item>選択肢1</item> <item>選択肢2</item> <item>選択肢3</item> </string-array> <string-array name="listValues"> <item>1</item> <item>2</item> <item>3</item> </string-array> </resources>
■ プリファレンスう画面のクラス
Preferenceへの値の格納は、特にコードを記述しなくても自動で行われる。次のコーディングは、設定値を即時プリファレンス・メニュー画面上(summary)に反映するためのもので、それが必要無いのであれば、このクラス関数onCreateは自動作成された状態の「空」で構わない。
package com.example.android_settings_menu_1; import android.os.Bundle; import android.preference.*; import android.preference.Preference.OnPreferenceChangeListener; public class SettingsMenu extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings_menu); // チェックボックスの現在値表示 CheckBoxPreference cCheckBox1 = (CheckBoxPreference)findPreference("keyCheckboxPref"); // 画面初期化時 if(cCheckBox1.isChecked()){ cCheckBox1.setSummary("現在の値:ON"); } else{ cCheckBox1.setSummary("現在の値:OFF"); } // ユーザによる選択書き換え時 cCheckBox1.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object value) { if(((Boolean)value).booleanValue()) { pref.setSummary("現在の値:ON"); } else { pref.setSummary("現在の値:OFF"); } return true; } }); // テキストボックスの現在値表示 EditTextPreference cEditText1 = (EditTextPreference)findPreference("keyEditTextPref"); // 画面初期化時 cEditText1.setSummary("現在の値:" + cEditText1.getText()); // ユーザによる選択書き換え時 cEditText1.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object value) { pref.setSummary("現在の値:" + value.toString()); return true; } }); // リスト選択の現在値表示 ListPreference cList1 = (ListPreference)findPreference("keyListPref"); // 画面初期化時 cList1.setSummary("現在の値:" + cList1.getValue()); // ユーザによる選択書き換え時 cList1.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object value) { pref.setSummary("現在の値:" + value.toString()); return true; } }); } }
■ プリファレンスの呼び出し・値の読み込みの実装 (MainActivity)
package com.example.android_settings_menu_1; import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBar; import android.support.v4.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.os.Build; import android.widget.LinearLayout; import android.widget.TextView; import android.app.AlertDialog; import android.widget.EditText; import android.text.InputType; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.widget.Toast; public class MainActivity extends ActionBarActivity { private int idText = 0x1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 現在の設定値を表示するテキスト領域を確保する LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); setContentView(layout); TextView text = new TextView(this); text.setId(idText); layout.addView(text); // 画面に現在の設定値を表示する DisplayPrefOnScreen(); } // 画面に現在の設定値を表示する private void DisplayPrefOnScreen() { SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); String str; if(pref.getBoolean("keyCheckboxPref", false)) { str = "チェックボックス:ON\n"; } else { str = "チェックボックス:ON\n"; } str = str + "テキスト入力:" + pref.getString("keyEditTextPref", "") + "\n"; str = str + "リスト選択:" + pref.getString("keyListPref", "---"); TextView text = (TextView) this.findViewById(idText); text.setText(str); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { // SettingsMenuクラスを呼び出して、プリファレンスメニューの初期化と表示を行う Intent intent = new Intent(this, SettingsMenu.class); // 設定完了時のコールバック startActivityForResult(intent, 10011); return true; } return super.onOptionsItemSelected(item); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (requestCode == 10011){ // 画面に現在の設定値を表示する DisplayPrefOnScreen(); } } }
■ 新規作成したプリファレンス画面クラスのjavaファイルをプロジェクトに追加する
src/.../SettingsMenu.java
を手動登録する必要がある。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android_settings_menu_1"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="10" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.android_settings_menu_1.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SettingsMenu" >
</activity>
</application>
</manifest>
プログラム中でPreferenceデータを読み書きする
メニューにPreferenceデータを読み書きする「テキスト入力」を追加する場合の説明
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
// SettingsMenuクラスを呼び出して、プリファレンスメニューの初期化と表示を行う
Intent intent = new Intent(this, SettingsMenu.class);
// 設定完了時のコールバック
startActivityForResult(intent, 10011);
return true;
}
else if (id == R.id.action_edittext) {
// 現在の「テキスト入力値」をプリファレンスxmlより読みだす
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
String str = pref.getString("keyEditTextPref", "");
// テキスト入力AlertDialogを表示する
final EditText editView = new EditText(MainActivity.this);
editView.setLines(1); // 1行
editView.setInputType(InputType.TYPE_CLASS_TEXT); // 改行を許可しない
// AlertDialogを構築する
AlertDialog.Builder dlg = new AlertDialog.Builder(this);
dlg.setTitle("テキスト入力");
dlg.setView(editView);
editView.setText(str);
dlg.setPositiveButton("OK", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
// 入力された文字列をプリファレンスxmlに書き込む
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor edit = pref.edit();
edit.putString("keyEditTextPref", editView.getText().toString());
edit.commit();
// 画面に現在の設定値を表示する
DisplayPrefOnScreen();
}
});
dlg.setNegativeButton("キャンセル", null);
dlg.show();
return true;
}
return super.onOptionsItemSelected(item);
}
javaでPreference設定画面を定義し、表示する場合
package com.example.android_settings_menu_2; import android.os.Bundle; import android.preference.*; import android.preference.Preference.OnPreferenceChangeListener; public class SettingsMenu extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PreferenceScreen pref = getPreferenceManager().createPreferenceScreen(this); // チェックボックスを作成 CheckBoxPreference cCheckBox1 = new CheckBoxPreference(this); cCheckBox1.setKey("keyCheckboxPref"); cCheckBox1.setTitle("チェックボックス"); // メニューに追加 pref.addPreference(cCheckBox1); // 画面初期化時 if(cCheckBox1.isChecked()){ cCheckBox1.setSummary("現在の値:ON"); } else{ cCheckBox1.setSummary("現在の値:OFF"); } // ユーザによる選択書き換え時 cCheckBox1.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object value) { if(((Boolean)value).booleanValue()) { pref.setSummary("現在の値:ON"); } else { pref.setSummary("現在の値:OFF"); } return true; } }); // チェックボックスを作成 EditTextPreference cEditText1 = new EditTextPreference(this); cEditText1.setKey("keyEditTextPref"); cEditText1.setTitle("テキスト入力"); cEditText1.setDialogTitle("テキスト入力"); // メニューに追加 pref.addPreference(cEditText1); // 画面初期化時 cEditText1.setSummary("現在の値:" + cEditText1.getText()); // ユーザによる選択書き換え時 cEditText1.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object value) { pref.setSummary("現在の値:" + value.toString()); return true; } }); CharSequence[] arrayListTitle = new String[] { "選択肢1", "選択肢2", "選択肢3" }; CharSequence[] arrayListValue = new String[] { "1", "2", "3" }; // リストを作成 ListPreference cList1 = new ListPreference(this); cList1.setKey("keyListPref"); cList1.setTitle("リスト選択"); cList1.setEntries(arrayListTitle); cList1.setEntryValues(arrayListValue); // メニューに追加 pref.addPreference(cList1); // 画面初期化時 cList1.setSummary("現在の値:" + cList1.getValue()); // ユーザによる選択書き換え時 cList1.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference pref, Object value) { pref.setSummary("現在の値:" + value.toString()); return true; } }); // プリファレンス メニューを画面表示する setPreferenceScreen(pref); } }