Занятие № 6. Средства хранения структурированных данных в мобильном приложении

Задачи

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

Задание № 1. Квиз

Создадим приложение с викториной. Ответ на каждый вопрос нужно вводить самостоятельно. Правильный ответ сопроводим звуком.

Вопросы и ответы сохраним в двух списках. Как только пользователь ответит на последний вопрос, начнём викторину заново.

Откройте шаблон проекта под именем quiz_start.aia. Для этого выберите пункт меню Проекты - Импортировать проект (.aia) с моего компьютера:

В появившемся диалоге нажмите кнопку Выберите файл, выберите файл с шаблоном quiz_start.aia, и нажмите кнопку OK:

После этого откроется шаблон приложения.

Содержимое шаблона

В медиабиблиотеке проекта находятся два звука: da.wav и net.wav. Они будут проигрываться когда игрок дал правильный или неправильный ответ:

Интерфейс экрана Screen1 содержит следующие компоненты:

Свойства компонентов настроены согласно следующей таблице:

Компонент Свойства
Screen1 ВыровнятьПоВертикали=Центр
TitleVisible=нет галочки
image-20240330142312646НадписьНомерВопроса ЖирныйШрифт=есть галочка
РазмерШрифта=18
Текст=0
image-20240330142312646НадписьВопрос РазмерШрифта=25
Высота=150 pixels
Текст=Вопрос
image-20240330142400990ГоризонтальноеРасположение Ширина=Наполнить родительский
image-20240330142344900ТекстОтвет Ширина=Наполнить родительский
Подсказка=Введите ответ
image-20240330142259337КнопкаОтвет Текст=Ответ
image-20240330142416916ЗвукДа Источник=da.wav
image-20240330142419692ЗвукНет Источник=net.wav

В этом шаблоне нет заранее созданных скриптов.

Программирование компонентов

Перейдите в раздел Блоки.

Добавьте глобальную переменную номер_вопроса и сохраните в ней число 1:

Добавьте глобальную переменную список_вопросов. В переменной будем хранить список строк с текстом вопросов. Изначально блок создать список позволяет создать список из двух элементов. Нажмите на image-20240330144713474, чтобы добавить третий элемент к новому списку:

Элементами списка будут три строки с вопросами:

  • В каком году выпущен первый персональный компьютер IBM PC?
  • Минимальная единица измерения количества информации.
  • В каком году был изобретён транзистор?

Скопируйте формулировки вопросов выше и добавьте их в глобальную переменную список_вопросов:

Аналогично создайте глобальную переменную список_ответов. Первый элемент будет хранит ответ на первый вопрос и так далее:

Для вывода на экран текста очередного вопроса объявите собственный блок-процедуру под названием показать_вопрос:

  1. Очищаем текстовое поле для ввода ответа.
  2. Берём из списка вопросов элемент с индексом номер_вопроса и выводим текст в компоненте НадписьВопрос.
  3. Выводим на экран строку вида Вопрос 1, Вопрос 2 и так далее.

Начнём вывод вопросов после запуска приложения. Для этого составьте следующий обработчик события Инициализировать компонента Screen1:

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

номер_вопроса = (номер_вопроса mod размер списка_вопросов) + 1.

В App Inventor выражение составьте в следующем порядке:

Поместите полученное выражение вместе с вызовом процедуры показать_вопрос внутрь обработчика нажатия на кнопку КнопкаОтвет:

Время тестирования!

Запустите приложение. Вы должны увидеть текст первого вопроса и надпись Вопрос 1. Нажимайте на кнопку Ответ. Должен появиться следующий вопрос. После третьего вопроса должен снова появиться первый.

Добавьте блок-процедуру проверить_ответ с параметром ответ. Внутри процедуры будем сравнивать значение параметра ответ c текстом, который хранится в списке_ответов по индексу номер_вопроса. Если строки совпадают, значит ответ правильный:

