Сохранение настроек

Введение
Сохранение значений параметров
Чтение значений параметров
Preferences Framework
CheckBoxPreference
EditTextPreference
ListPreference
RingtonePreference
PreferenceCategory
PreferenceScreen

Введение

Очень часто требуется сохранить настройки, управляющие поведением программы. Самый простой способ, который приходит в голову - сбросить данные в файл, а при запуске приложения считывать необходимые данные из файла. Второй вариант - работать с базой данных и хранить настройки там. Есть еще способ хранения настроек в централизованном месте (вспомните рееестр Windows). Но в Android реестра нет, поэтому нам подходит первый вариант.

На самом деле нет необходимости изобретать свой велосипед и придумывать свою структуру для хранения данных. В Android существует класс SharedPreferences, разработанный специально для этих целей.

Для любознательных могу добавить, что файлы, которые будут создаваться этим способом, хранятся в каталоге /data/data/имя_пакета/shared_prefs/имя_файла_настроек.xml. Поэтому в отладочных целях, если вам нужно сбросить настройки в эмуляторе, то при помощи перспективы DDMS, используя файловый менеджер, зайдите в нужную папку, удалите файл настроек и перезапустите эмулятор. На устройстве вы можете удалить программу и поставить ее заново.

Сохранение значений параметров

Для удобства создадим константу для имени файла настроек, например:


public static final String APP_PREFERENCES = "mysettings"; // это будет именем файла настроек

Далее нужно создать параметры, которые вы хотите сохранять в настройках:


public static final String APP_PREFERENCES_NAME = "Nickname"; // тип String
public static final String APP_PREFERENCES_EMAIL = "Email"; // тип String
public static final Int APP_PREFERENCES_AGE = "Age"; // тип Int

Когда вы определили названия параметров, вы можете сохранять любые значения этих параметров. Для этого создаем переменную, представляющую экземпляр класса SharedPreferences:


SharedPreferences mSettings;

Внутри метода onCreate() вы инициализируете эту переменную:


mSettings = getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE);

В этой строке вы передаете в указанный метод название вашего файла (он будет создан автоматически) и стандартное разрешение, используемое для закрытых файлов приложения.

Предположим, что у нас есть текстовые поля, где пользователь ввел свое имя, email и возраст. Чтобы сохранить параметр, нужно получить текст, который ввел пользователь, через метод getText().toString():


EditText nicknameText (EditText)findViewById(R.id.editText_Nick);
String strNickName = nicknameText.getText().toString(); // здесь содержится текст, введенный в текстовом поле

Получив нужный текст, сохраняем его через метод putString (есть также putLong, putBoolean и т.п.):


Editor editor = mSettings.edit();
editor.putString(APP_PREFERENCES_NAME, strNickName);
editor.commit();

Как правило, параметры сохраняют в событиях onPause() или onStop().

Чтение значений параметров

Для считывания данных используют onCreate() или onResume(). Нам нужно получить доступ к настройкам программы и проверить, есть ли среди них нужный нам параметр. Нас интересует параметр Nickname. Если мы его найдем, то загрузим его значение в текстовое поле.


if(mSettings.contains(APP_PREFERENCES_NAME)) {
    nicknameText.setText(mSettings.getString(APP_PREFERENCES_NAME, ""));
}

В этих строчках кода мы проверили существование параметра APP_PREFERENCES_NAME и получили его значение через getString(). Осталось только загрузить полученный результат в текстовое поле.

Аналогично поступаем и с другими параметрами (это вам в качестве домашнего задания).

Preferences Framework

В предыдущих примерах мы просто сохраняли и восстанавливали нужные параметры. Часто для этих целей в приложениях используют отдельный экран с настройками. Безусловно вы можете создать собственный xml-файл разметки и разместить там нужные элементы управления. Но Android для этих целей предоставляет собственный Preferences Framework, с помощью которого можно создавать индивидуальный набор предпочтений и встраивать их в приложения.

Данный подход позволяет быстро создать окно настроек практически без написания кода. Предпочтения — это отдельный экран в приложении, вызываемый из активности. Предпочтения определяются в отдельном XML-файле, где корневым элементом является элемент <PreferenceScreen>, который представляет собой контейнер для предпочтений и может содержать дочерние элементы <PreferenceScreen>. Элемент <PreferenceCategory> также является контейнерным элементом и предназначен для объединения предпочтений в группы.

