1. Игровое поле 6х6 с картинками (часть 1)
2. Учет количество ходов (или времени) (часть 2)
3. Вывод таблицы рекордов
4. Настройки: выбор цвета фона, набора картинок (часть 3)
5. Начальный экран (часть 2)
Осталось только сохранять кол-во очков и время в конце каждой игры и выводить 5 лучших результатов по нажатию на кнопку «Рекорды». Окно результатов будет с двумя табами, в одной соответственно время, во второй — очки. Сами результаты будем хранить в файле во внутренней памяти телефона.
1. Создадим новый класс Records (в Records.java) унаследованный от TabActivity и разметку окна records.xml (в /res/layout). Разметка окна будет такая:
- <?xml version="1.0" encoding="utf-8"?>
- <TabHost xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/tabhost"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TabWidget
- android:id="@android:id/tabs"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
- </LinearLayout>
- </TabHost>
TabHost - контейнер для окна с вкладками. У него обязательно должны быть два дочерних элемента: TabWidget и FrameLayout.
TabWidget — список ярлыков вкладок, при нажатии на один из ярлыков в FrameLayout открывается соответствующая вкладка.
В классе Records напишем:
- public class Records extends TabActivity {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.records);
- TabHost tabHost = getTabHost();
- // Вкладка Время
- TabSpec timetab = tabHost.newTabSpec("Time");
- // устанавливаем заголовок и иконку
- timetab.setIndicator("по времени", getResources().getDrawable(R.drawable.time));
- // устанавливаем окно, которая будет показываться во вкладке
- Intent timeIntent = new Intent(this, RecordTime.class);
- timetab.setContent(timeIntent);
- // Вкладка Очки
- TabSpec pointtab = tabHost.newTabSpec("Point");
- pointtab.setIndicator("по очкам", getResources().getDrawable(R.drawable.point));
- Intent pointIntent = new Intent(this, RecordPoint.class);
- pointtab.setContent(pointIntent);
- // Добавляем вкладки в TabHost
- tabHost.addTab(timetab);
- tabHost.addTab(pointtab);
- }
- }
2. В качестве иконки вкладки мы написали «R.drawable.time», что обычно соответствует файлу /res/drawable/time.png. Но мы хотим сделать, чтобы рисунок активной вкладки отличался от рисунка неактивной. Например, у активной вкладки изображение будет красным (time_red.png), а у неактивной — серым (time_grey.png). Это легко сделать с помощью StateListDrawable — объект drawable определённый в xml, который позволяет использовать несколько изображений в зависимости от состояния объекта. Создадим в /res/drawable файл time.xml и напишем в него
- <?xml version="1.0" encoding="utf-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/time_red"
- android:state_selected="true" />
- <item android:drawable="@drawable/time_grey" />
- </selector>
3. Добавим еще два класса RecordTime (в RecordTime.java) и RecordPoint (в RecordPoint.java) и соответственно две разметки к ним time_tab.xml и point_tab.xml. Добавим все новые классы в AndroidManifest.xml и по кнопке «Результаты» будем открывать окно Records. Все это мы уже делали, так что еще раз писать код не буду, его можно посмотреть в части 2.
4. Запускаем:
5. С табами разобрались, теперь посмотрим как сохранять и выводить результаты. Можно сохранять результат каждой игры, а выводить только 5 лучших, но тогда получится, что мы храним кучу никому не нужных (и никогда не используемых) данных. Поэтому в файле у нас всегда будет не больше 5 лучших результатов (без дубликатов), при чем уже отсортированных, чтобы удобнее было сразу их выводить.
6. Для того, чтобы сохранить данные в файле нам понадобятся два класса:
FileOutputStream — входной поток, который записывает данные в файл. Класс инициализируется методом openFileOutput(String name, int mode) (класса Context), где name — имя файла, который надо открыть, mode — режим, по умолчанию используется MODE_PRIVATE (создается файл доступный только из данного приложения). Также может быть MODE_APPEND (если файл уже существует, то данные добавляются в конец файла), MODE_WORLD_READABLE и MODE_WORLD_WRITABLE (файлы которые может читать или записывать сторонее приложение).
ObjectOutputStream — класс для сериализации любых объектов.
- SomeObject obj;
- FileOutputStream fos = openFileOutput(«FileName.txt», Context.MODE_PRIVATE);
- ObjectOutputStream os = new ObjectOutputStream(fos);
- os.writeObject(obj);
- os.close();
Мы сериализуем и записываем в файл FileName.txt, объект типа MyObject. Объект может быть любого типа, например String, Integer, ArrayList.
Для чтения файлов используются классы FileInputStream и ObjectInputStream. Работа с ними аналогична
- FileInputStream fis = openFileInput(«FileName.txt»);
- ObjectInputStream is = new ObjectInputStream(fis);
- SomeObject s1 = (SomeObject) is.readObject();
- is.close();
Метод Object readObject () возвращает значение типа Object — базовый класс языка Java. Все не примитивные типы данных являются наследниками этого класса, поэтому мы можем выполнить операцию по приведению типа.
Записать в один файл можно несколько объектов (несколько раз вызвав метод writeObject()). Соответственно читать их надо будет в том же порядке.
7. Если вставить эти четыре строчки в код, то eclipse их подчеркнет и напишет кучу ошибок типа «Unhandled exception type FileNotFoundException» (IOException или ClassNotFoundException). Этот код потенциально может вызывать исключения и поэтому его надо окружить операторами try/catch. По хорошему надо обработать каждое исключение и написать код, что делать в случае его возникновения. Но все эти виды исключений являются наследниками класса Exception, поэтому все их можно отловить в одном catch (Exception e) {}. В нашей программе мы так и поступим, в случае любой ошибки будем писать сообщение пользователю (Toast).
8. С рекордами удобно работать в ArrayList, так как во-первых, в нем их можно сортировать и искать, а во-вторых, его легко можно вывести в ListView. Значит в нашем файле будет хранится (в сериализованном виде) два массива — рекорды по времени и рекорды по очкам. По окончании игры, считаем массивы, добавим в них значения результатов текущей игры, отсортируем и первые 5 элементов массива запишем обратно в файл. Чтобы не засорять код класса MemoriaActivity напишем всю работу с файлами в отдельном классе RecordAdapter (файл RecordAdapter.java). Это будет просто класс java не от чего не унаследованный.
- class RecordAdapter
- {
- // Название файла в котором хранятся данные
- private static String FILE_RECORDS = "memoria-records";
- // Два массива для рекордов
- ArrayList<String> recTime;
- ArrayList<Integer> recPoint;
- Context mContext;
- public RecordAdapter(Context context)
- {
- recTime = new ArrayList<String> ();
- recPoint = new ArrayList<Integer> ();
- mContext = context;
- // читаем из файла два массива с рекордами
- try {
- FileInputStream fis = mContext.openFileInput(FILE_RECORDS);
- ObjectInputStream is = new ObjectInputStream(fis);
- recPoint = (ArrayList<Integer>) is.readObject();
- recTime = (ArrayList<String>) is.readObject();
- is.close();
- } catch (Exception e) {
- Toast.makeText(mContext, "Произошла ошибка чтения таблицы рекордов", Toast.LENGTH_LONG);
- }
- }
- public void WriteRecords ()
- {
- // записываем в файл массивы с рекордами
- try {
- FileOutputStream fos = mContext.openFileOutput(FILE_RECORDS, Context.MODE_PRIVATE);
- ObjectOutputStream os = new ObjectOutputStream(fos);
- os.writeObject(recPoint);
- os.writeObject(recTime);
- os.close();
- } catch (Exception e) {
- Toast.makeText(mContext, "Произошла ошибка записи в таблицу рекордов", Toast.LENGTH_LONG);
- }
- return;
- }
- public void addTime (String str)
- {
- // Добавляем новое значение времени,
- // если такого еще нет в массиве
- if (!recTime.contains(str))
- recTime.add(str);
- // сортируем
- Collections.sort(recTime);
- // оставляем в массиве только 5 элементов
- for (int i = 5; i < recTime.size(); i++)
- recTime.remove(i);
- return;
- }
- public void addPoint (Integer num)
- {
- if (!recPoint.contains(num))
- recPoint.add(num);
- Collections.sort(recPoint);
- for (int i = 5; i < recPoint.size(); i++)
- recPoint.remove(i);
- return;
- }
- public ArrayList<String> getRecTime()
- {
- // возвращаем массив рекордов по времени
- return recTime;
- }
- public ArrayList<String> getRecPoint()
- {
- // переделываем массив целых чисел в массив строк
- ArrayList<String> arr = new ArrayList<String>();
- for (Integer temp : recPoint)
- arr.add(temp.toString());
- // возвращаем массив рекордов по очкам
- return arr;
- }
- }
9. Теперь напишем в методе ShowGameOver() класса MemoriaActivity, код для сохранения результатов игры:
- private void ShowGameOver () {
- String time = mTimeScreen.getText().toString();
- // Читаем файл с рекордами
- RecordAdapter ra = new RecordAdapter (this);
- // Добавляем новые значения
- ra.addPoint(StepCount);
- ra.addTime(time);
- // Записываем рекорды в файл
- ra.WriteRecords();
- // Диалоговое окно
- ...
- }
10. А в классах RecordPoint и RecordTime выведем список рекордов. Как вывести список ArrayList я писала в статье про списки. Пишем в point_tab.xml и time_tab.xml:
- <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="@android:id/list"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
- </LinearLayout>
А в классы:
- public class RecordPoint extends ListActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.point_tab);
- RecordAdapter ra = new RecordAdapter (this);
- ArrayList<String> arr = ra.getRecPoint();
- ArrayAdapter<String> mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, arr);
- setListAdapter(mAdapter);
- }
- }
11. Запускаем. Теперь в конце игры результаты сохраняются и выводятся в списках по кнопкам «Рекорды».
Все, игра готова :)
Исходники: Memoria-4.zip
Все части урока:
1. Игровое поле 6х6 с картинками (часть 1)
2. Учет количество ходов (или времени) (часть 2)
3. Просмотр таблицы рекордов (часть 4)
4. Настройки: выбор цвета фона и набора картинок (часть 3)
5. Начальный экран (Часть 2)
Спасибо за статью! Прекрасная работа.
ОтветитьУдалитьСпасибо за статью! Прекрасная работа.
ОтветитьУдалить