Процесс разработки Android-приложения, Системные требования - Создание приложения

Разработка приложения "KotobaQuiz" включает три этапа:

    1) создание базы данных с таблицами, содержащими задания и варианты ответов, и таблицами, которые в процессе использования будут заполнятся ответами пользователя. Содержимое таблиц заполнялось с учетом эксперимента, описанного в первой главе; 2) создание элементов графического интерфейса: кнопок, текстовых полей, всплывающих уведомлений и декоративных элементов; 3) разработка логики приложения: возможность смены дизайна и языка, включение / выключение звука, обработка нажатий на кнопки, подсчет статистики, взаимодействие с базой данных.

В процессе создания приложения было написано 13 java-классов, 2 интерфейса (см. Приложение 1), 8 файлов с xml-разметкой (см. Приложение 2), 25 элементов пользовательского интерфейса, иконка приложения, база данных с заданиями и динамически загружаемыми во время работы приложения ответами пользователя; осуществлена локализация на русский, английский и японский языки. Разрешение всех элементов пользовательского интерфейса, включая иконку приложения, было пересчитано в соответствии со всеми существующими плотностями экрана у разных планшетов и смартфонов под управлением ОС Андроид. Все java-файлы находятся в пакете "com. lyubov. app. kotobaquiz.". Пакет подразделяется на 5 субдиректорий: "customization", "database", "preferences", "task", "ui" (см. Приложение 2). Рассмотрим назначение каждой субдиректории подробнее.

В субдиректории "customization" содержатся 2 java-класса и один интерфейс:

    - BackGroundManager. java - класс; - SoundManager. java - класс; - ThemeKeys. java - интерфейс.

Класс BackGroundManager. java отвечает за управление темами оформления. Использует ключи тем из интерфейса ThemeKeys. java для сохранения в файл настроек. Данный класс спроектирован в соответствии с паттерном "Одиночка" ("Singleton Pattern"). Это означает, что будет создан один и только один экземпляр его типа, что является актуальным для данного приложения по двум причинам: оно является многопоточным и в нем есть совместно используемые данные, а именно константы тем оформления. Они используются большим количеством других классов. (Йенер, 2016:60). Паттерн проектирования "Одиночка" для класса ThemeKeys. java реализовывался с помощью создания метода getInstance(); пустого приватного конструктора BackgroundManager(); инициализации полей в методе init(), а не в конструкторе:

Public static BackgroundManager getInstance() {

If (sInstance == null) {

Return sInstance = new BackgroundManager();

} else {

Return sInstance;

}

}

Private BackgroundManager() {

}

Public void init (Context context) {

// поля класса

}

В интерфейсе ThemeKeys. java, доступному для классов, которые его имплементируют, в целочисленных константах хранятся ключи тем оформления:

Integer ORIGINAL_THEME_KEY = 1;

Integer DARK_THEME_KEY = 2;

Integer NEUTRAL_THEME_KEY = 3;

Константа "ORIGINAL_THEME_KEY" хранит значение 1, что соответствует теме приложения "Стандарт": бледно-голубой фон всех экранов приложения. Это тема по умолчанию, она будет видна пользователю при первом запуске приложения на своем устройстве. Константа "DARK_THEME_KEY" хранит значение 2, что соответствует теме оформления "Темный": темно-сливовый цвет фона всех экранов приложения. Она будет видна пользователю, если пользователь сменит оформление. Аналогично, константа "NEUTRAL_THEME_KEY" хранит значение 3, что соответствует теме оформления "Нейтрал": светло-бежевый фон всех экранов приложения.

Класс SoundManager. java отвечает за управление звуком нажатия на кнопки в приложении. Также, как и класс BackgroundManager. java объявлен "Singleton". С помощью метода getState() проверяется включен ли звук нажатия на кнопку.

Public boolean getState() {

Return mState;

}

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

Public void playSoundClick (final Context context) {

New Thread (new Runnable() {

@Override

Public void run() {MediaPlayer mediaPlayer = MediaPlayer. create (context, R. raw. button_click);

MediaPlayer. start();

}

}).start();

}

