Android Studio中底部导航关联Fragment间Bundle传递数据失败
Android 底部导航关联Fragment跨页面传值失败解决方案
问题根因
你在GoalsFragment的SET GOAL点击事件中创建的ProgressFragment是临时的全新实例,和底部导航管理器中已经初始化、实际显示的ProgressFragment不是同一个对象。你给临时实例设置的Arguments不会作用到实际展示的页面上,因此实际运行时ProgressFragment拿不到传递的参数,int类型默认返回0。
推荐解决方案
方案1:使用共享ViewModel(Jetpack官方推荐同Activity下Fragment传值方案)
该方案无需Fragment互相持有引用,代码耦合度低,支持数据变化实时响应。
- 新建共享ViewModel类:
import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class SharedGoalViewModel extends ViewModel { private final MutableLiveData<Integer> selectedGoal = new MutableLiveData<>(0); public void setSelectedGoal(int goal) { selectedGoal.setValue(goal); } public LiveData<Integer> getSelectedGoal() { return selectedGoal; } }
- 修改
GoalsFragment逻辑,点击按钮时给ViewModel存值:
// 先在onCreate中初始化共享ViewModel,注意要依附Activity才能跨Fragment共享 private SharedGoalViewModel sharedViewModel; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedGoalViewModel.class); } // 修改SET GOAL按钮的点击逻辑,删掉原来创建ProgressFragment实例的无用代码 case R.id.fg_goals_setgoal: if(check == 0) { Toast.makeText(getActivity(), "Please select one of the 3 Goals!", Toast.LENGTH_LONG).show(); break; } // 存值到共享ViewModel sharedViewModel.setSelectedGoal(check); String toastText; if (check == 1) { toastText = "Current Goal: Socialize More"; } else if (check == 2) { toastText = "Current Goal: Enhance Study Motives"; } else { toastText = "Current Goal: Physical Exercise"; } Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show(); // 如果需要点击后自动切到Progress页面,添加如下代码调用底部导航切换即可 // ((你的主Activity类名) requireActivity()).底部导航控件id.setSelectedItemId(R.id.nav_progress); break;
- 修改
ProgressFragment逻辑,观察ViewModel的值更新UI:
private SharedGoalViewModel sharedViewModel; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedGoalViewModel.class); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_progress, container, false); TextView fg_progress_header2 = rootView.findViewById(R.id.fg_progress_header2); // 观察目标值变化,自动更新UI sharedViewModel.getSelectedGoal().observe(getViewLifecycleOwner(), goal -> { System.out.println(goal); if (goal == 0) { fg_progress_header2.setText("请先选择目标"); return; } String headerText; if (goal == 1) { headerText = "Socialize More"; } else if (goal == 2) { headerText = "Enhance Study Motives"; } else { headerText = "Physical Exercise"; } fg_progress_header2.setText(headerText); }); return rootView; }
方案2:使用SharedPreferences存储(适合需要持久化目标选择的场景)
如果需要退出APP再打开仍然保留上次选择的目标,可以用本地存储实现:
- 修改
GoalsFragment的SET GOAL点击逻辑:
case R.id.fg_goals_setgoal: if(check == 0) { Toast.makeText(getActivity(), "Please select one of the 3 Goals!", Toast.LENGTH_LONG).show(); break; } // 存储到本地SharedPreferences SharedPreferences sp = requireContext().getSharedPreferences("app_config", Context.MODE_PRIVATE); sp.edit().putInt("selected_goal", check).apply(); // 吐司逻辑同上 break;
- 修改
ProgressFragment逻辑,页面显示时读取本地值:
@Override public void onResume() { super.onResume(); TextView fg_progress_header2 = getView().findViewById(R.id.fg_progress_header2); SharedPreferences sp = requireContext().getSharedPreferences("app_config", Context.MODE_PRIVATE); int goal = sp.getInt("selected_goal", 0); // 更新UI逻辑同上 }
注意事项
你原来的代码中直接调用b.getInt("goal")如果Bundle为空会触发空指针异常,建议改用getInt("goal", 0)的重载方法指定默认值,避免崩溃。
内容的提问来源于stack exchange,提问作者Loizos Aristeidis
相关产品推荐
相关产品推荐

