Введение
Общее описание
Структура приложения
Уроки
Урок 1
Создание приложения, экраны, меню, список, локализация
Урок 2
Просмотр видео (youtube), панель ввода
Урок 3
Карты, PlusMinus, пользовательская обработка экранов
Урок 4
Завершение разработки, закрепление материала
Урок 5
Интро, авторизация, фото
Урок 6
Боковое меню, горизонтальный список, пагинация, раскрывающийся список, баркод-сканер, распознавание речи, поиск, листание страниц
Урок 7
Работа с базами данных SQLite, свайп, componentDateDiapason
Урок 8
Спиннер, календарь, пиккер и события
Урок 9
Работа с пуш сообщениями, BadgeTextView
Урок 10
Анимация, кастомные компоненты
Описание библиотеки
Приложения

Урок 6

На этом уроке мы научимся: работать с боковым меню (Drawer), горизонтальным списком, пагинацией, раскрывающимся списком, сканером баркода, распознаванием речи, поиском и листанием страниц

Постановка задачи (задание урока)

Рис. 1 экран DRAWER
Рис. 1 экран DRAWER
Рис. 2 экран DRAWER
Рис. 2 экран DRAWER
Рис. 3 экран CATALOG
Рис. 3 экран CATALOG
Рис. 4 экран CATALOG
Рис. 4 экран CATALOG
Рис. 5 экран CATALOG
Рис. 5 экран CATALOG
Рис. 6 экран PRODUCT_LIST
Рис. 6 экран PRODUCT_LIST
Рис. 7 экран PRODUCT_LIST
Рис. 7 экран PRODUCT_LIST
Рис. 8 экран PRODUCT_LIST
Рис. 8 экран PRODUCT_LIST
Рис. 9 экран BARCODE
Рис. 9 экран BARCODE
Рис. 10 экран DESCRIPT
Рис. 10 экран DESCRIPT
Рис. 12 экран DESCRIPT
Рис. 11 экран DESCRIPT
Рис. 12 экран CHARACTERISTIC
Рис. 12 экран CHARACTERISTIC
Рис. 13 экран PROFILE
Рис. 13 экран PROFILE
Рис. 14 диалог перед выходом
Рис. 14 диалог перед выходом из приложения

На рисунке 1 изображен экран DRAWER для неавторизованного пользователя, а на рисунке 2 - для авторизованного. На рисунках 3 - 5 изображен один экран CATALOG в разных состояниях (с нераскрытым списком категорий и раскрытым одним и двумя уровнями). На рисунках 6 - 8 покащан экран PRODUCT_LIST, в т.ч. с голосовым поиском и поиском при вводе в поле поиска. На рисунке 9 изображен екран BARCODE. Экран PRODUCT_DESCRIPT имеет две вкладки: "Описание" и "Характеристики". На рисунках 10, 11 показан экран PRODUCT_DESCRIPT с вкладкой "Описание" (экран DESCRIPT), а на рисунке 12 с вкладкой "Характеристики" (экран CHARACTERISTIC). На рисунке 13 показан экран PROFILE. На рисунке 14 показан диалог перед выходом из при клике на аппаратную кнопку Back

Описание API

Для всех запросов данного урока, кроме запросов экрана PROFILE, авторизация не обязательна

Экран CATALOG для горизонтального списка (Новинки) URL depro/cron/news, метод GET. При чтении данных о новинках используется пагинация. Параметры пагинации передаются в загловке.
Ответ 
[
    {
        "product_id":4610,
        "catalog_id":15984,
        "product_name":"Термоусаживаемая трубка 20мм набор 6 цветов (пак 1м*20шт) APRO",
        "catalog_code":"ZRG-20kit",
        "picture":"depro/cronimg/picture_1.jpeg",
        "bar_code":"4824041010653",
        "oem":"",
        "price":175.98,
        "brand":"APRO",
        "product_code":"032578",
        "gift":0,
        "bonus":0,
        "new_product":1,
        "quantity":5
    },
    . . .
]

Список категорий для нераскрытого списка URL depro/cron/catalog. 
Для раскрывающихся списков URL depro/cron/catalog_ex с параметром catalog_id в качестве значений которого
используется id категории, которая раскрывается.
Ответ для всех запросов одинаков:
[
    {
        "catalog_id":15510,
        "parent_id":0,
        "catalog_name":"Кузов"
    },
    {
        "catalog_id":15584,
        "parent_id":0,
        "catalog_name":"Двигатель"
    },
    . . .
]