Для сохранения предпочтений используется четыре класса:

Android позволяет сохранять настройки в виде пары ключ-значение. Можно сохранять любые данные типа string или примитивных типов данных (boolean, int и т.д.).

Предпочтение может быть доступно для единственной активности, а может быть общим для всех активностей приложения. Предпочтение возможно также сделать общим для нескольких приложений.

Для получения доступа к настройкам в коде приложения используются три метода:

  • getPreferences() — внутри активности, чтобы обратиться к определенному для активности предпочтению;
  • getSharedPreferences() — внутри активности, чтобы обратиться к предпочтению на уровне приложения;
  • getDefaultSharedPreferences() — из объекта PreferencesManager, чтобы получить общедоступное предпочтение, предоставляемое Android.

Все эти методы возвращают экземпляр класса SharedPreferences, из которого можно получить соответствующее предпочтение с помощью ряда методов:

  • getBoolean(String key, boolean defValue);
  • getFloat(String key, float defValue);
  • getInt(String key, int defValue);
  • getLong(String key, long defValue);
  • getString(String key, String defValue)

Второй параметр в указанных методах — это значение по умолчанию, возвращаемое в том случае, когда нельзя получить выбранное значение. Например, чтобы получить значение для предпочтения с типом boolean используем следующий код:

 
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 
boolean val = prefs.getBoolean(getString(R.string.pref_item), false));

CheckBoxPreference

Для начала необходимо создать в папке res/xml/ XML-файл ресурсов, например, settings.xml. При создании файла в диалоговом окне установите переключатель на Preference и убедитесь, что корневым элементом файла установлен PreferenceScreen:

Далее устанавливаем настройки.


<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="wifi"
        android:title="@string/wifi_title"
        android:summary="@string/wifi_summary"
        android:defaultValue="true" />
  <CheckBoxPreference
        android:key="hints"
        android:title="@string/hints_title"
        android:summary="@string/hints_summary"
        android:defaultValue="true" />
</PreferenceScreen>

Мы определили две настройки для программы: включение и выключение Wi-Fi и показ подсказок. В нашем случае мы выводим флажки (элемент CheckBoxPreference), которые по умолчанию будут отмечены (атрибут android:defaultValue).

Не забываем добавить новые строковые ресурсы в файл strings.xml:


<string name="wifi_title">Wi-Fi</string>
<string name="wifi_summary">Вкл. или выкл. сеть WiFi</string>
<string name="hints_title">Подсказки</string>
<string name="hints_summary">Показывать подсказки</string>

Далее создаем новый класс Prefs, который будет наследоваться от класса PreferenceActivity:


package ru.alexanderklimov.preferencedemo

import android.os.Bundle;
import android.preference.PreferenceActivity;

public class Prefs extends PreferenceActivity 
{
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
	    super.onCreate(savedInstanceState);
	    addPreferencesFromResource(R.xml.settings);
	}
}

Метод addPreferencesFromResource() считывает установленные настройки из XML-файла, где хранятся наши ресурсы, и делает их доступными для программы. В результате мы должны увидеть настройки на экране. Не забываем зарегистрировать новый Activity в манифесте AndroidManifest.xml:


<activity 
    android:name=".Prefs"
    android:label="@string/settings_title">
</activity>

Напишем метод для вывода окна настроек, который можно повесить на щелчок мыши или пункта меню:


public void showSettings()
{
	Intent i = new Intent(this,Prefs.class);
	startActivity(i);
}

Запустите программу и вызовите окно настроек. Попробуйте снять или установить флажки в разных сочетаниях. Затем закройте приложение и снова запустите его. Если вы откроете окно настроек, то увидите, что сделанные вами изменения, сохранены. Система сама делает за нас всю работу и запоминает установленные настройки. Что при этом происходит за кулисами программы и как хранятся данные на устройсте - тема отдельного разговора.

EditTextPreference

Мы научились сохранять настройки при помощи флажков. Существует также возможность сохранения настроек в текстовом виде - EditTextPreference. Подобный способ настройки позволят сохранять текст, вводимый пользователем. Давайте реализуем эту возможность в нашем текстовом редактеоре - добавим возможность устанавливать размер шрифта для текста. Откроем снова файл preferences.xml и добавим новый элемент EditTextPreference:


