EditText+ViewPager2实现Fragment传值报错及替代方案咨询
问题描述
我在第一个Fragment里用EditText输入整数值,通过Integer.parseInt转换后,尝试用接口把值传到第二个Fragment,项目里用了ViewPager2,这是我第一次用ViewPager,经验不足。现在有两个问题:
- 代码运行报错,错误在
SM.sendData(Integer.parseInt(text1.getText().toString()));,不知道原因; - 有没有其他传值方式,比如用Bundle?
First Fragment代码
public class FirstFragment extends Fragment { private FragmentFirstBinding binding; SendMessage SM; @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { binding = FragmentFirstBinding.inflate(inflater, container, false); return binding.getRoot(); } public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final EditText text1 = view.findViewById(R.id.text1); binding.buttonFirst.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { SM.sendData(Integer.parseInt(text1.getText().toString())); NavHostFragment.findNavController(FirstFragment.this) .navigate(R.id.action_FirstFragment_to_SecondFragment); } }); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } interface SendMessage { void sendData(int number); } @Override public void onAttach(@NonNull Context context) { super.onAttach(context); try { SM = (SendMessage) getActivity(); } catch (ClassCastException e) { throw new ClassCastException("Error in retrieving data. Please try again"); } } }
Second Fragment代码
public class SecondFragment extends Fragment { private FragmentSecondBinding binding; TextView textView; @Override public View onCreateView( @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) { binding = FragmentSecondBinding.inflate(inflater, container, false); return binding.getRoot(); } public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); textView = view.findViewById(R.id.score_result); binding.buttonSecond.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { NavHostFragment.findNavController(SecondFragment.this).navigate(R.id.action_SecondFragment_to_FirstFragment); } }); } protected void displayReceivedData(int number) { textView.setText(number); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; } }
ViewPager Adapter代码
public class ViewPagerAdapter extends FragmentStateAdapter { public ViewPagerAdapter(FragmentManager fm, Lifecycle behavior) { super(fm,behavior); } @NonNull @Override public Fragment createFragment(int position) { Fragment fragment = null; if (position == 0) { fragment = new FirstFragment(); } else if (position == 1) { fragment = new SecondFragment(); } assert fragment != null; return fragment; } @Override public int getItemCount() { return 2; } }
Main Activity代码
public class MainActivity extends AppCompatActivity implements FirstFragment.SendMessage{ ViewPager2 viewPager; ViewPagerAdapter viewPagerAdapter; private AppBarConfiguration appBarConfiguration; private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); viewPager = findViewById(R.id.viewPager2); viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager(),getLifecycle()); viewPager.setAdapter(viewPagerAdapter); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); } @Override public void sendData(int number) { SecondFragment secondFragment=new SecondFragment(); SecondFragment f = (SecondFragment) getSupportFragmentManager().findFragmentByTag(String.valueOf(secondFragment)); f.displayReceivedData(number); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_status) { 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(); } }
问题解答
第一个错误的原因及修复
报错核心原因
- 输入非法导致格式异常:EditText为空或输入非数字内容时,
Integer.parseInt会抛出NumberFormatException; - 获取SecondFragment实例失败:你在
sendData里新建了一个SecondFragment实例,用它的toString值查找Fragment,根本找不到ViewPager中已加载的实例,导致f为空,调用方法时触发空指针; - setText方法误用:
textView.setText(number)会把整数当成资源ID,而非文本内容,会触发资源找不到的崩溃; - 方法访问权限问题:SecondFragment的
displayReceivedData是protected修饰,外部无法直接调用。
修复步骤
- 校验EditText输入合法性:
binding.buttonFirst.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String input = text1.getText().toString().trim(); if (input.isEmpty()) { Toast.makeText(requireContext(), "请输入数字", Toast.LENGTH_SHORT).show(); return; } try { int number = Integer.parseInt(input); SM.sendData(number); NavHostFragment.findNavController(FirstFragment.this) .navigate(R.id.action_FirstFragment_to_SecondFragment); } catch (NumberFormatException e) { Toast.makeText(requireContext(), "请输入有效的整数", Toast.LENGTH_SHORT).show(); } } });
- 正确获取ViewPager中的SecondFragment:FragmentStateAdapter创建的Fragment标签格式为
f{position},直接按位置查找:
@Override public void sendData(int number) { // 查找ViewPager中位置1的SecondFragment实例 String tag = "f1"; SecondFragment f = (SecondFragment) getSupportFragmentManager().findFragmentByTag(tag); if (f != null && f.isAdded()) { f.displayReceivedData(number); } }
- 修改SecondFragment的方法权限与setText逻辑:
// 改成public权限 public void displayReceivedData(int number) { // 把整数转为字符串,避免当成资源ID textView.setText(String.valueOf(number)); }
其他传值方式(含Bundle)
1. ViewModel共享数据(推荐ViewPager场景)
ViewModel可以在Activity的生命周期内共享数据,适合ViewPager这种多Fragment共存的场景:
- 创建共享ViewModel:
public class SharedViewModel extends ViewModel { private final MutableLiveData<Integer> numberData = new MutableLiveData<>(); public void setNumber(int number) { numberData.setValue(number); } public LiveData<Integer> getNumber() { return numberData; } }
- FirstFragment设置数据:
binding.buttonFirst.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String input = text1.getText().toString().trim(); if (input.isEmpty()) { Toast.makeText(requireContext(), "请输入数字", Toast.LENGTH_SHORT).show(); return; } try { int number = Integer.parseInt(input); SharedViewModel viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); viewModel.setNumber(number); NavHostFragment.findNavController(FirstFragment.this) .navigate(R.id.action_FirstFragment_to_SecondFragment); } catch (NumberFormatException e) { Toast.makeText(requireContext(), "请输入有效的整数", Toast.LENGTH_SHORT).show(); } } });
- SecondFragment观察数据:
@Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); textView = view.findViewById(R.id.score_result); SharedViewModel viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); viewModel.getNumber().observe(getViewLifecycleOwner(), number -> { textView.setText(String.valueOf(number)); }); // 原有按钮逻辑... }
2. Bundle传递(适合Navigation跳转场景)
通过Navigation跳转时直接携带Bundle:
- FirstFragment传递数据:
binding.buttonFirst.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String input = text1.getText().toString().trim(); if (input.isEmpty()) { Toast.makeText(requireContext(), "请输入数字", Toast.LENGTH_SHORT).show(); return; } try { int number = Integer.parseInt(input); Bundle bundle = new Bundle(); bundle.putInt("KEY_NUMBER", number); NavHostFragment.findNavController(FirstFragment.this) .navigate(R.id.action_FirstFragment_to_SecondFragment, bundle); } catch (NumberFormatException e) { Toast.makeText(requireContext(), "请输入有效的整数", Toast.LENGTH_SHORT).show(); } } });
- SecondFragment接收数据:
@Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); textView = view.findViewById(R.id.score_result); if (getArguments() != null) { int number = getArguments().getInt("KEY_NUMBER", 0); textView.setText(String.valueOf(number)); } // 原有按钮逻辑... }
3. 接口回调(已修复的原有方式)
就是你最初使用的接口传值,只要完成前面的错误修复,就能正常工作。
内容的提问来源于stack exchange,提问作者user20780119
相关产品推荐
相关产品推荐

