Фрагменты уничтожены / воссозданы с помощью компонентов Android Navigation Jetpack

Я пытаюсь реализовать Навигация с компонентами архитектуры Jetpack в своем существующем приложении.

У меня есть приложение с одной активностью, где основной фрагмент (ListFragment) представляет собой список элементов. В настоящее время, когда пользователь нажимает на элемент списка, fragmentTransaction.add(R.id.main, detailFragment) добавляет в стек второй фрагмент. Таким образом, при нажатии кнопки «Назад» DetailFragment отсоединяется и снова отображается ListFragment.

Архитектура навигации делает это автоматически. Вместо добавления нового фрагмента это заменены, поэтому представление фрагмента уничтожается, вызывается onDestroyView(), а onCreateView() вызывается при нажатии кнопки «Назад» для воссоздания представления.

Я понимаю, что это хороший шаблон, используемый с LiveData и ViewModel, чтобы не использовать больше памяти, чем необходимо, но в моем случае это раздражает, потому что список имеет сложную компоновку, а его раздувание требует времени и ресурсов ЦП, а также потому, что мне понадобится чтобы сохранить позицию прокрутки списка и снова прокрутить до той же позиции, которую пользователь оставил фрагмент. Это возможно, но кажется, что должен существовать лучший способ.

Я попытался «сохранить» представление в частном поле фрагмента и повторно использовать его на onCreateView(), если оно уже есть, но это кажется анти-шаблоном.

private View view = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    if (view == null) {
        view = inflater.inflate(R.layout.fragment_list, container, false);
        //...
    }

    return view;
}

Есть ли другой, более элегантный способ избежать повторного раздувания макета?

Вы нашли ответ на это? Я сейчас застрял в той же ситуации

Raicky Derwent 12.02.2019 10:42

Нет, кажется, нет другого решения, кроме как воссоздать представление.

pauminku 12.02.2019 11:09

Просто чтобы другие пользователи знали, кажется очень простым сохранить и восстановить положение прокрутки списков (recyclerView), например, посмотрите на этот другой вопрос: stackoverflow.com/questions/47110168/…

pauminku 13.02.2019 13:21

Мне удалось исправить эту проблему по-другому в моем коде. Вместо того, чтобы каждый раз создавать новую модель представления в onCreateView(), я использовал ViewModelProviders.of(). Это сохранит мою позицию прокрутки без необходимости проходить через saveInstanceStates

Raicky Derwent 25.02.2019 11:02

Да, это правильный способ работы с ViewModel, но он не решает проблему повторного заполнения представления. Я думаю, что шаблон состоит в том, чтобы повторно раздуть его, отдавая приоритет использованию памяти, а не производительности.

pauminku 25.02.2019 11:05

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

cutiko 09.03.2019 01:56

ты решил эту проблему? Действительно ли работает решение Ian Lake?

Kapta 16.05.2019 10:24

да, это работает, но учтите, что, как сказано, потребляется больше памяти.

pauminku 22.05.2019 11:43

@pauminku, можем ли мы поделиться рабочим кодом этой проблемы.

Akash Chaudhary 16.04.2020 11:03

посмотри мой ответ на этот вопрос может тебе поможет Сохранить состояние в компоненте навигации

Elblasy 05.09.2020 01:44
106
10
32 195
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Ответ принят как подходящий

Ян Лейк из google ответил мне, что мы можем сохранить представление в переменной и вместо раздувать новый макет, просто вернись экземпляр предварительно сохраненный вид на onCreateView()

Источник: https://twitter.com/ianhlake/status/1103522856535638016

утечка может показать это как утечку, но это ложный положительный результат..

Итак, пример кода, который я поставил в вопросе, правильный? Рады узнать. Не могли бы вы дать ссылку на разговор с Яном Лейком? в случае, если это на общественном форуме.

pauminku 07.03.2019 10:25

Было бы здорово, если бы у нас был образец. @erluxman

Hardy 10.08.2019 17:22

после раздувания макета сохраните его в переменной перед возвратом.. это так просто

erluxman 11.08.2019 14:36

Есть ли решение DataBinding для этого? Я пробовал этот подход, но, похоже, он не работает.

YellowJ 07.10.2019 15:06
github.com/square/leakcanary/issues/1656 утверждает обратное.
rupinderjeet 11.02.2020 13:19

Есть ли способ добиться обратного перехода общего элемента без сохранения представления в переменной? обратный переход не работает, потому что представление воссоздается, и сохранение представления в переменной вызывает исключения OOM

G_comp 26.03.2020 22:28

