Android Fragment показва оформление за зареждане при изобразяване

Да кажем, че имам Activity само с FrameLayout (Опитах и ​​с новия FragmentContainerView), така че ще заредя Fragments на него. Да приемем, че имам два фрагмента: fragA и fragB, първо ще заредя fragA и с бутон, присъстващ в този фрагмент, < em>заменете го с fragB.

Дефинирах функция на моя Activity, за да мога да заредя нов Fragment в контейнера:

public void loadFragment(Fragment fragment) {
    FragmentTransaction fragTrans = getSupportFragmentManager()
            .beginTransaction()
            .replace(R.id.container, fragment)
            .addToBackStack(null);

    fragTrans.commit();
}

И ще извикам това от fragA, за да отиде до fragB. Дотук добре, всичко работи добре. Всъщност проблемът, с който се сблъсквам, не е във функционирането, е, че не ми харесва времето за зареждане на fragB, отнема около 2 секунди и бих искал да покажа зареждане на потребителя, но не мога да го постигна.

Първото нещо, което опитах, е да добавя gone ProgressBar към оформлението на дейността, където е контейнерът. След това моята loadFragment функция първо ще зададе тази лента на прогреса видима и след това ще извърши фрагментната транзакция и всеки фрагмент ще скрие (изчезнал) този изглед, преди да излезе от метода onCreateView. Но проблемът е, че потребителят никога не вижда това ProgressBar, все едно основният потребителски интерфейс е замразен, докато изобразява оформлението на fragB и следователно не актуализира потребителския интерфейс, правейки този прогресбар безполезен. Дори виждам някои пропуснати кадри в LogCat около 50 кадъра.

И двата фрагмента нямат логика, само onCreateView имплементиран, където оформлението е раздуто. Мога да забележа, че ако променя оформлението на fragB на по-просто, то се зарежда моментално, така че предполагам, че проблемът е свързан с времето, необходимо за изобразяване на оформлението. Това тежко оформление е наистина голяма форма с много TextInputLayout с TextInputEditText и MaterialSpinner (от GitHub).

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

Едно нещо, което опитах, е да заредя фиктивно оформление с ProgressBar в средата и в метода onCreateView да публикувам Handler, за да раздуя истинско оформление в същия контейнер в фона, като този:

public View onCreateView(final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_dummy, container, false);

    Handler mHandler = new Handler();
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            container.removeAllViews();
            View realView = inflater.inflate(R.layout.fragment_real, container);
        }
    });
}

Това донякъде работи, тъй като изживяването при гледане е приятно, но при навигиране назад оформлението на fragB остава видимо като фон на другите фрагменти. Не съм го тествал, но може би мога да извикам container.removeAllViews(); преди да изляза от фрагмента и ще работи, но все още ми изглежда като заобиколно решение, а не решение.

И друго нещо, което не съм опитвал, защото може би е прекомерно убиване е да имаш междинен или зареждащ фрагмент и да го зареждаш винаги преди истинския фрагмент, и ще премина Intent Extra към него, за да мога да разбера кой е истинският фрагмент, който искам да покажа. Този междинен фрагмент няма да бъде добавен към задния стек.

Как решавате този тип проблеми?


person sebasira    schedule 04.04.2020    source източник
comment
Спомням си, че наричат ​​Skeleton изглед на фиктивния изглед, който споменах в публикацията си. И също ще бъде като Shimmer, но тъй като проблемът е, че потребителският интерфейс е замръзнал, не мога да актуализирам потребителския интерфейс   -  person sebasira    schedule 04.04.2020
comment
Добавете статичен диалогов обект в клас на приложение (в който можете да показвате напредъка), като използвате дейност или контекст на приложение. Показване преди зареждане на изгледа и скриване на същия след зареждане.   -  person Nilesh B    schedule 04.04.2020
comment
@NileshB благодаря за предложението ти. Пробвах го без успех, същото като с лентата на прогреса, за която говоря, замръзва и не показва нищо. Мислех, че статичността може да помогне, но не. В момента единственото работещо решение, което намерих, е това с Handler и премахване на всички изгледи в метода onDestroyView. Но ми изглежда грозно   -  person sebasira    schedule 04.04.2020


Отговори (2)


Мисля, че трябва да използвате AsycTask, когато новият ви фрагмент се зарежда

1- Заредете новия фрагмент

2 -Стартиране на Async задача

3 - Инициализирайте Progressdialog в конструктора на AsyncTask, където искате да заредите данните

4 - Показване на Progressdialog в onPreExecute() във вашата AsyncTask с dialog.show()

5 - Отхвърлете диалоговия прозорец Progressdialog в onPostExecute() във вашата AsyncTask с dialog.dismiss()

6- Актуализирайте потребителския интерфейс / задайте новите данни на вашия адаптер и т.н