Экран PRODUCT_LIST URL depro/cron/product_list с параметрами: expandedLevel и catalog_id.
Здесь expandedLevel - уровень раскрытия каталога (0, 1 или 2), catalog_id - id выбанной категории.
Для категорий уровня 0 и 1 определяется список всех категорий, которые входят в выбранную, а затем
по этому списку определяется перечень товаров входящих в какую нибудь категорию из этого списка.
Метод GET
Структура данных ответа аналогична как для горизонтального списка (Новинки)

Для выбора продукции по заданному баркоду используется URL depro/cron/product_barcode с 
параметром barcode_scanner. 
Для выбора продукции по строке поиска используется URL depro/cron/product_search с параметром 
product_name. Отбор осуществляется по условию LIKE. В product_name может быть одно или два слова.
Аналогичный запрос используется и для голосового задания текста поиска.

Экран DESCRIPT 
Описание продукции: URL depro/cron/product_id с параметром product_id. Метод GET.
Ответ
{
    "product_id":2942,
    "catalog_id":15594,
    "product_name":"Датчик температуры ВАЗ 2110, 2111, 2112, ЗАЗ Sens 'Сенс' АВТОПРИБОР ",
    "catalog_code":"23.3828"
    ,"picture":"depro/cronimg/picture_16.jpeg",
    "bar_code":"2000000148472",
    "oem":"2112-3851010",
    "price":103.02,
    "brand":"Автоприбор, г. Калуга, Россия",
    "product_code":"027729",
    "gift":1,
    "bonus":0,
    "new_product":0,
    "quantity":16
}

Аналоги: URL depro/cron/product_analog с параметром product_id. Метод GET.
Ответ
[
    {
        "product_id":561,
        "catalog_id":15587,
        "product_name":"Насос водяной ВАЗ 2110, 2111, 2112 (помпа на 16 клап. мотор) AURORA",
        "catalog_code":"WP-LA2112",
        "picture":"depro/cronimg/picture_12.jpeg",
        "bar_code":"2900011680711",
        "oem":"2112-1307010",
        "price":188.16,
        "brand":"AURORA, Poland",
        "product_code":"016807",
        "gift":0,
        "bonus":1,
        "new_product":0,
        "quantity":15
    },
    . . .
]

Экран CHARACTERISTIC URL depro/cron/product_charact с параметром product_id. Метод GET.
Ответ
[
    {
        "prop_id":2764,
        "product_id":2942,
        "name":"Производитель",
        "value":"Автоприбор, г. Калуга, Россия"
    },
    {
        "prop_id":2765,
        "product_id":2942,
        "name":"Марки Авто для ИМ",
        "value":"ВАЗ,ЗАЗ"
    },
    . . .
]

Экран PROFILE для авторизованного пользователя URL depro/cron/profile. Метод GET.
Ответ
{
    "user_id":69,
    "surname":"Іванченко",
    "name":"Петро",
    "second_name":"Петрович",
    "phone":"+380888877778",
    "photo":"depro/usersphoto/1567873247345.jpg",
    "email":"d@x.com"
}

Отправка откорректированного профиля URL depro/cron/profile_edit. Метод POST. Тип multipart/form-data.
Данные:
{
    "surname":"Іванченко",
    "name":"Петро",
    "second_name":"Петрович",
    "phone":"+380888877778",
    "email":"d@x.com"
}
Ответ как для чтения профиля.

Экран DRAWER в заголовке для авторизованного пользователя показывается фото, фамилия и имя пользователя. Также отображается кнопка "ВЫЙТИ". Для неавториованного пользователя показывается только кнопка "ВОЙТИ"

Меню при первом входе позиционируется на первом элементе. При последующих - на последнем выбраном пункте. Пункт "Профиль" для неавторизованного пользователя должен быть недоступным.

Экран CATALOG имеет два списка. Один (Новинки) горизонтальный, второй (Каталог) - раскрывающийся. При клике на item списка "Новинки" осуществляется переход на екран PRODUCT_DESCRIPT. При клике на кнопку "Добавить" осуществляется переход на екран "ADD_PRODUCT". В списке "Каталог" при клике на кнопку "стрелка вниз" для любой категории списка показывается список входящих в нее категорий. Имеется три уровня вхождения категорий. При клике на item списка "Каталог" осуществляется переход на экран PRODUCT_LIST.

