You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android新增CardView触发NullPointerException异常修复

问题描述

开发时间表应用时,点击add按钮向存储行对象的ArrayList<row> rows集合新增CardView行时应用崩溃,报错为java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.matches(java.lang.String)' on a null object reference,即使上一行已经填写内容也无法新增。
每个行布局包含1个展示序号的TextView、2个分别填写任务名、工时的EditText,原有逻辑会校验上一行内容非空才允许新增。

崩溃根因
  • row类构造函数逻辑无效:构造方法内仅声明了局部变量接收成员变量默认值,没有给成员变量赋初始值。新建row对象时,String类型的tasksName默认值为null,int类型的hours默认值为0,直接对null调用matches()方法直接触发空指针。
  • 数据没有双向绑定:现有Adapter仅实现了将row对象的数据设置到控件上的逻辑,没有监听EditText的输入事件将用户填写的内容同步回rows集合中的row对象,即使用户填完内容,row实例里的tasksName还是初始的null、hours还是0,校验逻辑永远无法拿到用户输入的真实值。
  • 校验逻辑存在缺陷:原有代码用matches(" ")仅能匹配单个空格的字符串,无法正确识别空字符串、全空格等无效输入场景。
  • 列表刷新位置错误:新增行后调用notifyItemInserted(rows.size())传入的索引越界,列表长度为n时元素索引范围是0~n-1,新插入元素的位置应为rows.size()-1。
修复方案

1. 修正row类定义

遵循Java类名大驼峰命名规范将类名改为Row,修正构造函数给成员变量赋合理默认值:

package com.example.timetable;

public class Row {
    int index;
    int hours;
    String tasksName;

    public Row() {
        // 给成员变量赋默认值,避免空指针
        this.index = 0;
        this.hours = 0;
        this.tasksName = "";
    }

    public int getHours() {
        return hours;
    }

    public int getIndex() {
        return index;
    }

    public String getTasksName() {
        return tasksName;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void setHours(int hours) {
        this.hours = hours;
    }

    public void setTasksName(String tasksName) {
        this.tasksName = tasksName;
    }
}

注意:全局替换所有原row类引用为Row,包括ArrayList<Row> rows、Adapter的集合定义等位置。

2. 给Adapter增加输入同步逻辑

在Adapter中给EditText添加文本变化监听,用户输入时实时同步内容到对应的数据对象,注意先移除旧监听避免绑定数据时触发错乱:

// 顶部新增导入
import android.text.Editable;
import android.text.TextWatcher;

// 修正onBindViewHolder方法
@Override
public void onBindViewHolder(@NonNull cellVH holder, int position) {
    Row currentRow = unitCell.get(position);
    // 序号直接用位置+1生成,无需单独存储
    holder.index.setText(String.valueOf(position + 1));

    // 移除旧的文本监听,避免复用 ViewHolder 时数据错乱
    if (holder.tasksName.getTag() instanceof TextWatcher) {
        holder.tasksName.removeTextChangedListener((TextWatcher) holder.tasksName.getTag());
    }
    if (holder.totalHours.getTag() instanceof TextWatcher) {
        holder.totalHours.removeTextChangedListener((TextWatcher) holder.totalHours.getTag());
    }

    // 把控件当前值设置为数据对象存储的值
    holder.tasksName.setText(currentRow.getTasksName());
    holder.totalHours.setText(currentRow.getHours() == 0 ? "" : String.valueOf(currentRow.getHours()));

    // 监听任务名输入,实时同步到数据对象
    TextWatcher taskNameWatcher = 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) {
            currentRow.setTasksName(s.toString().trim());
        }
        @Override
        public void afterTextChanged(Editable s) {}
    };
    holder.tasksName.addTextChangedListener(taskNameWatcher);
    holder.tasksName.setTag(taskNameWatcher);

    // 监听工时输入,实时同步到数据对象,处理非数字输入异常
    TextWatcher hoursWatcher = 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) {
            String input = s.toString().trim();
            try {
                currentRow.setHours(input.isEmpty() ? 0 : Integer.parseInt(input));
            } catch (NumberFormatException e) {
                currentRow.setHours(0);
            }
        }
        @Override
        public void afterTextChanged(Editable s) {}
    };
    holder.totalHours.addTextChangedListener(hoursWatcher);
    holder.totalHours.setTag(hoursWatcher);
}

3. 修正新增按钮的校验与刷新逻辑

增加空判断,修正校验规则,修正列表刷新的索引位置:

addBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Row lastRow = rows.get(rows.size() - 1);
        String lastTaskName = lastRow.getTasksName();
        int lastHours = lastRow.getHours();
        
        // 校验上一行是否填写完整
        if (lastTaskName == null || lastTaskName.isEmpty() || lastHours <= 0) {
            Toast.makeText(TimeTable.this, "Fill out previous row's content", Toast.LENGTH_SHORT).show();
            return;
        }

        // 新增行
        Row newRow = new Row();
        newRow.setIndex(rows.size());
        rows.add(newRow);
        // 传入正确的插入位置索引
        mAdapter.notifyItemInserted(rows.size() - 1);
        // 自动滚动到最新添加的行
        recyclerView.smoothScrollToPosition(rows.size() - 1);
    }
});

内容的提问来源于stack exchange,提问作者Parth Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:45:31