В субдиректории "database" имеется один класс - ExternalDBOpenHelper. java, отвечающий за подключение и взаимодействие с базой данных. Он наследуется от стандартного абстрактного класса SQLiteOpenHelper. java. Рассмотрим реализацию класса подробнее.

Класс содержит следующие поля:

Private final String DB_PATH;

Private final String DB_NAME;

Private SQLiteDatabase database;

Private final Context context;

Константам "DB_PATH" и "DB_NAME" в конструкторе присваивается строковое значение пути до базы данных и имени базы данных соответственно. В переменной "database" типа SQLiteDatabase хранится сама база данных, с которой будут производится все манипуляции. В константе "context" хранится информация обо всех классах и ресурсах приложения.

С помощью функции createDataBase() создадим пустую базу данных, затем с помощью метода copyDataBase() поместим в пустую базу данных созданную ранее базу данных по механизма входного потока и сериализации.

С помощью метода openDataBase() откроем ее в режиме чтения и записи, возвратим полученную базу данных. После этого закроем базу данных для того, чтобы открытая база данных не занимала ресурсы приложения. Закрытие осуществляется методом close() с модификатором "synchronized" для того, чтобы закрытие базы данных мог выполнять только один поток.

В субдиректории "preferences" находится класс - PreferencesManager(). Он управляет настройками приложения, а именно: находит их и сохраняет. Рассмотрим реализацию данного класса подробнее. В поле класса "mPref" типа SharedPreferences хранятся настройки приложения. С помощью метода savePreference настройки сохраняются следующим образом создается редактор настроек путем создания экземпляра стандартного класса Editor. java, на нем вызывается метод edit(). С помощью метода putInt() передаем в экземпляр класса Editor. java ключ и значение настроек. Результат сохраняется с помощью метода savePreferences. Загрузка и получение настроек осуществляется методами loadPreferences() и getPreference() соответственно.

В субдиректории "task" находятся следующие классы и интерфейс:

    - TaskTypes. java - интерфейс; - QuestionAndAnswers. java - класс; - QuestionAndAnswerManager. java - класс; - TaskLoader. java - класс.

Эти модули ответственны за загрузку и хранение информации из базы данных; осуществляется взаимодействие с пользователем по выбору вариантов ответа на то или иное задание.

Класс QuestionAndAnswer. java выполняет функцию контейнера, в который можно положить и выбрать вопрос и варианты ответов. В полях класса хранятся вопрос из базы данных и варианты ответов:

    - mQuestion - содержит текст того или иного задания; - mAnswerOne - содержит 1-ый вариант ответа; - mAnswerTwo - содержит 2-ой вариант ответа; - mAnswerThree - содержит 3-ий вариант ответа; - mAnswerFour - содержит 4-ый вариант ответа; - mCorrectAnswer - содержи правильный ответ.

В конструкторе эти поля инициализируются пустыми строками. В классе определены соответствующие методы-геттеры и методы-сеттеры для задания и каждого варианта ответа.

Класс QuestionAndAnswersManager. java - это класс, обеспечивающий доступ, взаимодействие, управление вопросами и вариантами ответов. Он содержит следующие поля:

    - mArray - массив, в который поместим экземпляры класса QuestionAndAnswers. java; - mCorrectAnswerCount - количество правильных ответов; - mIncorrectAnswerCount - количество неправильных ответов; - mNumberOfQuestion - номер вопроса, для отображения в заголовке экрана задания; - mAnswerAndQuestionNumber - номер экземпляра класса QuestionAndAnswers. java;

В конструкторе инициализируем массив, заполняя его экземплярами класса QuestionAndAnswers:

MArray = new ArrayList<>(quantity);

For (int i = 0; i < quantity; i++) {

MArray. add (i, new QuestionAndAnswers());}

Инициализируются остальные поля:

MCorrectAnswerCount = 0;

MIncorrectAnswerCount = 0;