Экран PRODUCT_LIST показывет список продукции выбранных категорий. При клике на item списка продукции осуществляется переход на экран PRODUCT_DESCRIPT с детальным описанием товара. При клике на кнопку "Добавить" осуществляется переход на экран ADD_PRODUCT. Экран PRODUCT_LIST имеет в верху строку поиска, кнопки "Баркод" и "Голосовой помощник". При вводе в строку поиска текста сразу (если длина текста более 3 символов) осуществляется запрос на сервер для выбора продукции название которых близко к набранному тексту (отбор осуществляется по условию LIKE) и показа результата поиска. При клике на кнопку "Баркод" осуществляется вызов экрана BARCODE. После распознавания им штрихкода осуществляется запрос на сервер для выборки продукции с таким кодом и показа результата. При клике на "Голосового помощника" надиктованный текст заносится в строку поиска с последующим обращением на сервер для отбора продукции и ее показа.

Экран BARCODE позволяет распознать штрихкод.

Описание экранов

Добавим в класс MyDeclareScreens.java описание всех указанных в постановке экранов (приведено ниже). Также обновим описание экрана MAIN. А затем поясним новые компоненты.

    activity(MAIN, R.layout.activity_main)
        .navigator(finishDialog(R.string.attention, R.string.finishOk))
        .drawer(R.id.drawer, R.id.content_frame, R.id.left_drawer, null, DRAWER);

    fragment(DRAWER, R.layout.fragment_drawer)
        .navigator(start(R.id.enter, AUTH), exit(R.id.exit))
        .component(TC.PANEL, model(PROFILE),
            view(R.id.panel).noDataView(R.id.no_data))
        .menu(model(menu), view(R.id.recycler));

        fragment(CATALOG, R.layout.fragment_catalog)
            .navigator(handler(R.id.back, VH.OPEN_DRAWER))
            .list(model(Api.NEWS_PROD).pagination().progress(R.id.progr),
                view(R.id.recycler_news, R.layout.item_news_prod).horizontal(),
                navigator(start(PRODUCT_DESCRIPT), start(R.id.add, ADD_PRODUCT, PS.RECORD)))
            .list(model(Api.CATALOG),
                view(R.id.recycler, "expandedLevel", new int[]{R.layout.item_catalog_type_1,
                    R.layout.item_catalog_type_2, R.layout.item_catalog_type_3})
                    .expanded(R.id.expand, R.id.expand, model(Api.CATALOG_EX, "catalog_id")),
                navigator(start(PRODUCT_LIST)));

    activity(PRODUCT_LIST, R.layout.activity_product_list).animate(AS.RL)
        .navigator(back(R.id.back),
            handler(R.id.barcode, BARCODE, after(handler(R.id.recycler, VH.UPDATE_DATA,
                model(Api.PRODUCT_BARCODE, "barcode_scanner")))))
        .componentRecognizeVoice(R.id.microphone, R.id.search)
        .list(model(Api.PRODUCT_LIST, "expandedLevel,catalog_id"),
            view(R.id.recycler, R.layout.item_product_list)
                .visibilityManager(visibility(R.id.bonus_i, "bonus"),
                    visibility(R.id.gift_i, "gift"),
                    visibility(R.id.newT, "new_product")),
            navigator(start(PRODUCT_DESCRIPT),
                handler(R.id.add, ADD_PRODUCT, PS.RECORD)))
        .componentSearch(R.id.search, model(Api.PRODUCT_SEARCH, "product_name"), R.id.recycler);

    activity(BARCODE, R.layout.activity_barcode).animate(AS.RL)
        .navigator(back(R.id.back),
            handler(R.id.apply, VH.RESULT_PARAM, "barcode_scanner"))
        .componentBarcode(R.id.barcode_scanner, R.id.result_scan, R.id.repeat);

    activity(PRODUCT_DESCRIPT, R.layout.activity_product_descript, "%1$s", "catalog_name").animate(AS.RL)
        .navigator(back(R.id.back))
        .setValue(item(R.id.product_name, TS.PARAM, "product_name"))
        .component(TC.PAGER_F, view(R.id.pager,
            new String[] {DESCRIPT, CHARACTERISTIC})
            .setTab(R.id.tabs, R.array.descript_tab_name));

    fragment(DESCRIPT, R.layout.fragment_descript)
        .component(TC.PANEL, model(Api.PRODUCT_ID, "product_id"),
            view(R.id.panel).visibilityManager(visibility(R.id.bonus, "bonus")),
            navigator(handler(R.id.add, ADD_PRODUCT, PS.RECORD)))
        .list(model(Api.ANALOG_ID_PRODUCT,"product_id"),
            view(R.id.recycler, R.layout.item_product_list).noDataView(R.id.not_analog)
                .visibilityManager(visibility(R.id.bonus_i, "bonus"),
                    visibility(R.id.gift_i, "gift"),
                    visibility(R.id.newT, "new_product")),
            navigator(start(0, PRODUCT_DESCRIPT, PS.RECORD),
                handler(R.id.add, ADD_PRODUCT, PS.RECORD), handler(0, VH.BACK)));

    fragment(CHARACTERISTIC, R.layout.fragment_characteristic)
        .list(model(Api.CHARACT_ID_PRODUCT, "product_id"),
            view(R.id.recycler, "2", new int[] {R.layout.item_property, R.layout.item_property_1}));

    fragment(PROFILE, R.layout.fragment_profile)
        .navigator(handler(R.id.back, VH.OPEN_DRAWER))
        .componentPhoto(R.id.cli, new int[] {R.id.blur, R.id.photo}, R.string.source_photo)
        .component(TC.PANEL_ENTER, model(Api.PROFILE),
            view(R.id.panel),
            navigator(handler(R.id.done, VH.CLICK_SEND, model(POST, Api.EDIT_PROF,
                "surname,name,second_name,phone,photo,email"),
                after(setProfile()))));

