【问题标题】:I get a crash and nullPointer when Filtering through my custom ListView with Custom Adapter使用自定义适配器过滤我的自定义 ListView 时出现崩溃和 nullPointer
【发布时间】:2020-06-08 10:43:23
【问题描述】:

我有一个包含 100 多个条目/对象的自定义 listView,这就是我想要实现搜索/过滤功能的原因。我已经走了很远,但是当我在 editText 中输入内容时,应用程序由于此处的 nullPointer 而崩溃:

@Override public void onTextChanged(CharSequence s, int start, int before, int count) { adapter.getFilter().filter(s); }

这是我的 MainActivity:

public class MainActivity extends AppCompatActivity implements TextWatcher{
MyAdapter adapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ListView lw = findViewById(R.id.lw);
    EditText suche = findViewById(R.id.suchen);
    suche.addTextChangedListener(this);

    final ArrayList<Objekt> arrayList = new ArrayList();

    arrayList.add(new Objekt("AAA", "AAA1"));
    arrayList.add(new Objekt("BBB", "BBB1"));
    arrayList.add(new Objekt("CCC", "CCC1"));
    arrayList.add(new Objekt("DDD", "DDD1"));
    arrayList.add(new Objekt("EEE", "EEE1"));
    arrayList.add(new Objekt("FFF", "FFF1"));

    final MyAdapter adapter = new MyAdapter(this,arrayList);

    lw.setAdapter(adapter);

    lw.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            String selection = arrayList.get(position).getKuerzel();
            if(selection.equals("AAA")){
                Intent intent = new Intent(MainActivity.this, AAA.class);
                startActivity(intent);
            } else if (selection.equals("BBB")){
                Intent intent = new Intent(MainActivity.this, BBB.class);
                startActivity(intent);
            } else if (selection.equals("CCC")){
                Intent intent = new Intent(MainActivity.this, CCC.class);
                startActivity(intent);
            } else if (selection.equals("DDD")){
                Intent intent = new Intent(MainActivity.this, DDD.class);
                startActivity(intent);
            } else if (selection.equals("EEE")){
                Intent intent = new Intent(MainActivity.this, EEE.class);
                startActivity(intent);
            } else if (selection.equals("FFF")){
                Intent intent = new Intent(MainActivity.this, FFF.class);
                startActivity(intent);
            }
        }
    });

}

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        adapter.getFilter().filter(s); //<-- nullPointer here
    }

    @Override
    public void afterTextChanged(Editable s) {

    }

}

这是我的 CustomAdapter,实现了 Filterable、覆盖 getFilter 等:

public class MyAdapter extends BaseAdapter implements Filterable {
Context context;
ArrayList<Objekt> arr, tempArr;
CustomFilter cs;

public MyAdapter(Context context, ArrayList<Objekt> arr) {
    this.context = context;
    this.arr = arr;
    this.tempArr = arr;
}

@Override
public int getCount() {
    return arr.size();
}

@Override
public Object getItem(int position) {
    return arr.get(position);
    //return position;
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    convertView = LayoutInflater.from(context).inflate(R.layout.adapter_view_layout,parent,false);

    TextView kuerzel = convertView.findViewById(R.id.textViewKuerzel);
    TextView fullAdress = convertView.findViewById(R.id.textViewFullAdress);

    kuerzel.setText(arr.get(position).getKuerzel());
    fullAdress.setText(arr.get(position).getFullAdress());

    return convertView;
}

@Override
public Filter getFilter() {
    if (cs == null){
        cs = new CustomFilter();
    }

return cs;
}

class CustomFilter extends Filter{
    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
        FilterResults results = new FilterResults();
        if (constraint != null && constraint.length()>0) {
            constraint = constraint.toString().toUpperCase();

            ArrayList<Objekt> filters = new ArrayList<Objekt>();

            for (int i = 0; i < tempArr.size(); i++) {
                if (tempArr.get(i).getKuerzel().toUpperCase().contains(constraint)) {
                    Objekt objekt = new Objekt(tempArr.get(i).getKuerzel(), tempArr.get(i).getFullAdress());
                    filters.add(objekt);
                }
            }
            results.count = filters.size();
            results.values = filters;
        }
        else
        {
            results.count = tempArr.size();
            results.values = tempArr;
        }
        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        arr = (ArrayList<Objekt>)results.values;
        notifyDataSetChanged();
    }
}

}

