През годините съм виждал различни реализации на навигационния модел на Android. Някои приложения използваха само дейности, докато други дейности бяха смесени с фрагменти и / или персонализирани изгледи ( Персонализирани изгледи ).
Една от любимите ми реализации на фрагменти е базирана на философията „Една активност-Множество фрагменти“ („ Една дейност-множество фрагменти ”), Или просто Фрагментният модел за навигация ( Модел за навигация на фрагменти ), където всеки екран в приложението е фрагмент на цял екран и всички или повечето от тези фрагменти са в рамките на дейност.
Този подход не само опростява начина на внедряване на навигацията, но също така има по-добра производителност и следователно ви предлага по-добро потребителско изживяване.
В тази статия ще разгледаме някои често срещани реализации на навигационните модели на Android, след което ще представим базиран на фрагмент навигационен модел. Примерно приложение, което прилага този модел, може да бъде намерено в GitHub .
Типично приложение за Android, което използва само дейности, е организирано в дървовидна структура (точно в насочена графика), където основната дейност се инициира от стартер . Докато разглеждате приложението, има „ обратно стек ”От дейността, която се поддържа благодарение на операционната система.
Един прост пример е показан на следната диаграма:
Дейност A1 е входната точка на нашето приложение (например, тя представлява начален екран или главно меню) и от тази точка потребителят може да навигира до A2 или A3. Когато трябва да общувате между дейности, които можете да използвате startActivityForResult () Или можете да споделите глобално достъпен бизнес логически обект между двете дейности.
Когато трябва да добавите нова активност, трябва да изпълните следните стъпки:
Въпреки че тази навигационна схема е доста опростен подход; но може да стане много сложно, когато имате нужда манипулиране на обратно стек или когато трябва да използвате повторно една и съща дейност няколко пъти, например когато искате потребителят да навигира през различни екрани с уроци, но всеки екран използва една и съща дейност като основа.
За щастие имаме наречени инструменти за тези случаи домашни задължения и някои ръководства за a навигация на обратно стек подходящо .
След това, с API ниво 11, фрагментите пристигнаха ...
Android въвежда фрагменти в Android 3.0 (API ниво 11), предимно за да поддържа по-динамични и гъвкави оформления на потребителския интерфейс на големи екрани като таблети. Тъй като екранът на таблета е много по-голям от този на телефона, има повече място за смесване и съчетаване на компонентите на потребителския интерфейс. Фрагментите поддържат тези оформления, без да е необходимо да управлявате сложни промени в йерархията на изгледа. Чрез разбиване на оформлението на дейност на парчета, можете да промените външния вид на активността по време на изпълнение и да запазите тези промени в стека от дейности, управляван от дейността. - цитат от Google Guide API за фрагменти .
Тази нова играчка позволи на разработчиците да създадат многопанелен потребителски интерфейс и да могат да използват повторно компонентите в други дейности. Някои разработчици обичат това, докато други Не толкова много . Дали да се използват фрагменти е популярен дебат, но мисля, че всеки ще се съгласи, че фрагментите са донесли допълнителна сложност и разработчиците трябва да ги разберат правилно, за да ги използват.
Започнах да виждам все повече и повече примери, при които фрагментите не само представляваха част от екрана, но целият екран беше фрагмент в рамките на дадена дейност. Имаше време, когато видях оформление, при което всяка дейност имаше точно един фрагмент на цял екран и нищо друго и единствената причина тези дейности да съществуват беше да съхранява фрагментите. Освен недостатък в дизайна, има и друг проблем с този подход. Погледнете диаграмата по-долу:
Как А1 може да комуникира с F1? Това, което се случва, е, че A1 има пълен контрол над F1, защото е създал F1. A1 може да предаде пакет, например, при създаването на F1 или може да се позове на своите публични методи. Как F1 може да комуникира с A1? Ами това е по-сложно, може да се реши с модел на обаждане / наблюдател където А1 се абонира за F1 и F1 уведомява А1.
Но как могат А1 и А2 да комуникират? Както беше обяснено по-горе, те биха могли да комуникират чрез startActivityForResult () .
И сега истинският въпрос: Как F1 и F2 могат да комуникират? Дори и в този случай можем да имаме компонент на бизнес логика, който е глобално достъпен и може да се използва за предаване на данни. Но този компонент не винаги се равнява на изискан дизайн. Какво ще стане, ако F2 трябва да предаде информация на F1 по-директно? В такъв случай, с шарка обратно извикване F2 може да уведоми A2, след което A2 завършва с резултат и този резултат може да бъде съхранен от A1, който може да уведоми F1.
Този подход изисква много код стереотипния и бързо се превръща в източник на бъгове , болка и гняв.
Ами ако можем да се отървем от всички дейности и да запазим само едно от тях, което ще запази останалите фрагменти?
С течение на времето започнах да използвам шаблона „Една активност-Множество фрагменти“ в повечето от моите приложения и все още го използвам. Има много дискусии за този подход или философия например тук Y. тук . Това, което пропуснах, беше конкретен пример, който мога да видя и тествам сам.
Нека разгледаме за момент следната диаграма:
Сега имаме само една дейност в контейнера и имаме множество фрагменти, които отново са в структура от тип Tree. Навигацията между тях се управлява от FragmentManager , това си има обратно стек .
Ще разберете, че сега нямаме startActivityForResult () но можем да приложим шаблона обаждане / наблюдател . Сега нека разгледаме някои плюсове и минуси на този подход:
Сега, когато имаме само една дейност, вече не се налага да актуализираме манифеста всеки път, когато добавяме нов екран. За разлика от дейностите, ние не трябва да декларираме фрагментите.
Това може да изглежда малко, но за по-големи приложения с повече от 50 дейности това може значително да подобри четливостта на AndroidManifest.xml файл.
Погледнете файла на манифеста за примерното приложение, което има множество екрани. Файлът на манифеста се поддържа изключително просто.
package='com.exarlabs.android.fragmentnavigationdemo.ui' >
В моя примерен код ще забележите, че използвам NavigationManager което в моя случай се инжектира на всеки от фрагментите. Този мениджър може да се използва като централно място за сеч , управление обратно стек наред с други, така че поведението при сърфиране да е отделено от останалата част от бизнес логиката и да не се разпространява в изпълнението на различни екрани.
Нека си представим ситуация, в която искаме да стартираме екран, при който потребителят може да избере някои елементи от списък с хора. Също така бихте искали да предадете някои аргументи за филтриране, като възраст, професия и пол.
В случай на Дейности, бихте написали:
Intent intent = new Intent(); intent.putExtra('age', 40); intent.putExtra('occupation', 'developer'); intent.putExtra('gender', 'female'); startActivityForResult(intent, 100);
След това трябва да дефинирате onActivityResult някъде надолу и се справете с резултата.
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); }
Моят личен проблем с този подход е, че тези аргументи са „екстри“ и не се изискват, така че трябва да се уверя, че приемащата дейност обработва различните случаи, когато липсва екстра. По-късно, когато се извърши рефакторинг и когато допълнителната, например 'възраст' вече не е необходима, тогава трябва да прегледам целия код, от който е започнала тази дейност, и да се уверя, че всички екстри са правилни.
Също така, не би ли било по-добре резултатът (списък с хора) да пристигне като _List_, а не в серийна форма, която след това трябва да бъде десериализирана?
В случай на сърфиране на базата на фрагменти всичко е по-лесно. Всичко, което трябва да направите, е да напишете метод в NavigationManager Наречен startPersonSelectorFragment () с необходимите аргументи и с изпълнение обратно извикване .
mNavigationManager.startPersonSelectorFragment(40, 'developer', 'female', new PersonSelectorFragment.OnPersonSelectedListener() { @Override public boolean onPersonsSelected(List selection) { [do something] return false; } });
Или с RetroLambda
mNavigationManager.startPersonSelectorFragment(40, 'developer', 'female', selection -> [do something]);
Между дейностите можем да споделяме само Пакет, който съдържа примитивни или сериализирани данни. Сега с фрагменти можем да приложим шаблон обратно извикване където например F1 може да слуша F2, преминавайки произволни обекти. Моля, разгледайте предишните примери за изпълнение на обратно извикване , който връща обратно _List_.
Това става очевидно, когато използвате чекмедже, което има например 5 елемента от менюто и на всяка страница чекмеджето трябва да се покаже отново.
В случай на чисто сърфиране, всяка страница трябва да се надуе и да стартира чекмеджето, но разбира се това е скъпо.
На показаната диаграма можете да видите няколко фрагмента от корен или коренови фрагменти (FR *), които са фрагментите на екрана, които могат да бъдат достъпни директно от чекмеджето, а също така чекмеджето е достъпно само когато тези фрагменти се показват. Всичко вдясно от маркираната линия в диаграмата е пример за произволна схема за навигация.
Тъй като съдържащата дейност съдържа чекмеджето, ние имаме само един екземпляр на чекмеджето, така че всяка стъпка за навигация, където чекмеджето трябва да бъде видимо не е нужно да го стартирате отново . Все още не сте убедени как работи всичко? Погледнете моето примерно приложение, където е демонстрирано използването на чекмеджета.
Моят голям страх винаги е бил, че ако използвам шаблона за навигация на фрагменти в проект, в даден момент ще срещна някакъв неизвестен проблем, който ще бъде трудно разрешим около сложността на фрагментите, библиотеки на трети страни и различни версии на System Оперативен. Ами ако трябваше да пречупя всичко, което съм правил досега?
Всъщност трябва да решите проблемите с вложени фрагменти , библиотеки на трети страни, които използват фрагменти като ShinobiControls , ViewPagers Y. FragmentStatePagerAdapters .
Признавам, че получаването на достатъчно опит с фрагменти за решаване на тези проблеми беше дълъг процес. Но във всеки случай проблемът не беше в това, че философията е лоша, а в това, че той не разбира достатъчно фрагменти. Но ако разбирате фрагменти по-добре от мен по това време, тогава няма да имате проблем.
Единствените минуси, които мога да спомена сега, е, че можем да намерим проблеми, които не са тривиални за решаване, тъй като няма зряла библиотека, показваща всички сложни сценарии на сложно приложение с базирана на фрагменти навигация.
В тази статия видяхме алтернатива за внедряване на навигация в приложение Android . Моделът беше сравнен с традиционната философия за навигация, която използва дейности и видяхме няколко много добри причини, поради които е изгодно да се използват фрагменти вместо традиционния подход.
В случай, че все още не сте го прегледали, разгледайте демонстрационното приложение в разполагането GitHub . Не се страхувайте да дадете добри примери, които биха могли да демонстрират по-добре използването му.
Свързани: Топ 10 на най-често срещаните грешки, които разработчиците на Android правят