<EditTextPreference
    android:key="@string/pref_size"
    android:title="Размер шрифта"
    android:summary="Устанавливает новый размер шрифта"
    android:defaultValue="14"
    android:dialogTitle="Введите размер шрифта (от 10 до 32)" />

В метод onResume() добавим новый код для чтения установленного значения размера шрифта:


// читаем размер шрифта из EditTextPreference 
float fSize = Float.parseFloat( 
    prefs.getString(getString(R.string.pref_size), "20")); 
// применяем настройки в текстовом поле 
myEdit.setTextSize(fSize);

Запустите проект и вызовите окно настроек. Теперь у нас появилась опция установки размера шрифта с треугольником. Если щелкнуть на треугольнике, то откроется новое диалоговое окно с текстовым полем ввода.

Настройки Настройки

Обращаю ваше внимание, что в нашем примере не проверяется пользовательский ввод, что может привести к ошибкам, если вместо числового значения для размера шрифта пользователь введет слово Кот или любое другое слово из трех букв. Никогда не доверяйте пользователю!

ListPreference

Также можно использовать списки для хранения настроек. Для этой цели используется диалоговое окно ListPreference. Необходимо заранее подготовить строковый ресурс для заголовка и массив строк для самого списка значений. Индекс выбранной строки списка будет задавать нужное значение для сохранения. в SharedPreferences.

Продолжим работу с текстовым редактором и добавим в него список для выбора стиля текста. В списке будет четыре опции: Обычный, Полужирный, Курсив, Полужирный+Курсив.

Подготовим массив строк и сохраним его в файле arrays.xml, который необходимо поместить в каталог res/values/.


<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string-array name="text_style">
		<item>Обычный</item>
		<item>Полужирный</item>
		<item>Курсив</item>
		<item>Полужирный+Курсив</item>
	</string-array>
</resources>

В файл preferences.xml добавим дополнительный элемент <ListPreference>, в котором определим атрибуты заголовка окна, привязку к массиву значений и значение по умолчанию:


<ListPreference
	android:key="@string/pref_style"
	android:title="Стиль для шрифта"
	android:summary="Устанавливает стиль для шрифта"
	android:defaultValue="1"
	android:entries="@array/text_style"
	android:entryValues="@array/text_style"
	android:dialogTitle="Выберите стиль для шрифта" />

Для чтения настроек из списка добавляем код в метод onResume():


// читаем стили текста из ListPreference 
String regular = prefs.getString(getString(R.string.pref_style), "");
int typeface = Typeface.NORMAL; 

if (regular.contains("Полужирный")) 
    typeface += Typeface.BOLD; 

if (regular.contains("Курсив")) 
    typeface += Typeface.ITALIC; 

// меняем настройки в EditText 
//myEdit.setTextSize(fSize); 
myEdit.setTypeface(null, typeface);

Запустив проект, вы теперь увидите новую настройку Стиль для шрифта, которая открывает диалоговое окно для выбора стиля из списка. Обратите внимание, что в диалоговом окне нет кнопки сохранения, только Отмена. Изменения сохраняются сразу при выборе опции списка.

Настройки Настройки

Примеры использования различных настроек использованы в примере создания простого текстового редактора.

RingtonePreference

Рассмотрим работу с настройкой, связанной с установкой мелодии для звонка. Предпочтение <RingtonePreference> предоставляет диалоговое окно выбора мелодии звонка со списком опций. Список в диалоговом окне отображает мелодии для звонка, уведомлений, тонового набора, доступные на мобильном устройстве. Также предусмотрена возможность добавления дополнительной опции Silent (Тихий режим) - добавьте атрибут android:showSilent="true".

Создадим файл настроек preferences.xml:


<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
	xmlns:android="http://schemas.android.com/apk/res/android">

	<RingtonePreference
		android:key="@string/setting_ringtone"
		android:title="@string/setting_ringtone"
		android:showDefault="true"
		android:showSilent="true"
		android:summary="Устанавливает мелодию для звонка (вкл. или выкл.)" /> 	
</PreferenceScreen>

Код для запуска окна настроек


Intent intent = new Intent(); 
intent.setClass(this, Prefs.class); 
startActivity(intent); 
Настройки Настройки

PreferenceCategory

Если в приложении используется слишком много настроек разного типа, то можно сгруппировать их по категориям, чтобы улучшить внешний вид окна настроек. Для этого в Preferences Framework есть специальный элемент PreferenceCategory.

