Решение для построения динамических форм с Android
Я на самом деле разрабатываю приложение для Android, в котором я должен отображать динамические формы на основе метаданных, содержащихся в документах JSON. В основном, как это работает (без подробностей), это то, что документ JSON представляет структуру формы:
{
"fields": [
{
"name": "fieldA",
"type": "STRING",
"minCharacters": 10,
"maxCharacters": 100
},
{
"name": "fieldB",
"type": "INTEGER",
"min": 10,
"max": 100
},
{
"name": "fieldC",
"type": "BOOLEAN_CHECKBOX",
"defaultValue": true
}
...
],
"name": "Form A"
}
На самом деле, когда приложение получает один из этих документов JSON, оно выполняет цикл по всем полям и анализирует его в соответствующем представлении (EditText, Checkbox, настраиваемое представление и т. Д.), Добавляя тег в представление (чтобы легко найти) и добавить представление к LinearLayout
, Вот псевдокод того, как он работает на самом деле:
//Add form title
linearLayout.addView(new TextView(form.name));
//Add form fields
for(Field field: form.fields) {
View view;
switch(field.type){
case STRING: view = new EditText();
...
}
view.setTag(field.id);
linearLayout.addView(view);
}
Проблема заключается в том, что с большими формами (например,>20 полей) необходимо раздувать много представлений, а поток пользовательского интерфейса сильно страдает. Еще один момент, который необходимо учитывать, состоит в том, что на одном экране может быть несколько форм (одна за другой отсортированы по вертикали).
Чтобы избежать перегрузки потока пользовательского интерфейса, я подумал о 2 возможных решениях:
- Использование RecyclerView.
- Использование Litho от Facebook.
Но несколько вопросов приходит ко мне при рассмотрении этих двух решений:
- Это хороший вариант использования Litho? Или достаточно использовать RecyclerView?
- Как насчет состояния моих взглядов? Если я использую шаблон "Переработка", смогу ли я сохранить состояние каждого из моих полей (даже тех, которые находятся за пределами экрана) и, таким образом, сохранить форму без потери данных?
- Если я буду использовать шаблон Recycling для отображения одной формы, как мне будет обрабатывать несколько форм? Можем ли мы иметь вложенный RecyclerView? Формы должны отображаться одна за другой, как внутри вертикального RV, но если сами формы являются RV, как мне справиться с этим?
Это скорее вопрос "хорошей практики", который дает правильный или один из правильных способов достижения моей цели, чем необходимость конкретного ответа с примером кода и т. Д.
Спасибо заранее за ваше время.
3 ответа
При разработке мобильного приложения я хотел бы ответить на следующие вопросы:
Это хороший вариант использования Litho? Или достаточно использовать RecyclerView?
Правильно ли перерабатываются представления: для нас это означает, что необходимо создать представление 40-50 на экран, и когда пользователь выходит из представления, система не должна помечать все представления для GC, а должна находиться в каком-то архивном списке. и поскольку мы требуем этого снова, мы должны быть в состоянии извлечь из этого.
Зачем нам это нужно: GC является самой дорогой операцией, которая вызывает дрожание приложения, мы пытаемся свести к минимуму вызов GC на этом этапе, не очищая представления
Для этого я хотел бы пойти с Лито, обоснование здесь, поскольку ваше требование, кажется, имеет больше переменного количества
viewtypes
ссылкаВывод: Litho +1, RecyclerView +0
Как насчет состояния моих взглядов? Если я использую шаблон "Переработка", смогу ли я сохранить состояние каждого из моих полей (даже тех, которые находятся за пределами экрана) и, таким образом, сохранить форму без потери данных?
Сохранение содержимого EditText в RecyclerView. Это один из компонентов, но та же логика должна применяться и к флажкам или к переключателям. или как в состоянии для обслуживания лит здесь
Вывод: Litho +1, RecyclerView +1 оба имеют специальные API для достижения поддержания состояния
Если я буду использовать шаблон Recycling для отображения одной формы, как мне будет обрабатывать несколько форм? Можем ли мы иметь вложенный RecyclerView? Формы должны отображаться одна за другой, как внутри вертикального RV, но если сами формы являются RV, как мне справиться с этим?
- Это должно быть решено с помощью пользовательского опыта плюс технических возможностей: в соответствии с поведением пользователя, IMHO, я не рекомендую использовать вложенную вертикальную прокрутку, однако другие смогли добиться этого, что легко найти в SO. Оптимальным решением было бы иметь горизонтальную прокрутку в поле зрения переработчика Андриода или Лито, как здесь
ПРИМЕЧАНИЕ: если вам нужно знать детали реализации, пожалуйста, поднимите это как отдельный вопрос, я был бы рад помочь
ОБНОВЛЕНИЕ № 1:
Проблема заключается в том, что с большими формами (например,>20 полей) необходимо раздувать много представлений, а поток пользовательского интерфейса сильно страдает.
Создание / размещение пользовательского интерфейса должно выполняться на бэкэнде, только добавление к представлению должно выполняться в потоке пользовательского интерфейса. И Лито делает это встроенным. Тем не менее, то же самое может быть достигнуто и в представлении собственного рециркулятора, но вы должны отойти от потока пользовательского интерфейса и периодически отправлять сообщения в пользовательский интерфейс.
Хорошо, у вас есть две отдельные проблемы здесь. Один из них - переутомление потока пользовательского интерфейса, а другой - сохранение состояния ваших анонимных просмотров. О первой части:
1.-Лито мог бы помочь тебе с этим. Но вы должны переместить всю свою логику в сторону литологических компонентов вместо виджетов Android. Поскольку я не знаю вашу кодовую базу, я не знаю, насколько это сложно. Recyclelerview поможет с просмотром рециркуляции, но это важно только в том случае, если вы хорошо себя чувствуете, используя список.
2.-Это может, если у вас есть способ сохранить представление состояния виджета, которое вы можете передать адаптеру и затем вернуться к представлению (я предполагаю, что вы генерируете все окна по коду и затем получаете ноль ссылка на них) и так. Это звучит грязно, и это грязно, поэтому я не буду пробовать это.
3. -Вы можете, но это грязно. Лучшим подходом в этом случае будет наличие горизонтальных представлений переработчика внутри вертикального представления переработчика. Вложение обзоров переработчика в другое представление переработчика с тем же направлением создает забавные проблемы, такие как "Почему эта ячейка не прокручивается". Я бы не использовал в качестве родителя просмотрщик, если он не нужен.
Теперь к решениям:
А) Перегрузка пользовательского интерфейса: в соответствии с вашим псевдокодом, вы не раздувает вещи. Вы создаете Java-объекты, которые являются подклассами View. Это хорошо, потому что создавать объекты в фоновом потоке гораздо проще, чем раздувать (разбирать XML и использовать его в качестве аргументов для генерации идентичных копий данного ресурса путем вызова конструкторов) в фоновом потоке. В то время как конструктор контекста LinearLayout требует выполнения потока пользовательского интерфейса, другие вещи, такие как текстовые представления, этого не делают. Таким образом, вы можете создать последние внутри асинхронной задачи, и после того, как вы закончите генерацию всей иерархии, выполните методы, которым нужен поток пользовательского интерфейса, и добавьте сгенерированный макет в окно. Для классов представлений, которые не поддерживают асинхронное создание в виде java-объектов, вы можете иметь XML-файл только с этим компонентом, например linearLayout, и затем асинхронно создавать его с помощью пакета поддержки asyncLayoutInflater. Это решение может быть реализовано в любой кодовой базе и позволит вам сделать генерацию вашего пользовательского интерфейса полностью асинхронной.
Б) Отслеживание состояния представления. Опять же, я предполагаю, что ваша иерархия представлений является анонимной. Если это так, то вам нужно создать интерфейс, который вы можете использовать в качестве контракта для вызова как сохранения состояния, так и загрузки состояния из компонента, поддерживающего жизненный цикл, такого как действие. После создания такого интерфейса создайте подклассы виджетов и создайте систему подписки / шины событий в каждом виджете, которая сохраняет / загружает состояние из виджета каждый раз при запуске. Таким образом, каждый из компонентов на экране сможет запомнить свое состояние, оставаясь анонимным.
Просто используйте RecyclerView и создайте представления во время выполнения (вы не раздуваете, вы создаете экземпляры) Динамическое создание и добавление представлений не должно значительно замедлять поток пользовательского интерфейса на устройствах среднего уровня. Если это так, расследуйте наличие узких мест в других местах.
Вы можете выполнить простой тест, добавив / удалив / установив текст с большим количеством представлений динамически в RecyclerView или даже в LinearLayout, размещенном в ScrollView, и вы увидите, что все идет гладко
Используйте композитор Jetpack, предоставляемый Android