Этот ответ является искажением цитируемого твита. Ян прямо упоминает, что это «постоянная трата памяти и ресурсов», что означает, что вам следует избегать этого, если это возможно.

Wyko 12.05.2020 09:34

Если вы здесь и хотите избежать, по крайней мере, вызова API (который находится в onViewCreated или onActivityCreated), который вызывается из-за повторного создания фрагмента, переместите такие вызовы в соответствующие ViewModels и вызовите их внутри блока в этом.

333 02.10.2020 17:33

@YellowJ проверьте мой ответ, это моя помощь stackoverflow.com/a/67924398/8660721

Mohammad Sommakia 10.06.2021 17:44

Я пробовал так, и это работает для меня.

  • Инициализировать ViewModel с помощью navGraphViewModels (в режиме реального времени в области навигации)
  • Сохраните любое состояние восстановления в ViewModel
// fragment.kt
private val vm by navGraphViewModels<VM>(R.id.nav_graph) { vmFactory }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // Restore state
    vm.state?.let {
        (recycler.layoutManager as GridLayoutManager).onRestoreInstanceState(it)
    }
}

override fun onPause() {
    super.onPause()
    // Store state
    vm.state = (recycler.layoutManager as GridLayoutManager).onSaveInstanceState()
}

// vm.kt
var state:Parcelable? = null

Вы спасли мой день!

THANN Phearum 04.12.2019 04:24

Лучшее решение от команды Android: github.com/android/architecture-components-samples/tree/mast‌​er/…

Samnang CHEA 06.12.2019 05:50

Это состояние будет потеряно из-за нехватки памяти, вам, вероятно, следует поместить его в SavedStateHandle.

EpicPandaForce 16.03.2020 15:15

Вы можете иметь постоянное представление для своего фрагмента с помощью реализации ниже

Базовый фрагмент

open class BaseFragment : Fragment(){

        var hasInitializedRootView = false
        private var rootView: View? = null

        fun getPersistentView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?, layout: Int): View? {
            if (rootView == null) {
                // Inflate the layout for this fragment
                rootView = inflater?.inflate(layout,container,false)
            } else {
                // Do not inflate the layout again.
                // The returned View of onCreateView will be added into the fragment.
                // However it is not allowed to be added twice even if the parent is same.
                // So we must remove rootView from the existing parent view group
                // (it will be added back).
                (rootView?.getParent() as? ViewGroup)?.removeView(rootView)
            }

            return rootView
        }
    }

Главный фрагмент

class MainFragment : BaseFragment() {


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return getPersistentView(inflater, container, savedInstanceState, R.layout.content_main)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (!hasInitializedRootView) {
            hasInitializedRootView = true
            setListeners()
            loadViews()
        }
    }
}

Источник

Это сэкономило мне много времени. Большое спасибо за публикацию этого

dev2505 13.03.2020 14:44

Лучший ответ, который я нашел по теме! Спасибо, чемпион

orelzion 20.05.2020 10:51

Livedata не работает (не наблюдает) с этим решением.

Kuvonchbek Yakubov 05.08.2020 08:27

Далеко лучший и самый простой, но livedata не работает. Я не могу перестать думать, что нет ПРАВИЛЬНОГО подхода к этой проблеме. Согласно трекеру проблем на github навигационного компонента, они все еще говорят, что перерисовка была их целью. плотина

March3April4 15.09.2020 11:53

Я получаю эту ошибку: У указанного дочернего элемента уже есть родитель. Сначала вы должны вызвать removeView() для родителя дочернего элемента. Также иногда приложение зависает

G_comp 22.09.2020 03:45

Лучший ответ на эту тему: я исправил проблему наблюдения за оперативными данными в моем случае, повторно наблюдая за моей базовой LiveData в getPresistentView() в части else следующим образом (rootView?.parent as? ViewGroup)?.removeView(rootView) viewModel.stateBase.observe(viewLifecycleOwner, Observer { baseRender() }) }, где baseRender() — это абстрактная функция, которую я переопределяю в любом дочернем фрагменте.

Khaled Ahmed 01.11.2020 11:44

можно ли это изменить для использования с привязкой данных?

vivek panchal 07.05.2021 05:03

Это работает, но есть побочный эффект! предположим, что вы находитесь в frag1, затем переходите к frag2 с помощью навигационного компонента, и вы возвращаетесь к frag1 и наблюдаете за результатом из frag2. наблюдатель ничего не увидит, а слушатели вообще не работают, потому что hasInitializedRootView имеет значение true.

Alireza Noorali 15.05.2021 08:59

Хотя я думаю, что NavigationAdvancedSample — лучшее решение, я также решил эту проблему, используя код @shahab-rauf. Потому что у меня недостаточно времени, чтобы применить это в моем проекте.

