У указанного ребенка уже есть родитель.Фрагмент на задней прессе - PullRequest
0 голосов
/ 07 мая 2019

Я знаю, что на этот вопрос, похоже, задают и отвечают, но это не так. Потому что я еще не нашел решения.

У меня есть активность с большим количеством фрагментов. И я сохраняю историю всех переходов (могу вернуться к каждому фрагменту в порядке LIFO с помощью обратной печати - потому что каждый добавляет в задний стек)

Я хочу достичь следующей функции: Когда я нажимаю назад - вид нижнего фрагмента не должен быть воссоздан.

Я делаю следующее

1) Используйте компоненты навигации и переходы для Android, например

fun navigate(@IdRes resId: Int, bundle: Bundle? = null, navOptions: NavOptions? = null, sharedElements: List<View>? = null) {
        navController.navigate(resId, bundle, navOptions, sharedElements?.let {
            if (it.isEmpty()) null else
                FragmentNavigatorExtras(
                        *it.map { view -> Pair(view, view.transitionNameCompat.safe) }
                                .filter { pair -> pair.second.isNotEmpty() }
                                .toTypedArray()
                )
        })
    }

где idRes - идентификатор назначения (не идентификатор перехода)

2) держать представление содержимого во фрагменте и отсоединять его от родителя в методе представления onCreateView. Поскольку getView() возвращает нулевой четный фрагмент, появляется из заднего стека.

private var contentView: View? = null

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
        prepareView(contentView) ?: createView(inflater, container)

private fun prepareView(view: View?): View? {
    val parent = view?.parent
    val viewGroup = parent as? ViewGroup
    viewGroup?.removeView(view)
    return view
}

protected open fun createView(inflater: LayoutInflater, container: ViewGroup?): View? {
    val layout = layout()
    if (layout == -1)
        throw IllegalArgumentException("You need to override \"layout()\" fun or override \"createView\" fun")
    return inflater.inflate(layout(), container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    if (contentView == null) {
        onFirstInitialization(view, savedInstanceState)
    } else {
        onNextInitialization(view, savedInstanceState)
    }
    onEachInitialization(view, savedInstanceState)
    contentView = view
}

У меня есть исключение ТОЛЬКО ЕСЛИ я перехожу к следующему фрагменту и быстро нажимаю "назад"! В обычном режиме все хорошо и правильно.

Исключение:

 java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
        at android.view.ViewGroup.addViewInner(ViewGroup.java:5050)
        at android.view.ViewGroup.addView(ViewGroup.java:4881)
        at android.view.ViewGroup.addView(ViewGroup.java:4821)
        at android.view.ViewGroup.addView(ViewGroup.java:4794)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:890)
        at androidx.fragment.app.FragmentManagerImpl.addAddedFragments(FragmentManagerImpl.java:2092)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1866)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1822)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1723)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2624)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2580)
        at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2571)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1235)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1301)
        at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2620)
        at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2580)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1470)
        at android.app.Activity.performStart(Activity.java:7176)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3086)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6923)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Я могу решить это по методу жизненного цикла от prepareView до onDestroyView и все работает правильно, но это приводит к утечке памяти: я выясняю - когда представление не имеет родителя после onDestroyView, тогда onDestroy никогда Вызов и фрагменты сохраняются в памяти даже после обратного нажатия.

Вы можете сказать: «Эй, чувак, не торопись, используй свое приложение медленно, и, как ты говоришь, никогда не будет сгенерировано исключение. Но приложение в работе, и я встретился с этой ошибкой» :( Как я могу справиться с этим случай

UPDATE: Я немного исследовал и кое-что выяснил. Исключение выдается только тогда, когда вид не отделен от окна. И просмотр отсоединения от окна только через 300-400 мс после открытия нового фрагмента

1 Ответ

1 голос
/ 07 мая 2019

Да!Это так.Вы не можете снова создать его объект, потому что у него есть родитель.Главная проблема, которую вы, возможно, не заметили, заключается в том, что всякий раз, когда вы снова вызываете свой фрагмент, вызывается onCreateView().

Итак, в сортировке я предлагаю вам переместить код с onCreateView() на onCreate() методфрагмент.

Я также боролся с этим типом проблемы.Итак, накачайте ваше представление в методе onCreate() и верните основное представление в методе onCreateView().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...