如何解决Androidstudio“尝试在空对象引用上调用虚拟方法”/

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (1567)

有以下错误:

7167-7167/com.example.andrewg.sqltutorial E/AndroidRuntime: FATAL EXCEPTION: 
main
Process: com.example.andrewg.sqltutorial, PID: 7167
java.lang.NullPointerException: Attempt to invoke virtual method 'void 
com.example.andrewg.sqltutorial.ProductAdapter.filter(java.lang.String)' on a 
null object reference
    at
com.example.andrewg.sqltutorial.DisplayProduct$1.onQueryTextChange
(DisplayProduct.java:47)
    at android.widget.SearchView.onTextChanged(SearchView.java:1250)
    at android.widget.SearchView.access$2100(SearchView.java:98)
    at android.widget.SearchView$10.onTextChanged(SearchView.java:1776)
    at android.widget.TextView.sendOnTextChanged(TextView.java:9754)
    at android.widget.TextView.handleTextChanged(TextView.java:9851)
    at android.widget.TextView$ChangeWatcher.onTextChanged(TextView.java:12512)
    at android.text.SpannableStringBuilder.sendTextChanged(SpannableStringBuilder.java:1263)
    at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:575)
    at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:506)
    at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:36)
    at android.view.inputmethod.BaseInputConnection.replaceText(BaseInputConnection.java:843)
    at android.view.inputmethod.BaseInputConnection.setComposingText(BaseInputConnection.java:616)
    at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:396)
    at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:85)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6649)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:826)

DisplayDatabase.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.SearchView;

public class DisplayProduct extends AppCompatActivity {

ListView listView;
ProductAdapter adapter;

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

    listView = findViewById(R.id.Display_listview);

    BackgroundTask backgroundTask = new BackgroundTask(this);
    backgroundTask.execute("get_info");

   //-----------------------------EDIT--------------------------------
   adapter = new ProductAdapter(this, R.layout.display_product_row);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);

    MenuItem myActionMenuItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) myActionMenuItem.getActionView();
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String s) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String s) {

            if(TextUtils.isEmpty(s)){
                adapter.filter("");
                listView.clearTextFilter();
            }
            else{
                adapter.filter(s);
            }

            return true;
        }
    });

    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if(id == R.id.action_settings){
        //do your functionality here
        return true;
    }

    return super.onOptionsItemSelected(item);
}
}

ProductAdapter.java

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class ProductAdapter extends ArrayAdapter {

List list = new ArrayList();

//Menu Search
Context context;
ArrayList<Product> productArrayList = new ArrayList<>();


public ProductAdapter(@NonNull Context context, int resource) {
    super(context, resource);
    this.context = context;
    this.productArrayList.addAll(list);
}

public void addList(ArrayList<Product> Oldarraylist){
    this.productArrayList = Oldarraylist;
}

public void add(Product object) {
    list.add(object);
    super.add(object);
}

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

@Nullable
@Override
public Object getItem(int position) {
    return list.get(position);
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    //Referencing each row as a View, which is an object
    View row = convertView;
    ProductHolder productHolder;

    if(row == null){
        //There is no view at this position, so we are creating a new one
        //In this case we are inflating an XML layout

        LayoutInflater layoutInflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        row = layoutInflater.inflate(R.layout.display_product_row, parent, false);

        //Locate the views in row.xml
        productHolder = new ProductHolder();
        productHolder.id = row.findViewById(R.id.ID);
        productHolder.name = row.findViewById(R.id.NAME);
        productHolder.price = row.findViewById(R.id.PRICE);
        productHolder.quantity = row.findViewById(R.id.QUANTITY);

        /*setTag is very useful for custom ArrayAdapter using. It is some kind of optimization.
         There setTag used as reference to object that references on some parts of layout
         (that displaying in ListView) instead of findViewById.
         */

        row.setTag(productHolder);
    }

    else{
        //We are recycling a view that already exists
        productHolder = (ProductHolder) row.getTag();
    }

    //Once we have a reference to the View we are returning, we set its values
    //Setting results into textviews

    Product product = (Product) getItem(position);
    productHolder.id.setText(product.getID().toString());
    productHolder.name.setText(product.getName().toString());
    productHolder.price.setText(Integer.toString(product.getPrice()));
    productHolder.quantity.setText(Integer.toString(product.getQuantity()));

    //Menu
    //Listview item clicks

    row.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });

    return row;
}

//Menu
public void filter(String charText){
    charText = charText.toLowerCase(Locale.getDefault());
    list.clear();

    if(charText.length() == 0){
        list.addAll(productArrayList);
    }
    else{
        for (Product product : productArrayList){
            if(product.getName().toLowerCase(Locale.getDefault()).contains(charText)){
                list.add(product);
            }
        }
    }
    notifyDataSetChanged();
}

static class ProductHolder{

    TextView id, name, price, quantity;

}
}

AsyncTask线程中的相关代码

public class BackgroundTask extends AsyncTask<String,Product,String> {

Context context;
ProductAdapter productAdapter;
Activity activity;
ListView listView;

ArrayList<Product> arrayList = new ArrayList<>();

@Override
protected String doInBackground(String... params) {
    listView = activity.findViewById(R.id.Display_listview);
        SQLiteDatabase db = dbOperations.getReadableDatabase();
        Cursor cursor = dbOperations.getInformation(db);

        productAdapter = new ProductAdapter(context, 
  R.layout.display_product_row);

        String id, name;
        int price, quantity;

        while(cursor.moveToNext()){
            id = 
  cursor.getString(cursor.getColumnIndex(ProductContract.ProductEntry.ID));
            name = 
 cursor.getString(cursor.getColumnIndex(ProductContract.ProductEntry.NAME));
            price = 
 cursor.getInt(cursor.getColumnIndex(ProductContract.ProductEntry.PRICE));
            quantity = 
 cursor.getInt(cursor.getColumnIndex(ProductContract.ProductEntry.QUANTITY));

            Product product = new Product(id, name, price, quantity);

            publishProgress(product);
        }

        return "get_info";
}

@Override
protected void onPostExecute(String s) {
   //Bind the array adapter to the listview
   listView.setAdapter(productAdapter);
   productAdapter.addList(arrayList);

    @Override
protected void onProgressUpdate(Product... values) {
    productAdapter.add(values[0]);
    arrayList.add(values[0]);
}
提问于
用户回答回答于

必须已经初始化了ProductAdapter,只是添加ProductAdapter adapter = new ProductAdapter()在AsyncTask中,是不能工作的,因为它引用的对象与您以前创建的对象不同。

用户回答回答于

在调用Product.filter()方法之前未初始化ProductAdapter adapter;,当在onQueryTextChange中调用filter()时,适配器为NULL,因此出现了错误。如果ProductAdapter类声明为静态的,那么现在拥有的代码可能会正常工作,因为它不是必须在调用类中的方法之前创建类的新实例。

扫码关注云+社区

领取腾讯云代金券