Базовый фрагмент

abstract class AppFragment: Fragment() {

    private var persistingView: View? = null

    private fun persistingView(view: View): View {
        val root = persistingView
        if (root == null) {
            persistingView = view
            return view
        } else {
            (root.parent as? ViewGroup)?.removeView(root)
            return root
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val p = if (persistingView == null) onCreatePersistentView(inflater, container, savedInstanceState) else persistingView // prevent inflating
        if (p != null) {
            return persistingView(p)
        }
        return super.onCreateView(inflater, container, savedInstanceState)
    }
    protected open fun onCreatePersistentView(inflater: LayoutInflater,
                                              container: ViewGroup?,
                                              savedInstanceState: Bundle?): View? {
        return null
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (persistingView != null) {
            onPersistentViewCreated(view, savedInstanceState)
        }
    }

    protected open fun onPersistentViewCreated(view: View, savedInstanceState: Bundle?) {
        logv("onPersistentViewCreated")
    }
}

Реализует

class DetailFragment : AppFragment() {
    override fun onCreatePersistentView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // I used data-binding
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_program_detail, container, false)
        binding.model = viewModel
        binding.lifecycleOwner = this
        return binding.root
    }

    override fun onPersistentViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onPersistentViewCreated(view, savedInstanceState)
        
        // RecyclerView bind with adapter
        binding.curriculumRecycler.adapter = adapter
        binding.curriculumRecycler.apply {
            layoutManager = LinearLayoutManager(context)
            setHasFixedSize(true)
        }
        viewModel.curriculums.observe(viewLifecycleOwner, Observer {
            adapter.applyItems(it ?: emptyList())
        })

        viewModel.refresh()
    }
}

если вы следуете расширенному образцу из Google, они используют расширение. Вот его модифицированная версия. В моем случае мне пришлось показывать и скрывать фрагмент, когда они прикреплялись и отсоединялись:

/**
 * Manages the various graphs needed for a [BottomNavigationView].
 *
 * This sample is a workaround until the Navigation Component supports multiple back stacks.
 */
fun BottomNavigationView.setupWithNavController(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int,
    intent: Intent
): LiveData<NavController> {

    // Map of tags
    val graphIdToTagMap = SparseArray<String>()
    // Result. Mutable live data with the selected controlled
    val selectedNavController = MutableLiveData<NavController>()

    var firstFragmentGraphId = 0

    // First create a NavHostFragment for each NavGraph ID
    navGraphIds.forEachIndexed { index, navGraphId ->
        val fragmentTag = getFragmentTag(index)

        // Find or create the Navigation host fragment
        val navHostFragment = obtainNavHostFragment(
            fragmentManager,
            fragmentTag,
            navGraphId,
            containerId
        )

        // Obtain its id
        val graphId = navHostFragment.navController.graph.id

        if (index == 0) {
            firstFragmentGraphId = graphId
        }

        // Save to the map
        graphIdToTagMap[graphId] = fragmentTag

        // Attach or detach nav host fragment depending on whether it's the selected item.
        if (this.selectedItemId == graphId) {
            // Update livedata with the selected graph
            selectedNavController.value = navHostFragment.navController
            attachNavHostFragment(fragmentManager, navHostFragment, index == 0, fragmentTag)
        } else {
            detachNavHostFragment(fragmentManager, navHostFragment)
        }
    }

    // Now connect selecting an item with swapping Fragments
    var selectedItemTag = graphIdToTagMap[this.selectedItemId]
    val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId]
    var isOnFirstFragment = selectedItemTag == firstFragmentTag

    // When a navigation item is selected
    setOnNavigationItemSelectedListener { item ->
        // Don't do anything if the state is state has already been saved.
        if (fragmentManager.isStateSaved) {
            false
        } else {
            val newlySelectedItemTag = graphIdToTagMap[item.itemId]
            if (selectedItemTag != newlySelectedItemTag) {
                // Pop everything above the first fragment (the "fixed start destination")
                fragmentManager.popBackStack(
                    firstFragmentTag,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE
                )
                val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
                        as NavHostFragment

                // Exclude the first fragment tag because it's always in the back stack.
                if (firstFragmentTag != newlySelectedItemTag) {
                    // Commit a transaction that cleans the back stack and adds the first fragment
                    // to it, creating the fixed started destination.
                    if (!selectedFragment.isAdded) {
                        fragmentManager.beginTransaction()
                            .setCustomAnimations(
                                R.anim.nav_default_enter_anim,
                                R.anim.nav_default_exit_anim,
                                R.anim.nav_default_pop_enter_anim,
                                R.anim.nav_default_pop_exit_anim
                            )
                            .add(selectedFragment, newlySelectedItemTag)
                            .setPrimaryNavigationFragment(selectedFragment)
                            .apply {
                                // Detach all other Fragments
                                graphIdToTagMap.forEach { _, fragmentTagIter ->
                                    if (fragmentTagIter != newlySelectedItemTag) {
                                        hide(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
                                    }
                                }
                            }
                            .addToBackStack(firstFragmentTag)
                            .setReorderingAllowed(true)
                            .commit()
                    } else {
                        fragmentManager.beginTransaction()
                            .setCustomAnimations(
                                R.anim.nav_default_enter_anim,
                                R.anim.nav_default_exit_anim,
                                R.anim.nav_default_pop_enter_anim,
                                R.anim.nav_default_pop_exit_anim
                            )
                            .show(selectedFragment)
                            .setPrimaryNavigationFragment(selectedFragment)
                            .apply {
                                // Detach all other Fragments
                                graphIdToTagMap.forEach { _, fragmentTagIter ->
                                    if (fragmentTagIter != newlySelectedItemTag) {
                                        hide(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
                                    }
                                }
                            }
                            .addToBackStack(firstFragmentTag)
                            .setReorderingAllowed(true)
                            .commit()
                    }
                }
                selectedItemTag = newlySelectedItemTag
                isOnFirstFragment = selectedItemTag == firstFragmentTag
                selectedNavController.value = selectedFragment.navController
                true
            } else {
                false
            }
        }
    }

    // Optional: on item reselected, pop back stack to the destination of the graph
    setupItemReselected(graphIdToTagMap, fragmentManager)

    // Handle deep link
    setupDeepLinks(navGraphIds, fragmentManager, containerId, intent)

    // Finally, ensure that we update our BottomNavigationView when the back stack changes
    fragmentManager.addOnBackStackChangedListener {
        if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) {
            this.selectedItemId = firstFragmentGraphId
        }

        // Reset the graph if the currentDestination is not valid (happens when the back
        // stack is popped after using the back button).
        selectedNavController.value?.let { controller ->
            if (controller.currentDestination == null) {
                controller.navigate(controller.graph.id)
            }
        }
    }
    return selectedNavController
}

