Android Fragment前后导航时如何保留输入数据?
我太懂这种场景了——用add()+hide()代替replace()就是为了保留前一个Fragment的输入状态,结果回退修改再进入后一个Fragment时,之前填的内容没了,简直头疼!咱们一步步来解决:
问题根源先搞清楚
你现在的switchFragments方法每次跳转到下一个Fragment时,都会新建一个实例并add到容器。比如你第一次进FragmentC时创建了实例C1,返回修改FragmentB后再进FragmentC,又创建了新的实例C2——你看到的是C2,自然看不到C1里的输入内容!
解决方案分两步走
1. 先复用已存在的Fragment实例
修改你的switchFragments方法,通过tag先查找是否已有该Fragment的实例,有就直接show,没有才新建add:
public void switchFragments(Fragment fragment, Fragment currentFragment, boolean addToBackStack, String tag) { FragmentManager fm = currentFragment.getActivity().getSupportFragmentManager(); // 先检查容器里是否已经存在这个tag的Fragment Fragment existingFragment = fm.findFragmentByTag(tag); FragmentTransaction transaction = fm.beginTransaction(); transaction.hide(currentFragment); if (existingFragment != null) { // 存在就直接显示已有的实例,不用新建 transaction.show(existingFragment); } else { // 不存在才添加新实例 transaction.add(R.id.fragment_container_sign_up, fragment, tag); } if (addToBackStack) { transaction.addToBackStack(tag); } transaction.commit(); }
这样不管你来回切换多少次,FragmentA/B/C都是同一个实例,输入内容自然不会凭空消失。
2. 用ViewModel做数据持久化(推荐)
虽然复用实例能解决大部分问题,但如果遇到屏幕旋转这类配置变化,Fragment的View会重建,EditText的内容还是可能丢。用ViewModel绑定Activity生命周期,能彻底解决这个问题:
首先创建一个共享的ViewModel:
public class SignUpViewModel extends ViewModel { private final MutableLiveData<String> fragmentAInput = new MutableLiveData<>(); private final MutableLiveData<String> fragmentBInput = new MutableLiveData<>(); private final MutableLiveData<String> fragmentCInput = new MutableLiveData<>(); public MutableLiveData<String> getFragmentAInput() { return fragmentAInput; } public MutableLiveData<String> getFragmentBInput() { return fragmentBInput; } public MutableLiveData<String> getFragmentCInput() { return fragmentCInput; } }
然后在每个Fragment里绑定ViewModel,实时保存和恢复输入:
以FragmentA为例:
public class FragmentA extends Fragment { private SignUpViewModel viewModel; private TextInputEditText editText; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取和Activity绑定的ViewModel,所有Fragment共用同一个实例 viewModel = new ViewModelProvider(requireActivity()).get(SignUpViewModel.class); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_a, container, false); editText = view.findViewById(R.id.et_a); Button nextBtn = view.findViewById(R.id.btn_next_a); // 恢复之前保存的输入内容 viewModel.getFragmentAInput().observe(getViewLifecycleOwner(), savedText -> { if (savedText != null && !savedText.equals(editText.getText().toString())) { editText.setText(savedText); } }); // 输入变化时实时保存到ViewModel editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void afterTextChanged(Editable s) { viewModel.getFragmentAInput().setValue(s.toString()); } }); nextBtn.setOnClickListener(v -> { FragmentB fragmentB = new FragmentB(); switchFragments(fragmentB, FragmentA.this, true, "FragmentB"); }); return view; } }
FragmentB和FragmentC照猫画虎,分别绑定对应的fragmentBInput和fragmentCInput就行。
可选:用onSaveInstanceState兜底
如果不想用ViewModel,也可以在每个Fragment里重写onSaveInstanceState保存输入,在onViewCreated里恢复:
@Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); // 保存当前输入内容 outState.putString("input_content", editText.getText().toString()); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); editText = view.findViewById(R.id.et_xxx); // 恢复保存的内容 if (savedInstanceState != null) { String savedText = savedInstanceState.getString("input_content"); editText.setText(savedText); } }
不过这种方法只适合简单数据,ViewModel还是更靠谱。
最后验证一下
修改完后,你返回FragmentB修改内容,再进入FragmentC时,看到的会是之前的那个FragmentC实例,加上ViewModel的持久化,输入内容肯定不会丢了!
内容的提问来源于stack exchange,提问作者Antonio

