Android Fragment添加TextWatcher报空指针异常解决方法
报错根因
你触发的空指针异常是Fragment开发的典型新手问题,核心问题点如下:
- Fragment没有独立的
findViewById()方法,你代码里声明的所有控件(licznik、mianownik、kreska等)从未完成初始化赋值,默认值都是null,直接调用null对象的addTextChangedListener方法必然抛出空指针。 - 代码执行顺序错误:你在
onCreateView里还没加载布局、绑定控件,就提前给控件设置监听器。 - 原有
skracamy()方法还存在两个隐藏崩溃点:一是把EditText控件本身强转为CharSequence做判空,逻辑完全错误;二是调用setText()时直接传入int类型数值,系统会把int值当作字符串资源ID查找,找不到资源同样会崩溃;没有判断分母为0的场景,会触发算术异常。
Fragment下控件绑定正确写法
Fragment中查找控件不能直接调用Activity的方法,需要先把布局文件inflate为View对象,再通过这个View对象的findViewById()方法绑定控件,所有控件绑定完成后再设置监听器、执行业务逻辑。
修复后的完整可运行代码如下:
package com.example.tabbedapp; import android.os.Bundle; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; public class Fragment1 extends Fragment { protected TextView ulamekLabel; protected EditText licznik; protected EditText mianownik; protected TextView kUlamkowa; protected TextView calosci; protected TextView nLicznik; protected TextView nMianownik; protected TextView kreska; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 第一步:先加载布局,得到布局的根View对象 View rootView = inflater.inflate(R.layout.fragment1_layout, container, false); // 第二步:通过根View的findViewById绑定所有控件,这就是Fragment中等价于Activity findViewById的逻辑 ulamekLabel = rootView.findViewById(R.id.ulamekLabel); licznik = rootView.findViewById(R.id.licznik); mianownik = rootView.findViewById(R.id.mianownik); kUlamkowa = rootView.findViewById(R.id.kUlamkowa); calosci = rootView.findViewById(R.id.calosci); nLicznik = rootView.findViewById(R.id.nLicznik); nMianownik = rootView.findViewById(R.id.nMianownik); kreska = rootView.findViewById(R.id.kreska); // 第三步:控件全部绑定完成后,再设置监听器 licznik.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void afterTextChanged(Editable editable) { skracamy(); } }); mianownik.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void afterTextChanged(Editable editable) { skracamy(); } }); // 最后返回加载好的根View return rootView; } private void skracamy() { // 修正判空逻辑:判断输入框的文本内容,不是把控件本身强转 if (TextUtils.isEmpty(licznik.getText()) || TextUtils.isEmpty(mianownik.getText())) { calosci.setText("0"); calosci.setVisibility(View.INVISIBLE); nLicznik.setText("0"); nLicznik.setVisibility(View.INVISIBLE); nMianownik.setText("0"); nMianownik.setVisibility(View.INVISIBLE); kreska.setVisibility(View.INVISIBLE); return; } int a = Integer.parseInt(licznik.getText().toString()); int b = Integer.parseInt(mianownik.getText().toString()); // 新增分母为0判断,避免算术异常 if (b == 0) { calosci.setVisibility(View.INVISIBLE); nLicznik.setText("分母不能为0"); nLicznik.setVisibility(View.VISIBLE); nMianownik.setVisibility(View.INVISIBLE); kreska.setVisibility(View.INVISIBLE); return; } int nwd = NWD(a, b); int l = a / nwd; int m = b / nwd; int c = l / m; int d = l % m; calosci.setVisibility(View.VISIBLE); nLicznik.setVisibility(View.VISIBLE); nMianownik.setVisibility(View.VISIBLE); kreska.setVisibility(View.VISIBLE); if (c != 0 && d != 0) { // 修正setText传int的问题:int转String再设置,否则会被当成资源ID查找 calosci.setText(String.valueOf(c)); nLicznik.setText(String.valueOf(d)); nMianownik.setText(String.valueOf(m)); } else if (d != 0) { calosci.setText(""); nLicznik.setText(String.valueOf(d)); nMianownik.setText(String.valueOf(m)); } else { calosci.setText(String.valueOf(c)); nLicznik.setText("0"); nMianownik.setText("0"); } } int NWD(int a, int b) { if(b!=0) return NWD(b,a%b); return a; } }
关键注意点
- Fragment的控件绑定必须依托inflate得到的View对象调用
findViewById(),不能直接调用,也不要在onCreateView返回根View之前操作控件。 - 给TextView/EditText设置文本时,如果传入的是int类型,一定要手动转成String,否则系统会将int值解析为res/values/strings.xml里的资源ID,找不到对应资源就会崩溃。
- 涉及除法计算的逻辑,一定要提前判断除数是否为0,避免运行时算术异常。
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