private fun BottomNavigationView.setupItemReselected(
    graphIdToTagMap: SparseArray<String>,
    fragmentManager: FragmentManager
) {
    setOnNavigationItemReselectedListener { item ->
        val newlySelectedItemTag = graphIdToTagMap[item.itemId]
        val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
                as NavHostFragment
        val navController = selectedFragment.navController
        // Pop the back stack to the start destination of the current navController graph
        navController.popBackStack(
            navController.graph.startDestination, false
        )
    }
}

private fun BottomNavigationView.setupDeepLinks(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int,
    intent: Intent
) {
    navGraphIds.forEachIndexed { index, navGraphId ->
        val fragmentTag = getFragmentTag(index)


        // Find or create the Navigation host fragment
        val navHostFragment = obtainNavHostFragment(
            fragmentManager,
            fragmentTag,
            navGraphId,
            containerId
        )
        // Handle Intent
        if (navHostFragment.navController.handleDeepLink(intent)
            && selectedItemId != navHostFragment.navController.graph.id
        ) {
            this.selectedItemId = navHostFragment.navController.graph.id
        }
    }
}

private fun detachNavHostFragment(
    fragmentManager: FragmentManager,
    navHostFragment: NavHostFragment
) {
    fragmentManager.beginTransaction()
        .hide(navHostFragment)
        .commitNow()
}

private fun attachNavHostFragment(
    fragmentManager: FragmentManager,
    navHostFragment: NavHostFragment,
    isPrimaryNavFragment: Boolean,
    fragmentTag: String
) {
    if (navHostFragment.isAdded) return
    fragmentManager.beginTransaction()
        .add(navHostFragment, fragmentTag)
        .apply {
            if (isPrimaryNavFragment) {
                setPrimaryNavigationFragment(navHostFragment)
            }
        }
        .commitNow()

}

private fun obtainNavHostFragment(
    fragmentManager: FragmentManager,
    fragmentTag: String,
    navGraphId: Int,
    containerId: Int
): NavHostFragment {
    // If the Nav Host fragment exists, return it
    val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?
    existingFragment?.let { return it }

    // Otherwise, create it and return it.
    val navHostFragment = NavHostFragment.create(navGraphId)
    fragmentManager.beginTransaction()
        .add(containerId, navHostFragment, fragmentTag)
        .commitNow()
    return navHostFragment
}