Добавьте вызов блока-процедуры проверить_ответ в начало обработчика нажатия по кнопке КнопкаОтвет. В качестве аргумента передадим в блок значение из компонента ТекстОтвет:

Время тестирования!

Запустите приложение. Впишите ответ на первый вопрос и нажмите кнопку Ответ. В зависимости от правильности ответа, прозвучит определённый звук.

Дополнительное задание

Замените существующие вопросы и ответы на ваши на выбранную вами тему. Предложите соседу ответить на вопросы вашей викторины.

Задание № 2. Общий холст

Разработайте приложение для совместного рисования на общем холсте. Нажатие по экрану добавит круг. Цвет заливки выбирается случайно после каждого запуска приложения.

Информация о точках хранится на сервере. Отправка новой точки и получение списка существующих точек организуем с помощью веб-API. На сервер будем отправлять координаты точки и её цвет:

{
  "x": 100,
  "y": 50,
  "color": -256
}

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

[
    {
      "x": 100,
      "y": 50,
      "color": -255
    },
    {
      "x": 200,
      "y": 32,
      "color": -255
    }
]

Откройте шаблон проекта под именем canvas_start.aia.

Содержимое шаблона

Интерфейс экрана Screen1 содержит следующие компоненты:

Свойства компонентов настроены согласно следующей таблице:

Компонент Свойства
Screen1 ВыровнятьПоГоризонтали=Центр
TitleVisible=нет галочки
image-20240330234602180Холст1 Высота=300 pixels
Ширина=300 pixels
image-20240330234618215ИнтернетСохранить Timeout=50000
Адрес URL=https://simple-api-3rug.onrender.com/api/canvas
image-20240330234620416ИнтернетПолучить Timeout=50000
Адрес URL=https://simple-api-3rug.onrender.com/api/canvas
image-20240330234643746ЧасыЗапрос ТаймерВсегдаЗапущен=нет галочки
ТаймерВключен=есть галочка
Интервал=2000

Несмотря на то, что отправка и получение информации на сервер происходит по одному и тому же адресу, сервер отличает назначение запроса благодаря методу запроса.

HTTP-протокол определяет ряд методов по которым сервер понимает, какое действие нужно совершить. Чтобы получить информацию используется метод GET, а для отправки применяется метод POST. Существуют и другие методы, которые не используются в этом проекте. Подробности можно узнать по этой ссылке

Какие методы и другие подробности использования веб-АPI Холст можно узнать из документации к веб-API.

Программирование компонентов

Перейдите в раздел Блоки.

Опишем глобальную переменную цвет:

Добавьте глобальную переменную набор_цветов в которой сохраните список из пяти цветов на ваш выбор:

Выберем случайный цвет из списка после запуска приложения. Также нужно задать для компонента ИнтернетСохранить тип отправляемых данных. Сервер ожидает информацию о точке в JSON-формате:

У блока make a dictionary (создать словарь) необходимо оставить только одну пару ключ-значение. Для этого нажмите на image-20240330235514952 и удалите второй блок:

Ключ - Content-Type, значение - application/json.

Добавьте обработчик нажатия на холст:

  1. Меняем цвет заливки круга на значение переменной цвет.
  2. Рисуем на холсте круг с центром в точке касания.
  3. Отправляем информацию о точке с помощью веб-API.
  4. Отправляем данные в виде словаря с тремя парами ключ-значение: координаты и цвет точки.
Время тестирования!

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

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

Добавьте обработчик ПолученТекст для компонента ИнтернетПолучить:

  1. Преобразуем содержаниеОтвета в словарь.

  2. Получаем из словаря массив, который хранится в словаре по ключу canvas. Если такого ключа не окажется в словаре, вернём пустой список.

  3. С помощью цикла для каждого () в списке перебираем список точек. На каждом шаге цикла очередной элемент списка сохраняется в переменной точка. Каждая точка - это словарь со следующими парами ключ-значение:

    {
      "x": "координата x",
      "y": "координата y",
      "color": "цвет заливки"
    }
  4. Получаем из словаря точка значение по ключу color. Это будет цвет заливки круга.

  5. Берём из словаря точка координату x.

  6. Берём из словаря точка координату y. Рисуем круг с центром в точке с координатами (x, y).

