I'm using GridView
to display products. Im my custom adapter class I implemented filter search which allows me to give wanted results. After adding OnItemClick
I realised that the clicked item gives me wrong details when in filtering mode.
To my mind it has something to do with ViewHolder
which is implemented in customadapter
and sends user to wrong item activity. Why? Let's have a look at this picture:
It represents two situation
1)when activity is launched and gridview
displays all entries by default
2)when filter search was used and shows the results
Here is some part of my custom adapter class:
@Override
public View getView(int position, View view, ViewGroup parent) {
Log.d(TAG, "getView: assigning all values to custom adapter");
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
ViewHolder viewHolder;
Product product= productList.get(position);
//Creating new fields
if (view == null) {
view = layoutInflater.inflate(R.layout.single_product_layout, parent, false);
viewHolder = new ViewHolder();
viewHolder.productName = view.findViewById(R.id.name);
viewHolder.productPrice = view.findViewById(R.id.price);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
//Set up textviews and image
viewHolder.productName.setText(product.getName());
viewHolder.productPrice.setText(product.getPrice());
return view;
}
static class ViewHolder {
TextView productName;
TextView productPrice;
}
And here is code for filter search, I keep it in the same main class (CustomAdapter):
@Override
public Filter getFilter() {
if (customFilter == null) {
customFilter = new CustomFilter();
}
return customFilter;
}
class CustomFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
FilterResults filterResults = new FilterResults();
if (charSequence != null && charSequence.length() > 0) {
charSequence = charSequence.toString().toUpperCase();
List<Product> filters = new ArrayList<>();
for (int i = 0; i < tempList.size(); i++) {
if (tempList.get(i).getName().toUpperCase().contains(charSequence)) {
Product product = new Product( tempList.get(i).getName(), tempList.get(i).getPrice());
filters.add(product);
}
}
filterResults.count = filters.size();
filterResults.values = filters;
} else {
filterResults.count = tempList.size();
filterResults.values = tempList;
}
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
productList = (List<Product>) filterResults.values;
notifyDataSetChanged();
}
}
OnItemClick
code:
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Product product = allProducts.get(i);
String name = product.getName();
String price = product.getPrice();
Intent intent = new Intent(context, ProductDetails.class);
intent.putExtra("name", name);
intent.putExtra("price", price);
startActivity(intent);
}
});
I want to get rid of misleading OnItemClick
beahviour which launches details activity with wrong details.
Implementation of custom made search bar:
final EditText searchBar = findViewById(R.id.search_product);
searchBar.addTextChangedListener(this);
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
Log.d(TAG, "onTextChanged: starting...");
this.productAdapter.getFilter().filter(charSequence);
}
@Override
public void afterTextChanged(Editable editable) {
}
Clear button code:
//Clear search bar
ImageView cancelClearSearch = findViewById(R.id.cancel_clear_search);
cancelClearSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
searchBar.setText("");
searchBar.clearFocus();
//Hide keyboard
InputMethodManager inputManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(searchBar.getWindowToken(), 0);
new DownloadProducts().execute();
}
});
When you do this
productList = (List<Product>) filterResults.values;
the list in adapter and in activity starts pointing to two separate lists in memory but the filtered data is in the list used by adapter so instead of assigning new list to productList
use
productList.clear();
productList.addAll((List<Product>) filterResults.values);
or the alternative option is to retrieve the data from adapterView
instance as
//Product product = allProducts.get(i);
Product product = (Product)adapterView.getAdapter().getItem(i);
// get associated adapter with list view ^^^^^^^^^
// get the item from adapter, using position ^^^^^^^^^^^
To display the old list after clearing the search edittext
, just apply the filter with empty string as
//Clear search bar
ImageView cancelClearSearch = findViewById(R.id.cancel_clear_search);
cancelClearSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
this.productAdapter.getFilter().filter("");
searchBar.setText("");
searchBar.clearFocus();
//Hide keyboard
InputMethodManager inputManager = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(searchBar.getWindowToken(), 0);
//new DownloadProducts().execute(); no need to download data again
}
});