我对游戏相当陌生,筛选了很多帖子以试图了解为什么会出现空指针,但我就是想不通。任何帮助将不胜感激!

编辑: 我的日志猫

2020-06-08 12:19:59.631 22501-22501/com.example.testlistviewformbm E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testlistviewformbm, PID: 22501
java.lang.NullPointerException: Attempt to invoke virtual method 'android.widget.Filter com.example.testlistviewformbm.MyAdapter.getFilter()' on a null object reference
    at com.example.testlistviewformbm.MainActivity.onTextChanged(MainActivity.java:109)

还有我的 Objekt 类:

public class Objekt {

String kuerzel;
String fullAdress;

public Objekt(String kuerzel, String fullAdress) {
    this.kuerzel = kuerzel;
    this.fullAdress = fullAdress;
}

public String getKuerzel() {
    return kuerzel;
}

public void setKuerzel(String kuerzel) {
    this.kuerzel = kuerzel;
}

public String getFullAdress() {
    return fullAdress;
}

public void setFullAdress(String fullAdress) {
    this.fullAdress = fullAdress;
}

}

【问题讨论】:

    标签: java android listview filter


    【解决方案1】:

    这是因为当您设置listener 时,此时adapter 尚未初始化。

    而且您还在类级别声明了一个变量adapter,但再次创建了局部变量适配器并将局部值传递给列表。

    所以换行为adapter = new MyAdapter(this,arrayList);

    如果这不能解决问题,那么在设置适配器后设置监听器。

    adapter = new MyAdapter(this,arrayList);
    suche.addTextChangedListener(this);
    

    【讨论】:

      【解决方案2】:

      好的,所以我把问题简化了一点:

      public class StackOverflow extends AppCompatActivity implements TextWatcher
      {
          MyAdapter adapter;
      
          @Override
          protected void onCreate( Bundle savedInstanceState )
          {
              /* ..*/
      
              final MyAdapter adapter = new MyAdapter( this, arrayList );
      
              ListView lw = findViewById(R.id.lw);
              /*..*/;
      
              lw.setAdapter( adapter );
      
              /* ..*/
          }
      
          /* ..*/
      
          @Override
          public void onTextChanged( CharSequence s, int start, int before, int count )
          {
              adapter.getFilter().filter( s ); //<-- nullPointer here
          }
      
          /* ..*/
      }
      
      class MyAdapter{
          /*...*/
      }
      

      现在,位于NullPointerException 位置的adapter 变量,即null,是在类顶部定义的字段:

          MyAdapter adapter;
      

      然而,这与onCreate 方法中的变量相同:

             final MyAdapter adapter = new MyAdapter( this, arrayList );
      

      它具有相同的名称,并且隐藏该字段,但由于它是自己声明的,因此是一个不同的变量。它只能在onCreate 方法中访问(“在范围内”),在onTextChanged 中不存在。

      移除阴影并初始化字段将解决NullPointerException

             /*deleted: final Adapter*/ adapter = new MyAdapter( this, arrayList );
      

      这可行,但在方法内初始化字段不是一个好习惯。通常最好在构造函数中初始化字段。但是,由于它需要 MainActivity 本身作为其上下文,因此这在这里不起作用。我建议进一步重构它,以消除 Adapter 构造函数中对 MainActivity 参数的需要,这超出了此答案的范围。

      希望这会有所帮助!

      【讨论】:

      • 非常感谢您的详尽回答!很好的帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-29
      • 1970-01-01
      • 2011-08-12
      • 2020-12-06
      相关资源
      最近更新 更多