Search code examples
javaandroidgridviewfilteronitemclicklistener

Filtering products in GridView, OnItemClick passes different product details


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: Strange beahaviour 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();
        }
    });

Solution

  • 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
        }
    });