private fun FragmentManager.isOnBackStack(backStackName: String): Boolean {
    val backStackCount = backStackEntryCount
    for (index in 0 until backStackCount) {
        if (getBackStackEntryAt(index).name == backStackName) {
            return true
        }
    }
    return false
}

private fun getFragmentTag(index: Int) = "bottomNavigation#$index"

Было бы здорово, если бы ваш ответ также содержал руководство по использованию.

Mahdi-Malv 12.04.2021 22:51

Это тот же ответ, что и предложенный @Shahab Rauf, только дополнительной вещью является включение привязки данных и реализация onCreateView только в BaseFragment вместо дочерних фрагментов. А также инициализация navController в onViewCreated() BaseFragment.

Базовый фрагмент

abstract class BaseFragment<T : ViewDataBinding, VM : BaseViewModel<UiState>> : Fragment() {

protected lateinit var binding: T
var hasInitializedRootView = false
private var rootView: View? = null

protected abstract val mViewModel: ViewModel
protected lateinit var navController: NavController

fun getPersistentView(
    inflater: LayoutInflater?,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
    layout: Int
): View? {
    if (rootView == null) {
        binding = DataBindingUtil.inflate(inflater!!, getFragmentView(), container, false)
        //setting the viewmodel
        binding.setVariable(BR.mViewModel, mViewModel)
        // Inflate the layout for this fragment
        rootView = binding.root
    } else {
        // Do not inflate the layout again.
        // The returned View of onCreateView will be added into the fragment.
        // However it is not allowed to be added twice even if the parent is same.
        // So we must remove rootView from the existing parent view group
        // (it will be added back).
        (rootView?.getParent() as? ViewGroup)?.removeView(rootView)
    }

    return rootView
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? = getPersistentView(inflater, container, savedInstanceState, getFragmentView())


//this method is used to get the fragment layout file
abstract fun getFragmentView(): Int

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    navController = Navigation.findNavController(view)
}
}

HomeFragment (любой фрагмент, расширяющий BaseFragment)

class HomeFragment : BaseFragment<HomeFragmentBinding, HomeViewModel>(),
RecycleViewClickListener {

override val mViewModel by viewModel<HomeViewModel>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    if (!hasInitializedRootView) {hasInitializedRootView = true
        setListeners()
        loadViews()
         --------

}

Привет, проблема исправлена ​​в последней версии 2.4.0-alpha01, теперь есть официальная поддержка навигации по нескольким обратным стекам.

Проверьте ссылку: https://developer.android.com/jetpack/androidx/releases/navigation#version_240_2

Это отличная функция, но я думаю, что она никоим образом не помогает решить проблему: избегайте повторного создания представления.

pauminku 25.05.2021 07:37

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

просто сделайте это:

    lateinit var binding: FragmentConnectBinding
 override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    if (this::binding.isInitialized) {
        binding
    } else {
        binding = FragmentConnectBinding.inflate(inflater, container, false)
        binding.viewModel = viewModel
        binding.model = connectModel
        binding.lifecycleOwner = viewLifecycleOwner
        viewModel.buildAllProfiles()
// do what ever you need to do in first creation
    }
        setupObservers()
        return binding.root
}

Для разработчиков Java, как описано и объединено из приведенных выше ответов,

Базовый фрагмент.java

public abstract class BaseFragment<T extends ViewDataBinding, V extends BaseViewModel> extends Fragment {

    private View mRootView;
    private T mViewDataBinding;
    private V mViewModel;
    public boolean hasInitializedRootView = false;
    private View rootView = null;

    public View getPersistentView(LayoutInflater layoutInflater, ViewGroup container, Bundle saveInstanceState, int layout) {

        if (rootView == null) {
            mViewDataBinding = DataBindingUtil.inflate(layoutInflater, layout, container, false);
            mViewDataBinding.setVariable(getBindingVariable(),mViewModel);
            rootView = mViewDataBinding.getRoot();
        }else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove rootView from the existing parent view group
            // (it will be added back).
            ViewGroup viewGroup = (ViewGroup) rootView.getParent();
            if (viewGroup != null){
                viewGroup.removeView(rootView);
            }
        }
        return rootView;
    }
}

Реализовать в своем фрагменте как,

@AndroidEntryPoint
public class YourFragment extends BaseFragment<YourFragmentBinding, YourViewModel> {


@Override
    public View onCreateView(@NonNull @NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return getPersistentView(inflater, container, savedInstanceState, getLayoutId());
    }


@Override
    public void onViewCreated(@NonNull @NotNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!hasInitializedRootView){
            hasInitializedRootView = true;
            // do your work here

        }

    }


}

Другие вопросы по теме