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)
Спасибо за статью! Прекрасная работа.
ОтветитьУдалитьСпасибо за статью! Прекрасная работа.
ОтветитьУдалить