Время тестирования!

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

Дополнительное задание

Добавьте возможность поделиться изображением на холсте. Как реализовать эту функции можете посмотреть в одном из заданий Занятия № 3.

Задание № 3. Робо-ферма

Разработаем приложение для управления передвижением дрона-фермера. Перед запуском курс прокладывается с помощью кнопок с четырьмя направлениями. Нажатие каждой кнопки добавляет новый курс в очередь команд. После запуска дрон будет следовать по заданному курсу:

Цель - проложить такой курс, который приведёт дрон к урожаю. Курс прокладывается с помощью кнопок со стрелками. Например для следующего поля

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

Откройте шаблон проекта под именем fermer_start.aia.

Содержимое шаблона

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

Интерфейс экрана Screen1 содержит следующие компоненты:

Свойства компонентов настроены согласно следующей таблице:

Компонент Свойства
Screen1 ВыровнятьПоГоризонтали=Центр
ВыровнятьПоВертикали=Вверх
TitleVisible=нет галочки
image-20240330142259337КнопкаВверх Изображение=up.png
Текст=пустая строка
image-20240330142259337КнопкаВниз Изображение=down.png
Текст=пустая строка
image-20240330142259337КнопкаВлево Изображение=left.png
Текст=пустая строка
image-20240330142259337КнопкаВправо Изображение=right.png
Текст=пустая строка
image-20240330142259337КнопкаПуск Текст=Пуск
image-20240330142259337КнопкаНовоеПоле Текст=Новое поле
image-20240330234602180Холст1 ЦветФона=Бесцветный
ФоновыйРисунок=pole.png
Высота=300 pixels
Ширина=300 pixels
image-20240510151054343СпрайтФермер Изображение=dron.png
X=3
Y=3
image-20240510151054343СпрайтУрожай Изображение=m.png
image-20240330234643746Часы1 ТаймерВсегдаЗапущен=нет галочки
ТаймерВключен=нет галочки
ИнтервалТаймера=960
image-20240330142416916ЗвукУспех Источник=da.wav

В шаблоне определён один собственный блок-процедура переместить_урожай. Вызов этого блока перемещает спрайт СпрайтУрожай на случайную клетку поля:

  1. Случайным образом выбираем номер строки и столбца в котором разместим спрайт. При этом помним, что игровое поле имеет размер 5x5 клеток.
  2. Перемещаем спрайт в координаты, вычеленные по формуле.
  3. Число 60 в формулах - ширина одной клетки поля в пикселях.

Программирование компонентов

Перейдите в раздел Блоки.

Добавьте глобальную переменную команды, инициализируйте её значение пустым списком:

В этой переменной сохраним последовательность команд для дрона.

Чтобы вевести на экран текущий список команд, определим собственный блок-процедуру показать_команды. В этой процедуре используем блок, который соединит все элементы списка в одну строку с заданным разделителем:

Например, если в списке окажутся значения [90, 180, 180, 270],то после выполнения блока join items using separator (объединить элементы списка используя разделитель), получим строку 90>180>180>270.

Добавьте обработчики для кнопок КнопкаВниз, КнопкаВверх, КнопкаВлево и КнопкаВправо. После нажатия на кнопку в очередь команд будет добавляться курс движения дрона:

Время тестирования!

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

Создайте собственный блок-процедуру новая_игра:

  1. Очищаем текущую очередь команд.
  2. Останавливаем СпрайтФермер, указав скорость его передвижения равной 0.
  3. Остановим таймер, который следит за выполнением очереди команд.
  4. Включаем доступ к кнопке КнопкаПуск. На время выполнения очереди команд эту кнопку мы будем отключать.
  5. Переместим СпрайтУрожай на новую случайную клетку.