MAnswerAndQuestionNumber = 0;

MNumberOfQuestion = 1;

С помощью метода getCorrectAnswersCount() получим количество правильных ответов. С помощью метода getIncorrectAnswersCount() получим количество неправильных ответов. Задания и варианты ответов выводятся на экран псевдослучайным образом с помощью метода setAnswerAndQuestion():

Public void setAnswersAndQuestion (TextView question, Button answerOne, Button answerTwo, Button answerThree, Button answerFour, TextView numberQuestion, Context context) {

Random rand = new Random();

If (mArray. size() > 0) {

MAnswerAndQuestionNumber = rand. nextInt (mArray. size());}

Содержимое случайного задания (вопрос, варианты ответа) в элементы интерфейса выводится с помощью стандартного метода setText(). Для задачи текста для поля "question" и кнопки "answerOne" реализация метода выглядит следующим образом:

Question. setText (mArray. get(mAnswerAndQuestionNumber).getQuestion());

AnswerOne. setText (mArray. get(mAnswerAndQuestionNumber).getAnswerOne());

Номер задания, на котором находится пользователь устанавливается в поле интерфейса "numberQuestion" также с помощью метода setText():

NumberQuestion. setText (context. getResources().getString (R. string. question_number) +

String. valueOf(mNumberOfQuestion));

}

Проверка правильности ответа, подсчет правильных и неправильных ответов, данных пользователем проверяется методом isCorrect():

Public boolean isCorrect (Button currentButton) {

MNumberOfQuestion++;

If (mArray. get(mAnswerAndQuestionNumber).getCorrectAnswer().

Equals (currentButton. getText())) {

MCorrectAnswerCount++;

Return true;

} else {

MIncorrectAnswerCount++;

Return false;

}

}

Если экземпляр класса QuestionAndAnswers. java был полностью использован, то есть был дан ответ на вопрос, то он удаляется из массива mArray:

Public void removeChosenAnswerAndQuestion() {

MArray. remove(mAnswerAndQuestionNumber);

}

Можно получить массив mArray с помощью метода getArrayAnswersAndQuestions():

Public ArrayList<QuestionAndAnswers> getArrayAnswersAndQuestions() {

Return mArray;

}

}

Класс TaskLoader. java имплементирует интерфейс TaskTypes. java, тем самым получая доступ к целочисленным константам, хранящимся в нем:

    - int CHARACTER_MEANING = 0 - константа, соответствующая заданию "Иероглиф-значение"; - int GRAMMAR = 1 - константа, соответствующая заданию "Грамматика"; - int CHOOSE_RIGHT_PARTICLE = 2 - константа, соответствующая заданию "Выберите правильную частицу";

Класс содержит следующие поля: строковые константы, хранящие названия таблиц базы данных:

    - String TASK_TABLE_ONE = "task_char_trans" - константа, соответствующая заданию "Иероглиф-значение"; - String TASK_TABLE_TWO = "task_gram" - константа, соответствующая заданию "Грамматика"; - String TASK_TABLE_THREE = "task_choose_particle" - константа, соответствующая заданию "Выберите правильную частицу";

Строковые константы, хранящие название столбцов таблиц базы данных:

    - String TASK_ID = "_id" - константа, хранящая название столбца, в котором хранится уникальный идентификатор записи таблицы; - String TASK = "task" - константа, хранящая название столбца, в котором хранятся варианты ответа №1, аналогично для остальных трех вариантов ответа; - String CORRECT_ANSWER = "correct_answer" - константа, хранящая название столбца, в котором хранятся правильные ответы; - String DB_NAME = "taskdatabase" - константа, хранящая название таблицы с результатами выполнения задания 1; - String TABLE_RESULT_CHARACTER_MEANING = "result_character_meaning" - константа, хранящая название таблицы с результатами выполнения задания 2; - String TABLE_RESULT_CHARACTER_READING = "result_gram" - константа, хранящая название таблицы с результатами выполнения задания 3 - String TABLE_RESULT_CHOOSE_PARTICLE = "result_choose_particle" - константа, хранящая название столбца, в которых хранится кол-во правильных ответов, данных пользователем, при выполнении определенного задания - String RESULT_CORRECT_ANSWER = "correct_answer" - константа, хранящая число правильных ответов, данных пользователем; - String RESULT_INCORRECT_ANSWER = "incorrect_answer" - константа, хранящая название столбца в которых хранится кол-во неправильных ответов, данных пользователем; - String RESULT_TOTAL_RATE = "total_rate" - оценка, которую система выставляет пользователю; - int NUMBER_OF_ANSWERS = 4 - константа, хранящая количество возможных вариантов ответа;