Также необходимо сформировать (с учетом видимости) переменную menu для экрана DRAWER:

    Menu menu = new Menu()
            .item(R.drawable.list, R.string.m_catalog, CATALOG, true)
            .item(R.drawable.shoppingcard, R.string.m_orders, ORDER_LIST)
            .divider()
            .item(R.drawable.icon_profile, R.string.profile, PROFILE).enabled(1)
            .divider()
            .item(R.drawable.ic_aura, R.string.fitness, FITNESS)
            .item(R.drawable.icon_menu_news, R.string.news_events, NEWS_EVENTS).badge("news,events")
            .item(R.drawable.present, R.string.anim, ANIMATION);

Здесь приведены все пункты бокового меню, в т.ч. те, которые будут описаны на следующих уроках. Ее нужно поместить в любом месте файла (класса) MyDeclareScreens.java. Ниже будет приведено описание этой переменной

Если все введено правильно, то при старте приложения будут отображаться (и работать) все указанные экраны. А теперь объясним те новые конструкции, которые были использованы при описании экранов урока.

Объяснение работы новых компонентов

Экран MAIN

Экран содержит компонент drawer, которому передаются id элемента разметки DrawerLayout, id контейнера фрагментов (R.id.content_frame), id элемента куда будет загружен фрагмент с боковым меню (R.id.left_drawer) и название фрагмента с боковым меню (DRAWER)

Также нужно обратить внимание на обработчик finishDialog, который обеспечивает перед выходом из приложения по аппаратной кнопке Back вывод диалога с уточнением желания выйти из приложения. Ему передаются id заголовка и текста сообщения. В отличии от обычного сообщения об ошибке в этом случае также показывается кнопка errorDialogPositiveId по клику на которую и происходит выход из приложения.

Экран DRAWER

Экран DRAWER содержит два компонента. Это уже знакомый нам компонент типа PANEL и компонент menu. В компоненте PANEL в модели указан тип данных PROFILE. В этом случае используются сохраненные данные профиля. На прошлом уроке мы рассматривали как это сделать обработчиком setProfile(). Если пользователь не авторизованный (нет профиля) то показывается элемент R.id.no_data. Изменениях профиля сразу будут отображены в панеле.

Компонент menu содержит два параметра. Само меню и ссылку на элемент разметки, который будет его отображать - R.id.recycler. Данные menu задаются указанной выше переменной типа Menu:

