我有一个应用程序,其中主片段包含与 ViewPager2 绑定的选项卡布局,以便在三个不同片段之间导航。当我旋转手机屏幕时,应用程序崩溃并出现以下错误:
键 f#0 的片段不再存在:唯一 id c4576c9c-4bbd-4dc0-a304-b86653ed2820 (完整的错误堆栈在帖子末尾)
这是我的设置:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="@+id/main_activity_coordinator"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/app_bar"
android:theme="@style/Theme.VirtualGymBuddy.AppBarOverlay"
>
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:title="@string/app_name" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:tag="MAIN-FRAG"
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
main_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:tag="MAIN-FRAG"
android:id="@+id/main_relative_layout"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingTop="?attr/actionBarSize"
xmlns:tools="http://schemas.android.com/tools">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextAppearance="@style/MyTabLayoutStyle"
>
<com.google.android.material.tabs.TabItem
android:id="@+id/tab1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tab1" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tab2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tab2" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tab3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tab3" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:layout_below="@id/tabBar"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
/>
</RelativeLayout>
内容_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/content_main_constraint"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment
android:id="@+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
ActivityMain.java
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration appBarConfiguration;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
}
MainFragment.java
public class MainFragment extends Fragment {
private ViewPager2 viewPager;
private NavigationAdapter navigationAdapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setContentView(R.layout.main_fragment);
TabLayout tabLayout = getActivity().findViewById(R.id.tabBar);
int[] tabTexts = {
R.string.tab_1,
R.string.tab_2,
R.string.tab_3
};
// set up view pager and attach mediator to tab layout
this.viewPager = getActivity().findViewById(R.id.pager);
this.navigationAdapter = new NavigationAdapter(this);
viewPager.setAdapter(this.navigationAdapter);
new TabLayoutMediator(tabLayout, viewPager,
(tab, position) -> tab.setText(
tabTexts[position]
)
).attach();
}
}
NavigationAdapter.java
public class NavigationAdapter extends FragmentStateAdapter {
public NavigationAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
public NavigationAdapter(@NonNull Fragment fragment) {
super(fragment);
}
public NavigationAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
super(fragmentManager, lifecycle);
}
@NonNull
@Override
public Fragment createFragment(int position) {
switch (position) {
case 0:
return new Fragment1();
case 1:
return new Fragment2();
case 2:
return new Fragment3();
}
throw new AssertionError();
}
@Override
public int getItemCount() {
return 3;
}
}
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/MainFragment">
<fragment
android:id="@+id/MainFragment"
android:name="com.myapp.fragments.MainFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/main_fragment" />
</navigation>
一切正常,我可以浏览 ViewPager2 处理的片段。
但是,一旦我在其中一个片段内旋转屏幕,我就会得到:
2022-05-08 00:08:12.339 12343-12343/com.myapp E/FragmentManager: Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
2022-05-08 00:08:12.339 12343-12343/com.myapp E/FragmentManager: Activity state:
2022-05-08 00:08:12.358 12343-12343/com.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.myapp, PID: 12343
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp/com.myapp.MainActivity}: java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2827)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2902)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4784)
at android.app.ActivityThread.-wrap18(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1609)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:169)
at android.app.ActivityThread.main(ActivityThread.java:6578)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: java.lang.IllegalStateException: Fragment no longer exists for key f#0: unique id c4576c9c-4bbd-4dc0-a304-b86653ed2820
at androidx.fragment.app.FragmentManager.getFragment(FragmentManager.java:975)
at androidx.viewpager2.adapter.FragmentStateAdapter.restoreState(FragmentStateAdapter.java:549)
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:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3729)
at android.view.View.restoreHierarchyState(View.java:17620)
at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2130)
at android.app.Activity.onRestoreInstanceState(Activity.java:1122)
at android.app.Activity.performRestoreInstanceState(Activity.java:1077)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1260)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2800)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2902)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4784)
at android.app.ActivityThread.-wrap18(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1609)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:169)
at android.app.ActivityThread.main(ActivityThread.java:6578)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
我的设置有什么问题吗?
viewpager2 与 recyclerview 分离存在问题 https://issuetracker.google.com/issues/154751401
据我所知,有两个修复:
将此行添加到您的 xml viewpager2 组件中。
<androidx.viewpager2.widget.ViewPager2
android:layout_below="@id/tabBar"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:saveEnabled="false">
但是上面的实例并没有保存当前实例,片段每次弹出时都会重新创建该实例。
推荐的解决方案是在您的主片段上添加以下行
override fun onDestroyView() {
super.onDestroyView()
viewPager.adapter = null
}
我发现只有第一个修复有效: 安卓:saveEnabled =“假”
将适配器设置为 null 是不够的,因为 viewPager2 仍然使用片段 id 保存状态,然后尝试恢复它