Сама база данных хранится в переменной sDataBase типа SQLiteDatabase. Количество экземпляров класса QuestionAndAnswers. java, которое будет загружаться в уроки, содержится в целочисленной переменной mQuestionsAndAnswersCountForTask. Для загрузки данных из базы данных создается экземпляр класса Cursor. Подключение и открытие базы данных происходит с помощью метода connectAndOpenDataBase().Извлечение данных из базы данных осуществляется с помощью метода executeData(). В теле метода создается множество с размером, равным количеству заданий, которые будут загружены в уроки и заполняем это множество случайными числами, пока не заполнится. Далее создается массив из имен столбцов с вариантами ответов:

ArrayList<String> cursorColumns = new ArrayList<>(4);

CursorColumns. add (ANSWER_ONE);

CursorColumns. add (ANSWER_TWO);

CursorColumns. add (ANSWER_THREE);

CursorColumns. add (ANSWER_FOUR);

Затем создается массив, который будет принимать номера случайного расположения для вариантов ответа:

Integer[] columnOfAnswer;

Создадим итератор, для получения элементов множества, не используя массивов:

Iterator setIterator = randSet. iterator();

В цикле от 0 до количества вопросов будут загружаться в уроки:

For (int i = 0; i < arrayQuestionAndAnswers. size(); i++) {

Выбираем случайную позицию которые расположены во множестве:

Cursor. moveToPosition((int) setIterator. next());

Получаем случайное расположение вариантов ответа, для данного задания:

ColumnOfAnswer = randomLoadingAnswers();

Помещаем в массив "Вопросов &; Ответов", выгруженный вопрос из случайного задания:

ArrayQuestionAndAnswers. get(i).

SetQuestion (cursor. getString (cursor. getColumnIndex(TASK)));

Помещаем в массив "Вопросов &; Ответов" выгруженый вариант ответа, принадлежащий к тому же случайному заданию, что и вопрос: arrayQuestionAndAnswers. get(i).

SetAnswerOne (cursor. getString (cursor. getColumnIndex (cursorColumns. get (columnOfAnswer[0])

Помещаем в массив "Вопросов &; Ответов" выгруженный вариант ответа, принадлежащий к тому же случайному заданию, что и вопрос (для индексов с 0 до 3). Помещаем в массив "Вопросов &; Ответов" выгруженный правильный ответ, принадлежащий к тому же случайному заданию, что и вопрос:

ArrayQuestionAndAnswers. get(i). setCorrectAnswer (cursor. getString (cursor. getColumnIndex (CORRECT_ANSWER)));

Во избежание утечек памяти, закрываем курсор:

Cursor. close();

Задаем инициализацию курсору, для получения данных из базы данных:

Public void setCursorForTaskLoad (String tableName)

Создаем курсор, для которого просим получить нужные нам столбцы с данными, из нужной нам таблицы, из нашей базы данных:

Cursor = sDataBase. query (tableName, new String[] {TASK, ANSWER_ONE, ANSWER_TWO, ANSWER_THREE, ANSWER_FOUR, CORRECT_ANSWER}, null, null, null, null, null);

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

Public void setQuestionsAnswersCountForTask() {

MQuestionsAndAnswersCountForTask = cursor. getCount() / 2;

}

Получить кол-во "Вопросов &; Ответов", которое будет загружаться в уроки:

Public int getQuestionsAnswersCountForTask() {

Return mQuestionsAndAnswersCountForTask;

}

Далее получаем нужную таблицу, из базы данных, по номеру урока

Public String getTableFromNumber (int number) {

Switch (number) {

Case CHARACTER_MEANING:

Return TASK_TABLE_ONE;

Case GRAMMAR:

Return TASK_TABLE_TWO;

Case CHOOSE_RIGHT_PARTICLE:

Return TASK_TABLE_THREE;

Default:

Return null;

}

}

Получить нужную таблицу с результатами выполненных уроков, из базы данных, по номеру урока осуществляется аналогично конструкцией "switch-case-break":

Public String getResultTableFromNumberOfTask (int number) {

Switch (number) {

Case CHARACTER_MEANING:

Return TABLE_RESULT_CHARACTER_MEANING;

Case GRAMMAR:

Return TABLE_RESULT_CHARACTER_READING;

Case CHOOSE_RIGHT_PARTICLE:

Return TABLE_RESULT_CHOOSE_PARTICLE;

Default:

Return null;

}

}

С помощь соответствующих методов-геттеров получим результаты выполненных заданий. Например, для получения результата пройденного урока типа "иероглиф-значение" метод-геттер выглядит следующим образом:

Public String getResultTableForTaskOne() {

Return TABLE_RESULT_CHARACTER_MEANING;

}

С помощью метода getStatistics() получаем статистику выполненных заданий из базы данных и запишем результат на экран статистики. Закроем базу данных во избежание утечек памяти.

Перейдем к рассмотрению классов субдиректории "ui", в которой находятся классы-Activity, отвечающие за тот или иной layout или экран приложения. Функционал этих классов во многом схож: в них объявляются элементы пользовательского интерфейса, устанавливаются прослушка кликов на кнопки с помощью метода onClickListener(). Ниже приведем список классов этой субдиректории:

    - MainActivity. java; - SelectTypeLessonActivity. java; - GameActivity. java; - ResultActivity. java; - StatisticsActivity. java; - SettingsActivity. java;

Класс MainActivity. java отвечает за взаимодействие с экраном main_menu_layout. xml (см. Приложение). В классе объявлены следующие поля: поле типа RelativeLeayout для последующего задания ему тем оформления; поля типа Button для кнопок "Играть", "Статистика", "Настройки", "Об игре", "Выход"; поле булева типа для проверки состояния темы: менялась ли она. Для создания активности используется стандартная функция onCreate(), далее содержимое активности инициализируется методом init(). Настройка прослушивания кликов производится для каждой кнопки. Например, для кнопки "Статистика" настройка прослушки осуществляется следующим образом:

MStatisticButton. setOnClickListener(this);

Тему оформления задается с помощью метода setTheme(). Проверка состояния включения звука осуществляется следующим образом:

If (SoundManager. getInstance().getState()) {

SoundManager. getInstance().playSoundClick (getBaseContext());

}

Затем создадим механизм определения, какая кнопка была нажата пользователем и что следует делать по ее нажатии. По кнопке "Настройки" перейдем на экран настроек, по кнопке "Играть" перейдем на экран выбора типа задания и так далее:

Switch (view. getId()) {

Case R. id. settings_button:

GoToSettingActivity();

Break;

Case R. id. game_button:

GoToSelectLessonActivity();

Break;

Case R. id. about_button:

ShowAbout();

Break;

Case R. id. exit_button:

Exit();

Break;

Case R. id. statistic_button:

GoToStatisticsActivity();

Break;

Default:

Break;

}

}

По нажатии на кнопку "Об игре" показывается диалог "Диалог" на экране с информацией о приложении и авторе.

Private void showAbout() {

AlertDialog. Builder builder = new AlertDialog. Builder (MainActivity. this);

Builder. setTitle (R. string. about_game_title)

SetMessage (R. string. about_game_info)

SetPositiveButton (R. string. ok, new DialogInterface. OnClickListener() {

@Override

Public void onClick (DialogInterface dialogInterface, int i) {dialogInterface. dismiss();}});

AlertDialog aboutDialog = builder. create();

AboutDialog. show();

}

При выходе из приложения уничтожаем его процесс:

Android. os. Process. killProcess (android. os. Process. myPid());

Класс GameActivity. java отвечает за сам игровой процесс: за загрузку заданий из базы данных, за проверку ответов пользователя. После выбора ответа пользователем в течении 0,5 сек пользователю будет видно текстовое поле, в котором написано верно ли он ответил на вопрос: если верно, то поле зеленое, если не верное, то поле красное.

Класс ResultActivity. java отвечает за отчет о пройденном уроке, в котором видна оценка и количество правильных ответов. Вывод результатов прохождения задания пользователем на экран:

ResultView. setText (getResources().getString (R. string. correct_answers_text) + "" + String. valueOf(mCorrectAnswersCount) + " " + getResources().getString (R. string. incorrect_answers_text) +" "+ String. valueOf(mIncorrectAnswersCount));

После прохождения урока осуществляется расчет текстовой оценки пользователю. Если пользователь дал меньше 5 правильных ответов, то выставляется оценка "Плохо". Если количество правильных ответов больше или равно 10, тогда выставляем оценку "Неплохо". От 10 и выше - "Хорошо".

Private void calculateRate() {

If (mCorrectAnswersCount <= 5) {

SetRate (R. drawable. bad_rate, getResources().getString (R. string. bad));

} else if (mCorrectAnswersCount <= 10) {

SetRate (R. drawable. normal_rate, getResources().getString (R. string. not_bad));

} else {

SetRate (R. drawable. good_rate, getResources().getString (R. string. good));

}

}

Также, пользователю в зависимости от полученной оценки на экране показывается:

    - грустный смайл в случае оценки "Плохо"; - нейтральный смайл в случае оценки "Неплохо"; - улыбающийся смайл в случае оценки "Хорошо".

Private void setRate (int drawable, String totalTextRate) {

ImageView mGraphicalRate = (ImageView) findViewById (R. id. graphical_rating);

MGraphicalRate. setImageResource(drawable);

MTextRate. setText(totalTextRate);

}

Класс SelectTypeLessonActivity. java отвечает за выбор типа урока: "Иероглиф - значение", "Грамматика", "Выберите правильную частицу". В зависимости от выбранного типа урока по кнопке "Играть" будет осуществлен переход к экрану с нужным типом урока и будут загружаться соответствующие задания из базы данных.

Класс SettingsActivity. java отвечает за работу с настройками, а именно: с включением / выключением звука, заданием оформления приложения. Как было сказано выше, приложение локализовано на три языка: русский, английский и японский. Для того, чтобы изменить язык приложения, требуется изменить язык в системных настройках устройства.

Класс StatisticsActivity. java получает статистику обучения пользователя: сколько правильных ответов было дано и сколько заданий было всего с помощью метода getStatistics():

Private String[] getmStatistics()

Создаем экземпляр TaskLoader. java и подключаемся к базе данных:

TaskLoader statisticLoader = new TaskLoader();

StatisticLoader. connectAndOpenDataBase (getBaseContext());

Получение статистики для урока 1 (аналогично для остальных уроков):

String taskStatisticOne = statisticLoader. getStatistics (statisticLoader.

GetResultTableForTaskOne(), getBaseContext());

Закроем базу данных:

StatisticLoader. closeDatabase();

Возвратим массив статистики для трех заданий:

Return new String[] {taskStatisticOne, taskStatisticTwo, taskStatisticThree};}

Системные требования

Минимальная версия ОС Андроид, с которой совместимо приложение "KotobaQuiz" - Android 4.1 "Jelly Bean". Поскольку версии данной ОС имеют обратную совместимость, приложение успешно запустится на всех версиях ОС выше 4.1. Приложение занимает 5 Mb памяти.

Похожие статьи




Процесс разработки Android-приложения, Системные требования - Создание приложения

Предыдущая | Следующая