Добавьте вызов процедуры новая_игра при запуске приложения:

Также процедуру новая_игра будем вызывать после нажатия по кнопке КнопкаНовоеПоле:

Время тестирования!

Запустите приложение. Проведите следующие тесты:

  • Нажмите несколько раз кнопку Новое поле. После каждого нажатия спрайт с урожаем должен переместиться в новую случайную клетку.
  • С помощью кнопок со стрелками заполните очередь команд а затем нажмите кнопку Новое поле. Очередь должна очиститься.

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

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

Составьте следующий скрипт, который будет срабатывать через заданный интервал таймера:

  1. Проверяем, пуста ли очередь команд.
  2. Если команд больше нет, останавливаем таймер и спрайт с дроном.
  3. Иначе получаем новый курс для дрона из первого элемента со списком команд.
  4. После этого удаляем первую команду из списка.
  5. Показываем на экране телефона текущий список команд.

Очередь команд неспроста получила такое название. Любая очередь работает по принципу “первый пришёл - первый вышел”. Первая добавленная команда и будет выполняться первой. Когда мы нажимаем на кнопки со стрелками, очередь заполняется. На первое место попадает команда добавленная первой и так далее:

После запуска таймера, для выполнения из списка всегда берётся команда под индексом 1. После выполнения, эта команда удаляется из списка, как бы сдвигая все остальные команды:

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

В программировании структура данных Очередь встречается довольно часто, например для хранения очереди печати или очереди обработки событий.

Время тестирования!

Запустите приложение. С помощью кнопок со стрелочками составьте маршрут движения дрона и нажмите кнопку Пуск. Дрон должен последовательно выполнить все команды в очереди.

Добавим проверку победного условия - после остановки дрон находится в клетке с урожаем. Для этого в обработчик события Когда Часы1 Таймер добавьте следующую проверку условия:

Победный звук воспроизведётся, если СпрайтФермер пересекается с СпрайтУрожай.

Время тестирования!

Запустите приложение. Составьте маршрут, который приведёт дрон к клетке с урожаем. Должен проиграться звук. Начните новую игру и составьте неправильный маршрут. Когда все команды выполняться и дрон не окажется в клетке с урожаем, звук не должен воспроизветись.

Проект готов.

Задание № 4. Крок

Крок (бел.) - шаг.

Разработаем приложение для ведения дневника прогулок - скольшо шагов пройдено за день и сколько всего метров пройдено за все дни прогулок. Для того, чтобы долгосрочно хранить количество шаго даже после того, как приложение закрыт, используем компонент image-20240527211514538TinyDB из категории Хранилище. Компонент сохраняет данные в словаре по заданному ключу. Этот словарь храниться в постоянной памяти телефона и доступен приложению после его повторного запуска.

Откройте шаблон проекта под именем krok_start.aia.

Содержимое шаблона

Интерфейс экрана Screen1 содержит следующие компоненты:

Свойства компонентов настроены согласно следующей таблице:

Компонент Свойства
Screen1 TitleVisible=нет галочки
НадписьЗаголовок ЖирныйШрифт=есть галочка
СписокЗначений ЦветФона=Безцветны
ЦветТекста=Тёмно-серый
Надпись4 Текст=Всего пройдено метров:
НадписьПройденоВсего Текст=0
ТекстЗначение Подсказка=Шагов пройдено сегодня
ТолькоЦифры=есть галочка
КнопкаДобавитьЗначение Текст=Добавить
КнопкаСбросить Значение Ширина=Наполнить родительский
Текст=Сбросить

Программирование компонентов

Перейдите в раздел Блоки.

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

Опишите собственный блок-процедуру добавить_значение с одним параметром значение. С помощью процедуры будем добавлять в список значение, преобразованные в число:

После добавления нового значения хорошо бы показать его в списке на экране телефона. Опишите блок-процедуру вывести_значения. Вызов этого блока будет обновлять значения на экране:

Добавьте обработчик нажатия по кнопке КнопкаДобавитьЗначение:

  1. Вызываем процедуру добавить_значение. В качестве аргумента передаем текстовое значение из компонента ТекстЗначение. В собственном блоке это значение преобразуется в число и добавится к списку наблюдения.
  2. Уберём сохранённое значение из текстового поля, подготовив его для ввода следующего числа.
  3. Обновим содержимое списка на экране.

Добавим возможность очистить список наблюдений. Добавьте обработчик нажатия по кнопке КнопкаСброситьЗначения:

Время тестирования!

Запустите приложение. Введите значение в текстовое поле и нажмите кнопку Добавить. Это значение должно появиться в списке на экране телефона. Введите ещё несколько значений. Нажмите кнопку Сбросить. Список на экране будет пуст. Заново добавьте несколько значений.

Полностью закройте прилоложение и откройте заново. Все значения, добавленные ранее, стёрлись и список на экране будет пуст.

Используем компонент TinyDB чтобы сохранить и загружать список наблюдений даже после перезапуска приложения.

Добавьте собственную процедуру сохранить_в_БД. После вызова процедуры список наблюдения будет храниться в базе данных под ключём данные:

Вызывать этот блок нужно каждый раз, как список наблюдения изменился - после добавления новое значения или очистки.

Добавьте вызов блока сохранить_в_БД в существующие обработчики нажатия кнопок:

Теперь список наблюдения сохраняется в базе данных. Но после повторного запуска приложения содержимое списка не загружается заново. Исправим это, добавив обработчик события Инициализировать для экрана. После запуска приложения мы попробуем загрузить значение из базы данных по ключу данные. Если это первый запуск приложения и такого ключа в базе данных ещё нет, то вернём пустой список.

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

Время тестирования!

Запустите приложение. Введите значение в текстовое поле и нажмите кнопку Добавить. Введите несколько значений. Нажмите кнопку Сбросить. Полностью закройте прилоложение и откройте заново. Все добавленные ранее значения должны снова появиться на экране.

Нажмите нопку Сбросить и перезапустите приложения. После перезапуска список должен быть пуст.

Посчитаем, сколько метров мы прошли. Для этого нужно:

  1. Создать новый список на основе списка наблюдения. Каждый элемент нового списка равен соответсвующему элементу исходного списка, умноженному на длину одного шага.
  2. Сложим все элементы нового списка, чтобы получить ответ.

Для решения задачи используем два новых блока: make new list from (создать новый список на основе) и reduce list (свернуть список).

Добавьте следующие блоки в блок процедуру вывести_значения, чтобы посчитать суммарно пройденое расстояние в метрах:

  1. Объявляем локальные переменные для хранения нового списка и результата расчётов.
  2. Получаем новый список, преобразовав количество шагов в расстояние в метры.
  3. Умножаем на 0.7 - устреднённую длинну одного шага в метрах.
  4. Свертываем список к одному значению - сумме всех пройденных расстояний.
  5. Выводим результат в надпись.

  1. На вход подаётся список.
  2. Блок перебирает все элементы исходного списка, последовательно сохраняя его значения в переменной item. Внутри блока переменная item умножается на длину одного шага. Результат вычислений станет новым элементом выходного списка.
  3. Блок возвращает новый список.

Этот блок помогает “свернуть” список в одно значение. Блок подходит для поиска суммы или произведения элементов.

Коэффициент в блоке make new list from - количество метров в одном шаге. Расчитать это значения для своего роста можете по следующей формуле: \[ длина\ шага=\frac{рост}{4}+0.37 \] Рост указывается в метрах.

Время тестирования!

Запустите приложение. Нажмите кнопку Сбросить. Введите следующие значения: 345, 1345, 5436. Суммарное расстояние в метрах должно быть равно 4988.2.

Проект готов.