Каждый пункт меню имеет три обязательных параметра: ресурс иконки (одноцветной на прозрачном фоне), строковый ресурс с текстом пункта и строка с именем экрана, который будет вызван при клике на пункт меню. Для пункта меню, который буде выбран по умолчанию указывается четвертый булевый параметр. Управление доступностью пункта меню задается дополнительным функционалом .enabled(int en). en - задает условия доступности. В настоящее время реализовано одно условие "авторизация" (en = 1). Таким образом, в нашем случае пункт "Профиль" будет доступен только для авторизованного пользователя. Для неаторизованного он будет показываться, но будет не кликабельным. При запуске приложения сразу будет показываться экран CATALOG. Для соответствующего пункта меню четвертый параметр = true.

Экран CATALOG

Экран CATALOG содержит два списка: один горизонтальный, второй раскрывающийся. Горизонтальный список отличается от обычного только дополнительным функционалом для view ( horizontal() ). В данном случае в модели используется пагинация со стандартными параметрами, которые заданы в классе AppParams, а именно: paginationNameParamPerPage = "PerPage", paginationNameParamNumberPage = "NumberPage" и paginationPerPage = 20. При необходимости их можно изменить в файле MyParams. Особенностью этого списка является и то, что прогресс показывается не в диалоге, а в элементе разметки R.id.progr

Для задания списку свойства при клике на некоторый элемент раскрывать его сосотавляющие используется дополнительный функционал .expanded(R.id.expand, R.id.expand, model(Api.CATALOG_EX, "catalog_id")) В нем указываются элемент R.id.expand по клику на который осуществляется раскрытие списка, элемент, который будет проворачиваться на 180 градусов на раскрытом пункте списка (в нашем случае это тот же элемент). Также указывается модель по которой получаем данные для раскрытого списка.

Экран PRODUCT_LIST

Новым на этом экране является компонент для поиска - componentSearch. Ему задается id элемента в который будет вводиться текст для поиска (R.id.search). При изменен его содержимого (при условии что в нем больше 3 символов) осуществляется получение данных по модели, котрые передаются для отображения в компонент типа list id которого указан в view. Последний параметр указывает, что при выполнении поиска данных в list отображаются предыдущие данные.

Обработчик handler(R.id.barcode, BARCODE, after(handler(R.id.recycler, VH.UPDATE_DATA, model(Api.PRODUCT_BARCODE, "barcode_scanner")))) вызывает экран BARCODE, после успешного завершения выполнения действий экрана BARCODE выполняется обновление данных компонента с id R.id.recycler по модели model(Api.PRODUCT_BARCODE, "barcode_scanner")

Также новым является компонент распознавания речи - componentRecognizeVoice. У него всего два параметра: id элемента при клике на который будет вызван Google Voice Search и id элемента куда будет занесен распознаный текст. В нашем случае текст заносится в тот же элемент куда заносится строка поиска. Поэтому при изменении его содержимого автоматически запустится процесс поиска и отображения данных

Экран BARCODE

Новым является компонент componentBarcode. Ему передаются: id элемента класса BarcodeScanner (класс библиотеки декларативного программирования DePro , который наследуется от ZXingScannerView); id элемента куда будет занесен результат сканирования и id элемента по клику на который будет осуществлено повторное сканирование.

Обработчик handler(R.id.apply, VH.RESULT_PARAM, "barcode_scanner") заносит в отсканированное значение в параметр "barcode_scanner" и выходит с RESULT_OK. Таким образом на экране PRODUCT_LIST будет отображена продукция с распознанным штрих кодом.

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

Экран PRODUCT_DESCRIPT

На экране новым является отображение заголовка экрана (элемент типа TextView с id = "title"). Текст, который выводится задается в описании активити (фрагмента) третьим параметром, в нем можно указывать и шаблон. В этом случае параметры шаблон берутся из четвертого параметра. В нашем случае это "%1$s" и "catalog_name". Таким образом выводится название каталога (catalog_name).

Также новым является использование компонента типа PAGER_F. У него в целом понятные параметры: это id элемента разметки класса ViewPager (R.id.pager) и перечень фрагментов, которые буду отображаться. Дополнительным функционалом setTab() указываются параметры TabLayout.

Экраны DESCRIPT, CHARACTERISTIC и PROFILE не содержат новых элементов



Лучше пакета DePro может быть только искусственный интеллект
Задать вопрос
Отправить вопрос