Еще раз вернемся к статье Создание простого текстового редактора, где использовались настройки для открытия файла и для работы с текстом (Размер шрифта и стиль шрифта). Почему бы нам не разбить настройки на две категории: к первой категории мы отнесем настройку открытия файла, а ко второй две настройки, связанные с шрифтами.

Категории добавляются через элемент <PreferenceCategory> под корневым элементом <PreferenceScreen>, и в него уже помещаются нужные настройки:


<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
	xmlns:android="http://schemas.android.com/apk/res/android">
	<PreferenceCategory
		android:title="Работа с файлами">
		<CheckBoxPreference
			android:key="@string/pref_openmode"
			android:title="Открыть файл"
			android:summary="Открывать файл при запуске приложения" />
	</PreferenceCategory>

	<PreferenceCategory
		android:title="Настройки шрифта">
		<EditTextPreference
			android:key="@string/pref_size"
			android:title="Размер шрифта"
			android:summary="Устанавливает новый размер шрифта"
			android:defaultValue="14"
			android:dialogTitle="Введите размер шрифта (от 10 до 32)" />

		<ListPreference
			android:key="@string/pref_style"
			android:title="Стиль для шрифта"
			android:summary="Устанавливает стиль для шрифта"
			android:defaultValue="1"
			android:entries="@array/text_style"
			android:entryValues="@array/text_style"
			android:dialogTitle="Выберите стиль для шрифта" />
	</PreferenceCategory>
</PreferenceScreen>

Если вы запустите приложение для текстового редактора с добавленными категориями, то увидите дополнительную полоску с текстом. Сравните два рисунка до и после добавления элемента PreferenceCategory:

PreferenceScreen

Кроме корневого элемента <PreferenceScreen> в файле настроек можно использовать вложенные дочерние элементы <PreferenceScreen>, которые будут отображаться в отдельном окне. Родительский экран <PreferenceScreen> в этом случае будет отображать вход для запуска дочернего экрана настроек. Продолжим опыты над текстовым редактором и добавим еще одну настройку, управляющую цветом (выделено жирным).


<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
	xmlns:android="http://schemas.android.com/apk/res/android">
	<PreferenceCategory
		android:title="Работа с файлами">
		<CheckBoxPreference
			android:key="@string/pref_openmode"
			android:title="Открыть файл"
			android:summary="Открывать файл при запуске приложения" />
	</PreferenceCategory>

	<PreferenceCategory
		android:title="Настройки шрифта">

		<EditTextPreference
			android:key="@string/pref_size"
			android:title="Размер шрифта"
			android:summary="Устанавливает новый размер шрифта"
			android:defaultValue="14"
			android:dialogTitle="Введите размер шрифта (от 10 до 32)" />

		<ListPreference
			android:key="@string/pref_style"
			android:title="Стиль для шрифта"
			android:summary="Устанавливает стиль для шрифта"
			android:defaultValue="1"
			android:entries="@array/text_style"
			android:entryValues="@array/text_style"
			android:dialogTitle="Выберите стиль для шрифта" />
			
		<PreferenceScreen
			android:key="@string/pref_color"
			android:title="Цвет текста"
			android:summary="Устанавливает цвет для текста">

			<CheckBoxPreference
				android:key="@string/pref_color_black"
				android:title="Черный"
				android:defaultValue="true"
				android:summary="Устанавливает черный цвет" />
			<CheckBoxPreference
				android:key="@string/pref_color_red"
				android:title="Красный"
				android:summary="Устанавливает красный цвет" />
			<CheckBoxPreference
				android:key="@string/pref_color_green"
				android:title="Зеленый"
				android:summary="Устанавливает зеленый цвет" />
			<CheckBoxPreference
				android:key="@string/pref_color_blue"
				android:title="Синий"
				android:summary="Устанавливает синий цвет" />
		</PreferenceScreen>
		
	</PreferenceCategory>
</PreferenceScreen>

Запустив приложение, вы увидите новую настройку Цвет текста, которая открывает новое окно для выбора цвета текста.

Вам остается только написать код для обработки выбора пользователя, чтобы вывести текст в текстовом редакторе в нужном цвете. Обратите внимание, что в нашем примере используются флажки, которые позволяют создавать комбинации различных цветов. Например, сочетание красного и синего цвета дают красивый цвет, который понравится известному дизайнеру Артемию Лебедеву.

Исходный код для текстового редактора смотрите здесь