从Fragment向MainActivity传数据时出现NullPointerException求助
Fragment交互引发NullPointerException问题排查与解决
问题背景
在Udemy学习Android Studio时,实现列表Fragment与详情Fragment交互练习,点击列表项触发NullPointerException。报错指向MainActivity的onItemClicked方法中调用TextView.setText时对象为空。经排查,tvName和tvTel控件实际属于DetailFragment的布局文件,而非MainActivity布局,导致MainActivity通过findViewById无法获取控件实例。
相关代码与报错信息
DetailFragment代码
public class DetailFrag extends Fragment { ImageView imageView; TextView tvName, tvTel; private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM2 = "param2"; private String mParam1; private String mParam2; public DetailFrag() { // Required empty public constructor } public static DetailFrag newInstance(String param1, String param2) { DetailFrag fragment = new DetailFrag(); Bundle args = new Bundle(); args.putString(ARG_PARAM1, param1); args.putString(ARG_PARAM2, param2); fragment.setArguments(args); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mParam1 = getArguments().getString(ARG_PARAM1); mParam2 = getArguments().getString(ARG_PARAM2); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_detail, container, false); } }
ListFragment代码
public class ListFrag extends Fragment { RecyclerView recyclerView; RecyclerView.Adapter myAdapter; RecyclerView.LayoutManager layoutManager; View view; public ListFrag() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.fragment_list, container, false); return view; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); recyclerView = view.findViewById(R.id.list); recyclerView.setHasFixedSize(true); layoutManager = new LinearLayoutManager(this.getActivity()); recyclerView.setLayoutManager(layoutManager); myAdapter = new PersonAdapter(this.getActivity(), ApplicationClass.people); recyclerView.setAdapter(myAdapter); } }
MainActivity代码
public class MainActivity extends AppCompatActivity implements PersonAdapter.ItemClicked { TextView tvName,tvTel; EditText etName, etTel; Button btnAdd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvName = findViewById(R.id.tvName); tvTel = findViewById(R.id.tvTel); etName = findViewById(R.id.etName); etTel = findViewById(R.id.etTel); btnAdd = findViewById(R.id.btnAdd); btnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } @Override public void onItemClicked(int index) { tvName.setText(ApplicationClass.people.get(index).getName()); tvTel.setText(ApplicationClass.people.get(index).getTelNr()); } }
异常信息
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.recyclerfragments, PID: 31089 java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference at com.example.recyclerfragments.MainActivity.onItemClicked(MainActivity.java:43) at com.example.recyclerfragments.PersonAdapter$ViewHolder$1.onClick(PersonAdapter.java:46) at android.view.View.performClick(View.java:7575) at android.view.View.performClickInternal(View.java:7548) at android.view.View.access$3600(View.java:837) at android.view.View$PerformClick.run(View.java:28933) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:236)
MainActivity布局文件
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragmentContainerView" android:name="com.example.recyclerfragments.ListFrag" android:layout_width="200dp" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:layout="@layout/fragment_list" /> <LinearLayout android:id="@+id/linearLayout" android:layout_width="0dp" android:layout_height="match_parent" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/fragmentContainerView" app:layout_constraintTop_toTopOf="parent"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragmentContainerView2" android:name="com.example.recyclerfragments.DetailFrag" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" tools:layout="@layout/fragment_detail" /> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragmentContainerView3" android:name="com.example.recyclerfragments.AddPersonFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="3" tools:layout="@layout/fragment_add_person" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
解决方案
方案1:通过MainActivity中转数据给DetailFragment
适合入门场景的直接修复方式:
- 修改DetailFragment:初始化控件并添加更新UI方法
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_detail, container, false); // 初始化布局内控件 tvName = view.findViewById(R.id.tvName); tvTel = view.findViewById(R.id.tvTel); imageView = view.findViewById(R.id.imageView); return view; } // 对外暴露更新详情的方法 public void updatePersonInfo(String name, String tel) { tvName.setText(name); tvTel.setText(tel); }
- 修改MainActivity:移除无效控件声明,通过Fragment实例更新UI
public class MainActivity extends AppCompatActivity implements PersonAdapter.ItemClicked { // 移除不属于当前布局的tvName、tvTel变量 EditText etName, etTel; Button btnAdd; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 移除tvName、tvTel的初始化代码 etName = findViewById(R.id.etName); etTel = findViewById(R.id.etTel); btnAdd = findViewById(R.id.btnAdd); btnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); } @Override public void onItemClicked(int index) { // 获取已加载的DetailFragment实例 DetailFrag detailFragment = (DetailFrag) getSupportFragmentManager() .findFragmentById(R.id.fragmentContainerView2); if (detailFragment != null) { Person selectedPerson = ApplicationClass.people.get(index); detailFragment.updatePersonInfo(selectedPerson.getName(), selectedPerson.getTelNr()); } } }
方案2:使用ViewModel共享数据(推荐)
遵循Android组件化最佳实践,实现Fragment解耦:
- 创建共享ViewModel
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class PersonViewModel extends ViewModel { private final MutableLiveData<Person> selectedPerson = new MutableLiveData<>(); public void selectPerson(Person person) { selectedPerson.setValue(person); } public MutableLiveData<Person> getSelectedPerson() { return selectedPerson; } }
- 修改ListFragment适配器点击逻辑:直接通过ViewModel传递数据
// 在PersonAdapter的ViewHolder点击事件中 itemView.setOnClickListener(v -> { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { Person person = ApplicationClass.people.get(position); // 获取Activity级别的ViewModel实例 PersonViewModel viewModel = new ViewModelProvider(requireActivity()).get(PersonViewModel.class); viewModel.selectPerson(person); } });
- 修改DetailFragment:观察ViewModel数据变化并更新UI
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 初始化控件 tvName = view.findViewById(R.id.tvName); tvTel = view.findViewById(R.id.tvTel); imageView = view.findViewById(R.id.imageView); // 监听ViewModel数据变化 PersonViewModel viewModel = new ViewModelProvider(requireActivity()).get(PersonViewModel.class); viewModel.getSelectedPerson().observe(getViewLifecycleOwner(), person -> { if (person != null) { tvName.setText(person.getName()); tvTel.setText(person.getTelNr()); } }); }
- 简化MainActivity:无需再实现
PersonAdapter.ItemClicked接口,移除相关代码
内容的提问来源于stack exchange,提问作者Chris Damianidis
相关产品推荐
相关产品推荐

