java.lang.IllegalStateException: FragmentManager is already executing transactions at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1778) at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1814) at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:297)
at androidx.viewpager2.adapter.FragmentStateAdapter.removeFragment(FragmentStateAdapter.java:464) at androidx.viewpager2.adapter.FragmentStateAdapter.gcFragments(FragmentStateAdapter.java:228) at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:569) at androidx.viewpager2.widget.ViewPager2.restorePendingState(ViewPager2.java:350) at androidx.viewpager2.widget.ViewPager2.dispatchRestoreInstanceState(ViewPager2.java:375)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3829) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3829) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3829) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3829) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3829)
at android.view.View.restoreHierarchyState(View.java:18613)
at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:573) at androidx.fragment.app.FragmentStateManager.restoreViewState(FragmentStateManager.java:356) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1189) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356) at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434) at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497) at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625) at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:247)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:541) at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1392) at android.app.Activity.performStart(Activity.java:7260) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3009)
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:1840) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:207) at android.app.ActivityThread.main(ActivityThread.java:6878) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
解决方案
如果在 Fragment 中使用 ViewPager2,那么 FragmentStateAdapter 应该使用 childFragmentManager。将
overridefuncreateViewPagerAdapter(): RecyclerView.Adapter<*> { val items = items // avoids resolving the ViewModel multiple times returnobject : FragmentStateAdapter(this) { overridefuncreateFragment(position: Int): PageFragment { val itemId = items.itemId(position) val itemText = items.getItemById(itemId) return PageFragment.create(itemText) } overridefungetItemCount(): Int = items.size overridefungetItemId(position: Int): Long = items.itemId(position) overridefuncontainsItem(itemId: Long): Boolean = items.contains(itemId) } }
/** A very simple collection of items. Optimized for simplicity (i.e. not performance). */ classItemsViewModel : ViewModel() { privatevar nextValue = 1L
java.lang.IllegalStateException: Design assumption violated. at androidx.viewpager2.widget.ViewPager2.updateCurrentItem(ViewPager2.java:538) at androidx.viewpager2.widget.ViewPager2$4.onAnimationsFinished(ViewPager2.java:518) at androidx.recyclerview.widget.RecyclerView$ItemAnimator.isRunning(RecyclerView.java:13244) at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:515) at android.view.View.layout(View.java:15596)
/** * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem * where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout. * * This solution has limitations when using multiple levels of nested scrollable elements * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2). */ classNestedScrollableHost : FrameLayout { constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
privatevar touchSlop = 0 privatevar initialX = 0f privatevar initialY = 0f privateval parentViewPager: ViewPager2? get() { var v: View? = parent as? View while (v != null && v !is ViewPager2) { v = v.parent as? View } return v as? ViewPager2 }
privatefunhandleInterceptTouchEvent(e: MotionEvent) { val orientation = parentViewPager?.orientation ?: return
// Early return if child can't scroll in same direction as parent if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) { return }
if (e.action == MotionEvent.ACTION_DOWN) { initialX = e.x initialY = e.y parent.requestDisallowInterceptTouchEvent(true) } elseif (e.action == MotionEvent.ACTION_MOVE) { val dx = e.x - initialX val dy = e.y - initialY val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
// assuming ViewPager2 touch-slop is 2x touch-slop of child val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5felse1f val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1felse.5f
if (scaledDx > touchSlop || scaledDy > touchSlop) { if (isVpHorizontal == (scaledDy > scaledDx)) { // Gesture is perpendicular, allow all parents to intercept parent.requestDisallowInterceptTouchEvent(false) } else { // Gesture is parallel, query child if movement in that direction is possible if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) { // Child can scroll, disallow all parents to intercept parent.requestDisallowInterceptTouchEvent(true) } else { // Child cannot scroll, allow all parents to intercept parent.requestDisallowInterceptTouchEvent(false) } } } } } }
classPreviewPagesActivity : FragmentActivity() { overridefunonCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_viewpager2) findViewById<ViewPager2>(R.id.view_pager).apply { // Set offscreen page limit to at least 1, so adjacent pages are always laid out offscreenPageLimit = 1 val recyclerView = getChildAt(0) as RecyclerView recyclerView.apply { clipToPadding = false val leftPadding = resources.getDimensionPixelOffset(R.dimen.halfPageMargin) + resources.getDimensionPixelOffset(R.dimen.peekOffset) // setting padding on inner RecyclerView puts overscroll effect in the right place // TODO: expose in later versions not to rely on // getChildAt(0) which might break setPadding(leftPadding, 0, leftPadding, 0) } adapter = Adapter() } }
java.lang.IllegalStateException: Fragment no longer exists for key f1: unique id 55efaee5-a65c-4e57-9281-7c8f8f6e4156 at androidx.fragment.app.FragmentManager.getFragment(FragmentManager.java:960) at androidx.fragment.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:328) at androidx.viewpager.widget.ViewPager.onRestoreInstanceState(ViewPager.java:1461) at android.view.View.dispatchRestoreInstanceState(View.java:20032) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3922) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3928) at android.view.View.restoreHierarchyState(View.java:20010) at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:639) at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:3010) at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:3001) at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:580) at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:285) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002) at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:230) at android.app.ActivityThread.main(ActivityThread.java:8018) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:526) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)