IllegalStateException:使用 BottomNavigation 时尚未附加片段
IllegalStateException: Fragment has not been attached yet when using BottomNavigation
我正在使用 the following example 中的 BottomNavigationDrawer
/BottomSheetDialog
,其中 BottomNavigationDrawer
在单个 activity.[=31 中的多个片段之间切换=]
虽然implementing the BottomNavigationDrawer
, I realized that when the fragments were swapped without using the menu, i.e. by clicking a button in a fragment, this left the menu selection in an inconsistent state.另外,在主activity中使用函数触发FragmentTransaction
的解决方案导致崩溃。
public void resetNavDefault(){
if(fragment == null) {
fragment = new BottomNavigationDrawerFragment();
navigationView = fragment.getNavigationView();
} else navigationView = fragment.getNavigationView();
//navigationView.getMenu().getItem(0).setChecked(true);
Log.d(TAG, "resetNavDefault: setting check");
MenuItem item = navigationView.getMenu().getItem(0);
navigationView.getMenu().performIdentifierAction(item.getItemId(), 0);
}
我意识到这是由于 getSupportFragmentManager
为空引起的 and/or the fragment not being attached, and using getChildFragmentManager
不是正确的解决方案,因为我不使用嵌套片段,只使用单个 activity 中的多个片段。
我还尝试在 the onDetach
is overridden to make the childFragmentManager
accessible.
中实施此解决方法
This fix prevents the app from crashing with an NPE(由于 if (!isAdded()) return;
条件)但使启动新片段的按钮无响应(该按钮不起作用)。
如何使用 FragmentTransaction
在两个片段之间导航而不导致 NPE,并且不使菜单选择处于不一致状态?
我的BottomNavigationDrawer
实现如下:
BottomNavigationDrawer
public class BottomNavigationDrawerFragment extends BottomSheetDialogFragment {
BottomNavigationDrawerFragment fragment;
public NavigationView navigationView;
ImageView close, menu;
Statuser statuser;
RevivDatabase database;
String email, fname, lname;
TextView txtUsername, txtEmail;
private static String TAG = "BottomNavDrawerFragment";
public BottomNavigationDrawerFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bottomsheet, container, false);
txtUsername = view.findViewById(R.id.txtUsername);
txtEmail = view.findViewById(R.id.txtEmail);
navigationView = view.findViewById(R.id.navigation_view);
database = RevivDatabase.getDatabase(getActivity());
statuser = database.revivDao().getUserDetails();
fname = statuser.getFname();
lname = statuser.getLname();
email = statuser.getEmail();
txtEmail.setText(email);
txtUsername.setText(fname+" "+lname);
setRetainInstance(true);
close = view.findViewById(R.id.imgClose);
fragment = this;
close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
fragment.dismiss();
}
});
navigationView.setItemIconTintList(null);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
menuItem.setChecked(true);
int id = menuItem.getItemId();
FragmentManager manager;
try {
manager = getActivity().getSupportFragmentManager();
} catch (NullPointerException e) {
if (!isAdded()) return false;
manager = getChildFragmentManager();
Log.e(TAG, "onNavigationItemSelected: ", e);
}
switch (id){
case R.id.app_bar_incident:
navigationView.getMenu().findItem(id).setChecked(true);
manager.beginTransaction().replace(R.id.containerFrameLayout, new FragmentRevivIncidentDashboard()).commit();
Toast.makeText(getContext(), "Request Incident", Toast.LENGTH_SHORT).show();
fragment.dismiss();
break;
case R.id.app_bar_housecall:
navigationView.getMenu().findItem(id).setChecked(true);
manager.beginTransaction().replace(R.id.containerFrameLayout, new FragmentRevivHousecallDashboard()).commit();
fragment.dismiss();
Toast.makeText(getContext(), "Request Housecall", Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
navigationView.getMenu().findItem(id).setChecked(true);
manager.beginTransaction().replace(R.id.containerFrameLayout, new FragmentRevivSettingsMain()).commit();
Toast.makeText(getActivity().getApplicationContext(), "Settings", Toast.LENGTH_SHORT).show();
fragment.dismiss();
break;
}
return true;
}
});
return view;
}
@Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public NavigationView getNavigationView() {
return navigationView;
}
}
我的错误信息
09-13 00:26:32.996 28025-28025/com.package.name E/AndroidRuntime:
FATAL EXCEPTION: main
Process: com.package.name, PID: 28025
java.lang.IllegalStateException: Fragment has not been attached yet.
at android.support.v4.app.Fragment.instantiateChildFragmentManager(Fragment.java:2386)
at android.support.v4.app.Fragment.getChildFragmentManager(Fragment.java:842)
at com.package.name.Fragments.BottomNavigationDrawerFragment.onNavigationItemSelected(BottomNavigationDrawerFragment.java:108)
at android.support.design.widget.NavigationView.onMenuItemSelected(NavigationView.java:170)
at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:840)
at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:158)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:991)
at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:981)
at android.support.v7.view.menu.MenuBuilder.performIdentifierAction(MenuBuilder.java:977)
at com.package.name.Reviv.resetNavDefault(Reviv.java:644)
at com.package.name.Fragments.FragmentRevivSettingsMain.onClick(FragmentRevivSettingsMain.java:77)
at android.view.View.performClick(View.java:6303)
at android.view.View$PerformClick.run(View.java:24828)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6798)
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)
首先在您的 MainActivity 中实现按钮单击事件,如此 page。然后假设您在 MainActivity 中有 BottomNavigationDrawerFragment 实例,只需通过该实例获取视图并在单击按钮时通过该视图对导航菜单进行 setSelection 调用。确保在 bottomNavDrawerFragment.show(..) 调用后通过实例获取视图。否则你会得到一个 NPE 错误。
我正在使用 the following example 中的 BottomNavigationDrawer
/BottomSheetDialog
,其中 BottomNavigationDrawer
在单个 activity.[=31 中的多个片段之间切换=]
虽然implementing the BottomNavigationDrawer
, I realized that when the fragments were swapped without using the menu, i.e. by clicking a button in a fragment, this left the menu selection in an inconsistent state.另外,在主activity中使用函数触发FragmentTransaction
的解决方案导致崩溃。
public void resetNavDefault(){
if(fragment == null) {
fragment = new BottomNavigationDrawerFragment();
navigationView = fragment.getNavigationView();
} else navigationView = fragment.getNavigationView();
//navigationView.getMenu().getItem(0).setChecked(true);
Log.d(TAG, "resetNavDefault: setting check");
MenuItem item = navigationView.getMenu().getItem(0);
navigationView.getMenu().performIdentifierAction(item.getItemId(), 0);
}
我意识到这是由于 getSupportFragmentManager
为空引起的 and/or the fragment not being attached, and using getChildFragmentManager
不是正确的解决方案,因为我不使用嵌套片段,只使用单个 activity 中的多个片段。
我还尝试在 the onDetach
is overridden to make the childFragmentManager
accessible.
This fix prevents the app from crashing with an NPE(由于 if (!isAdded()) return;
条件)但使启动新片段的按钮无响应(该按钮不起作用)。
如何使用 FragmentTransaction
在两个片段之间导航而不导致 NPE,并且不使菜单选择处于不一致状态?
我的BottomNavigationDrawer
实现如下:
BottomNavigationDrawer
public class BottomNavigationDrawerFragment extends BottomSheetDialogFragment {
BottomNavigationDrawerFragment fragment;
public NavigationView navigationView;
ImageView close, menu;
Statuser statuser;
RevivDatabase database;
String email, fname, lname;
TextView txtUsername, txtEmail;
private static String TAG = "BottomNavDrawerFragment";
public BottomNavigationDrawerFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bottomsheet, container, false);
txtUsername = view.findViewById(R.id.txtUsername);
txtEmail = view.findViewById(R.id.txtEmail);
navigationView = view.findViewById(R.id.navigation_view);
database = RevivDatabase.getDatabase(getActivity());
statuser = database.revivDao().getUserDetails();
fname = statuser.getFname();
lname = statuser.getLname();
email = statuser.getEmail();
txtEmail.setText(email);
txtUsername.setText(fname+" "+lname);
setRetainInstance(true);
close = view.findViewById(R.id.imgClose);
fragment = this;
close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
fragment.dismiss();
}
});
navigationView.setItemIconTintList(null);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {
menuItem.setChecked(true);
int id = menuItem.getItemId();
FragmentManager manager;
try {
manager = getActivity().getSupportFragmentManager();
} catch (NullPointerException e) {
if (!isAdded()) return false;
manager = getChildFragmentManager();
Log.e(TAG, "onNavigationItemSelected: ", e);
}
switch (id){
case R.id.app_bar_incident:
navigationView.getMenu().findItem(id).setChecked(true);
manager.beginTransaction().replace(R.id.containerFrameLayout, new FragmentRevivIncidentDashboard()).commit();
Toast.makeText(getContext(), "Request Incident", Toast.LENGTH_SHORT).show();
fragment.dismiss();
break;
case R.id.app_bar_housecall:
navigationView.getMenu().findItem(id).setChecked(true);
manager.beginTransaction().replace(R.id.containerFrameLayout, new FragmentRevivHousecallDashboard()).commit();
fragment.dismiss();
Toast.makeText(getContext(), "Request Housecall", Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
navigationView.getMenu().findItem(id).setChecked(true);
manager.beginTransaction().replace(R.id.containerFrameLayout, new FragmentRevivSettingsMain()).commit();
Toast.makeText(getActivity().getApplicationContext(), "Settings", Toast.LENGTH_SHORT).show();
fragment.dismiss();
break;
}
return true;
}
});
return view;
}
@Override
public void onDetach() {
super.onDetach();
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(this, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public NavigationView getNavigationView() {
return navigationView;
}
}
我的错误信息
09-13 00:26:32.996 28025-28025/com.package.name E/AndroidRuntime: FATAL EXCEPTION: main Process: com.package.name, PID: 28025 java.lang.IllegalStateException: Fragment has not been attached yet. at android.support.v4.app.Fragment.instantiateChildFragmentManager(Fragment.java:2386) at android.support.v4.app.Fragment.getChildFragmentManager(Fragment.java:842) at com.package.name.Fragments.BottomNavigationDrawerFragment.onNavigationItemSelected(BottomNavigationDrawerFragment.java:108) at android.support.design.widget.NavigationView.onMenuItemSelected(NavigationView.java:170) at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:840) at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:158) at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:991) at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:981) at android.support.v7.view.menu.MenuBuilder.performIdentifierAction(MenuBuilder.java:977) at com.package.name.Reviv.resetNavDefault(Reviv.java:644) at com.package.name.Fragments.FragmentRevivSettingsMain.onClick(FragmentRevivSettingsMain.java:77) at android.view.View.performClick(View.java:6303) at android.view.View$PerformClick.run(View.java:24828) at android.os.Handler.handleCallback(Handler.java:789) at android.os.Handler.dispatchMessage(Handler.java:98) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6798) 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)
首先在您的 MainActivity 中实现按钮单击事件,如此 page。然后假设您在 MainActivity 中有 BottomNavigationDrawerFragment 实例,只需通过该实例获取视图并在单击按钮时通过该视图对导航菜单进行 setSelection 调用。确保在 bottomNavDrawerFragment.show(..) 调用后通过实例获取视图。否则你会得到一个 NPE 错误。