Проверете тук Показване на зареждащ се бутон, докато се зарежда фрагмент

person Okan Serdaroğlu    schedule 04.04.2020
comment
Благодаря! Предпочитам да не използвам AsyncTask, тъй като те са (или ще бъдат) отхвърлени: разработчик. android.com/reference/android/os/AsyncTask - person sebasira; 04.04.2020
comment
Ти си прав. Имам предвид, че трябва да използвате фонов работник, когато се отваря вторият ви фрагмент. Rx Java може да бъде добра алтернатива. Или можете да използвате Coroutines, ако използвате Kotlin. techyourchance.com/asynctask-deprecated - person Okan Serdaroğlu; 04.04.2020
comment
За съжаление не съм в Kotlin (поне засега) и относно фоновия работник, затова мисля за Handler, защото е простота. Значи и вие смятате, че основният работник е правилният начин? Изглежда странно, че няма готово решение. Четох за AsyncLayoutInflater (developer.android.com/reference/androidx/ asynclayoutinflater/), но не знам дали ще ми помогне - person sebasira; 05.04.2020
comment
Е, манипулаторът изглежда не работи. Тествах го с просто оформление с TextView, което казва зареждане и работи, но когато го променя на ProgressBar, виждам само празен екран. Отново потребителският интерфейс е замразен, така че изглежда лентата за напредъка не може да бъде анимирана. И предполагам, че същото ще се случи с всеки фонов работник, защото инфлацията използва потребителския интерфейс, нали? - person sebasira; 05.04.2020
comment
Току-що прочетох връзката ви. Благодаря, полезно е и за мен. Виждам от вашата връзка AsyncLayoutInflater надува изгледите ви асинхронно. Това може да е вярно за вашия случай, но използването на RxJava също може да е вярно. мисля, че трябва да опитате и двете. - person Okan Serdaroğlu; 05.04.2020
comment
инфлацията използва потребителския интерфейс. Поради тази причина трябва да използвате фонова нишка за анимиране на вашия напредък. Решително опитайте RxJava - person Okan Serdaroğlu; 05.04.2020
comment
Но как мога да направя каквато и да е анимация със замразен потребителски интерфейс? Това ме обърква - person sebasira; 05.04.2020
comment
Мисля, че те ще бъдат в различни теми. Така че това не е проблем. Във вашия целеви фрагмент обаче трябва да проверите и своите изгледи. Защо замръзват при надуване. Може би трябва да направите оптимизация в оформлението си - person Okan Serdaroğlu; 05.04.2020
comment
Ами AFAIK има една и само една основна/UI нишка на Android... Тествам с AsyncLayoutInflater, засега добре. ProgressBar се показва и има обратно извикване, когато изгледът приключи с раздуването. Сега ще трябва да видя как да приложа/покажа раздутия изглед, защото през цялото време виждам само ProgressBar - person sebasira; 05.04.2020

Е, изглежда, че AsyncLayoutInflater е правилният начин!

Ето как го използвам:

private ViewGroup fragmentContainer;

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View dummyView = inflater.inflate(R.layout.fragment_dummy_loading, container, false);

    fragmentContainer = container;
    AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(getContext());


    asyncLayoutInflater.inflate(R.layout.fragment_real_layout, container, new AsyncLayoutInflater.OnInflateFinishedListener() {
        @Override
        public void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {
            fragmentContainer.removeAllViews();
            fragmentContainer.addView(view);
        }
    });


    return dummyView;
}


@Override
public void onStop() {
    super.onStop();
    fragmentContainer.removeAllViews();
}

Там горе fragment_dummy_loading е оформление с ProgressBar, а fragment_real_layout е наистина тежкото оформление.

Има само едно нещо, което не разбирам... и това е как мога да обвържа обекти към XML уиджетите (без да използвам Android Data Binding, ако е възможно), когато AsyncLayoutInflater връща се към infalte в нишката на потребителския интерфейс.

Според документите:

Ако оформлението, което се опитва да бъде раздуто, не може да бъде конструирано асинхронно по някаква причина, AsyncLayoutInflater автоматично ще се върне към раздуване в нишката на потребителския интерфейс.

И проверявам източника на AsyncLayoutInflater и мога да кажа, че методът onInflateFinished се извиква без значение дали инфлацията е била във фонов режим или в основната нишка

person sebasira    schedule 04.04.2020
comment
Когато прилагам тази грешка показва изгледи, добавени към фрагмент, изгледът на контейнера трябва да бъде свързан с фрагмент. преглед на androidx.constraintlayout.widget.constraintlayout Имате ли някакво решение? - person Ahmad; 02.03.2021
comment
Съжалявам, @Ahmad, никога не съм срещал тази грешка. Може би можете да създадете нов въпрос, да публикувате повече подробности и да ми изпратите връзката, за да мога да го разгледам - person sebasira; 02.03.2021