I have created a SQL database using an AsyncTask that takes in inputs in one activity and displays them in another activity. Now I am trying to learn how to search the database in listview using a searchview.
I have an adapter class that binds to the listview. In it there is a filter function, to filter the searchview, which is where I believe the error originates from; since the error occurs at adapter.filter(s);.
My Error:
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;
}
}
Relevant Code in my AsyncTask thread
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]);
}