|
Пишем игру для Android. Часть 5. Хранение настроек
27.03.2009
Источник: megadarja.blogspot.com
Вот мы и добрались до конца. Осталось сделать только главное меню
приложения, а также сделать игре настройки (собственно, меню только для
того и нужно, чтобы было откуда настройки вызывать). Ну первое мы еще с
прошлой статьи умеем, так что особых сложностей быть не должно. А вот
второе следует рассмотреть подробнее. Итак, приступим. Окно приветствияВ одной из прошлых статей
подробно рассматривался вопрос, как создавать формы в приложении для
Android и делать переходы между ними. Так что особо останавливаться я
не буду, и так все ясно. На нашей форме приветствия будет
какая-нибудь картинка и три кнопки: "Начать игру", "Настройки" и
"Выход". Картинку в формате png, которую мы назовем start.png нужно положить в папку /res/drawable. Названия кнопок нужно вынести в strings.xml, добавив следующие строки: res/values/strings.xml<resources> <string name="app_name">PingPong</string>
<string name="start_title">Start Game</string> <string name="settings_title">Settings</string> <string name="exit_title">Exit</string>
</resources>
Тогда разметка для новой формы будет выглядеть так: res/layout/start.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" android:gravity="bottom" android:background="@drawable/start" android:padding="8dip">
<Button android:id="@+id/StartButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textStyle="bold" android:text="@string/start_title" />
<Button android:id="@+id/SettingsButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textStyle="bold" android:text="@string/settings_title" />
<Button android:id="@+id/ExitButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textStyle="bold" android:text="@string/exit_title" /> </LinearLayout>
Фоновое изображение можно задать экрану с помощью полезного свойства android:background. Собственно, так можно задавать фон и кнопкам, и вообще чему угодно. Получили вот такую разметку: Добавим соответствующий этой разметке класс StartScreen.java. Сразу обработаем нажатия всех кнопок: StartScreen.javapublic class StartScreen extends Activity implements OnClickListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.start);
Button startButton = (Button)findViewById(R.id.StartButton); startButton.setOnClickListener(this);
Button exitButton = (Button)findViewById(R.id.ExitButton); exitButton.setOnClickListener(this);
Button settingsButton = (Button)findViewById(R.id.SettingsButton); settingsButton.setOnClickListener(this); }
/** Обработка нажатия кнопок */ public void onClick(View v) { switch (v.getId()) { case R.id.StartButton: { Intent intent = new Intent(); intent.setClass(this, GameScreen.class); startActivity(intent); break; }
case R.id.SettingsButton: { break; }
case R.id.ExitButton: finish(); break;
default: break; } } }
По нажатию на кнопку Start происходит переход на экран с игрой. Обратите внимание, что StartScreen при этом не закрывается. Это значит, что, когда закроется StartScreen, мы попадем обратно на экран приветствия. По нажатию на Settings пока что ничего не происходит, по Exit — приложение закрывается. Осталось только зарегистрировать эту форму в приложении и сделать ее главной. Для этого идем в AndroidManifest.xml: AndroidManifest.xml<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.pingpong" android:versionCode="1" android:versionName="1.0.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".GameScreen" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity>
<activity android:name=".StartScreen"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
</application> </manifest>
Теперь приложение начинается с StartScreen, все кнопки работают. НастройкиЯ
сделаю в настройках два параметра — максимальное количество очков и
сложность. Сложность игры будем менять, варьируя скорость мячика и
ракеток. Сама форма с настройками делается достаточно просто. Есть специальный наследник класса Activity — PreferenceActivity,
который именно для этого и предназначен. Когда мы хотим сделать форму с
настройками, нужно унаследоваться именно от него, и он возьмет на себя
большую часть рутины. РазметкаРазметка для формы с настройками выглядит несколько необычно: res/xml/preferences.xml<?xml version="1.0" encoding="UTF-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > <PreferenceCategory android:title="@string/prefs_title">
<ListPreference android:key="@string/pref_difficulty" android:title="@string/difficulty_title" android:entries="@array/difficulty" android:entryValues="@array/difficulty" android:defaultValue="1" />
<EditTextPreference android:key="@string/pref_max_score" android:title="@string/score_title" android:defaultValue="10" /> </PreferenceCategory> </PreferenceScreen>
Настолько необычно, что, если поместить этот XML в папку
layout, eclipse начнет ругаться, что не может разрезолвить такие
классы. Собственно, это не просто разметка: там также содержатся ключи
настроек и значения по умолчанию. Поэтому мы создадим отдельную папку xml, и поместим этот файл туда. А теперь обо всем по порядку. PreferenceCategoryНу, PreferenceScreen все понятно, а что такое PreferenceCategory?
Как ни удивительно, это категория настроек. Например, у какой-нибудь
игры могут быть настройки графики, настройки звука, настройки сети и
т.д.. Удобно отобразить их сгруппированными, вот так: А можно обойтись без групп: PreferenceScreen
уже сам по себе является контейнером для настроек. В нашем случае,
например, настроек мало и группировать нечего. Но это я так, для
полноты картины. Какие можно сделать настройкиКак видно
даже не прошлой картинке, настройки могут быть разными. И флажки, и
текстовые поля, и списки. Все они происходят от одного класса Preference, и наследуют от него всякие полезные атрибуты, которые можно задавать в разметке, как то: - android:title
- Заголовок настройки или контейнера настроек.
- android:summary
- Краткое описание. Проще говоря, это то, что пишется под заголовком мелким шрифтом.
- android:defaultValue
- Значение по умолчанию
- android:key
- Ключ, с которым данная настройка будет храниться и с которым можно будет к ней обращаться.
- android:dependency
- Задает
зависимость от другого контрола. Например, можно поставить эдитору
зависимость от флажка, и тогда, если флажок не установлен, но эдитор
будет неактивен.
Ну и еще кое-что. Рассмотрим некоторые конкретные виды настроек. CheckBoxPreferenceВот такой флажок: EditTextPreferenceРедактор текста. Честно
говоря, у редактора текста очень хотелось бы валидатор, а еще лучше
маску. Например, IP-адрес имеет специфический формат, и хотелось бы
запрещать пользователю вводить там что попало. Но мне такую
функциональность так и не удалось обнаружить. ListPreferenceСвоеобразная реализация Dropdown-а. Хотя, на телефоне наверно и вправду так удобнее. На
этом контроле хотелось бы остановиться подробнее. А конкретнее,
рассказать, откуда он, собственно, берет элементы списка. А берет он их
из ресурсов с помощью таких атрибутов. - android:entries
Здесь
хранится ссылка на ресурс, в котором хранятся отображаемые элементы
списка. Все значения, которые хочется вынести в ресурсы, хранятся в
папке values. До сих пор там была только одна XML-ка — strings.xml. Но теперь надо добавить еще одну — arrays.xml. И добавить в узел resources следующее: <string-array name="performance"> <item>Best performance</item> <item>Normal performance and appearance</item> <item>Best appearance</item> </string-array>
После этого можно смело указывать в android:entries этот ресурс, список будет загружен. Кстати говоря, в item-ах может быть не непосредственно строка, а ссылка на строку в strings.xml. Например, в нашем случае будет так (разумеется, стоит добавить соответствующие значения в strings.xml): res/values/arrays.xml<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="difficulty"> <item>@string/difficulty_easy</item> <item>@string/difficulty_normal</item> <item>@string/difficulty_hard</item> </string-array> </resources>
- android:entryValues
- Список действительных
значений. Также ссылка на ресурс, и задавать можно так же. Если кодов
будет меньше, чем значений, приложение будет валиться с исключением.
Если больше — не будет. В нашем случае можно в entries и entryValues
смело задавать одно и то же, но бывает, когда имеет смысл их разделять.
А еще мне никак не удалось победить у ListPreference атрибут adnroid:defaultValue. Не работает и все. RingtonePreferenceНастройка
звукового сигнала. В нашем приложении, например, можно было бы добавить
звук, с которым мячик ударяется о стенку, и выбирать этот звук с
помощью такого контрола. Выглядит это так: Класс формыКласс для формы с настройками будет выглядеть так: SettingsScreen.javapublic class SettingsScreen extends PreferenceActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); } }
Кстати, настройки необязательно загружать из XML-ки,
можно добавлять все эти настройки прямо в коде конструктора. В сэмплах,
которые идут с Android SDK, такие примеры есть. Добавляем в StartScreen код для открытия формы настроек, прописываем SettingsScreen в AndroidManifest.xml. (Все выглядит точно так же, как и для GameScreen, так что листинги не привожу). И увидим мы следующее: Не
знаю, кому как, а мне нравится, когда рядом с названием опции написано
ее значение. Но как это сделать автоматически, я так и не нашла,
пришлось все делать руками, используя для этого поле summary. Итак, summary настройки должно обновляться при изменении значения. По счастью, есть такое событие OnPreferenceChange. Итак, пишем: SettingsScreen.javapublic class SettingsScreen extends PreferenceActivity implements Preference.OnPreferenceChangeListener { /* Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
ListPreference difficulty = (ListPreference)this.findPreference("pref_difficulty"); difficulty.setSummary(difficulty.getEntry()); difficulty.setOnPreferenceChangeListener(this);
EditTextPreference maxScore = (EditTextPreference)this.findPreference("pref_max_score"); maxScore.setSummary(maxScore.getText()); maxScore.setOnPreferenceChangeListener(this);
}
public boolean onPreferenceChange(Preference preference, Object newValue) { preference.setSummary((CharSequence)newValue);
return true; }
}
Думаю, все понятно без слов. Теперь мы видим такую картину: И, если мы будем менять настройки, изменения сразу же будут отображаться в summary. Использование настроек в других формахНу
все, настройки мы сделали, они как-то сами где-то сохранились. Теперь
возникла необходимость их прочитать и что-нибудь с ними сделать. Читать
и делать мы будем в классе GameManager, а конкретно, в конструкторе. Для работы с сохраненными настройками приложения используется класс SharedPreferences. Вся работа по чтению и применению настроек выглядит так: SettingsScreen.javapublic GameManager(SurfaceHolder surfaceHolder, Context context) { ...
...
...
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
String difficulty = settings.getString(res.getString(R.string.pref_difficulty), res.getString(R.string.difficulty_normal)); setDifficulty(difficulty);
int maxScore = Integer.parseInt(settings.getString( res.getString(R.string.pref_max_score), "10")); setMaxScore(maxScore);
}
Метод setDifficulty приводить не буду, там ничего особо умного не написано. Настройки из SharedPreferences можно читать с помощью методов getString, getInt, getBoolean и т.п. Все они принимают два параметра &mdahs; ключ к настройке (то, что мы задавали с помощью атрибута android:key) и значение по умолчанию. Однако, воспользоваться чем-то кроме getString мне так и не удалось. ЗаключениеИтак,
мануал закончен. Получился он огромным, но при этом собственно про
андроид оказалось не так уж и много, что самое-то обидное :( Спасибо,
если кто дочитал до конца. Буду рада услышать любые мнения. Отдельное спасибо xeye и std.denis А вот и все исходники
|
Комментарии
RSS лента комментариев этой записи.