androidandroid-recyclerviewandroid-filterable

filtered recycler view not getting correct card position on swipe


So, I have a RecyclerView that uses "swipe to dismiss" and implements filterable.

What works: The swipe to dismiss and filterable work individually as expected. I can swipe to dismiss with the proper animation (I also delete a row from my SQLite db upon this action). I can also filter the cards using the native filterable.

What doesn't work: When I swipe to dismiss after filtering, the position is incorrect and as expected then, the corresponding incorrect row is deleted from my db.

What I think: I believe I need to somehow update my Activity arrayList after/during the filter process.

my search code in Activity

//create options menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_jar_options, menu);
    /*return super.onCreateOptionsMenu(menu);*/

    action_search = menu.findItem(R.id.menu_jar_action_search);
    SearchView searchView = (SearchView) action_search.getActionView();
    searchView.setBackgroundColor(0xffffffff);
    search(searchView);
    return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    return super.onOptionsItemSelected(item);
}

//search
private void search(SearchView searchView) {
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }
        @Override
        public boolean onQueryTextChange(String newText) {
            adapter.getFilter().filter(newText.toLowerCase());
            return true;
        }
    });
}

my swipe code in Activity (cleaned up for clarity purposes)

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction){
            if (direction ==ItemTouchHelper.RIGHT){
                //swipe right - delete card
                final int position = viewHolder.getAdapterPosition();
                //get necessary data from item
                s_deleteIdentifier = arrayList.get(position).getIdentifier();
                s_deleteIdentifier = (!FormatHelper.isStringEmpty(s_deleteIdentifier)) ? s_deleteIdentifier : "";
                s_deleteName = arrayList.get(position).getName();
                s_deleteName = (!FormatHelper.isStringEmpty(s_deleteName)) ? s_deleteName : "";

                //REMOVE ME after testing
                Toast.makeText(JarActivity.this, "" +position +" " +s_deleteName, Toast.LENGTH_SHORT).show();

                adapter.notifyItemRemoved(position);
                arrayList.remove(position);

                //user confirms delete, so delete row from db
                DatabaseHelper.getInstance(JarActivity.this).deleteRow(
                                    "table",
                                    "IDENTIFIER = ?",
                                    new String[] {s_deleteIdentifier}
                );
        }

my filter code in adapter

@Override
public Filter getFilter(){
    return new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence charSequence){
            String charString;

            if (charSequence != null) charString = charSequence.toString();
            else charString = "";

            if (charString.isEmpty()){
                arrayListFiltered = arrayList;
            }else{
                ArrayList<JarObject> filteredList = new ArrayList<>();
                for (JarObject jarObject : arrayList) {
                    if (jarObject.getName().toLowerCase().contains(charString) ||
                            jarObject.getLocation().toLowerCase().contains(charString) ||
                            jarObject.getUserNote().toLowerCase().contains(charString) ||
                            jarObject.getSummary().toLowerCase().contains(charString)
                            )
                        filteredList.add(jarObject);
                }
                arrayListFiltered = filteredList;
            }

            FilterResults filterResults = new FilterResults();
            filterResults.values = arrayListFiltered;
            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
            arrayListFiltered = (ArrayList<JarObject>) filterResults.values;
            notifyDataSetChanged();
        }
    };
}

Solution

  • The items in your original list and the filtered list won't be in the same positions. I don't have the ability to throw together an example right now but you should be able to override the adapters getItemId() method to return something meaningful and use the ViewHolder.getItemId() to get the actual item you want to delete instead of depending on the position.

    You could also expose your filtered list so you can access it from your activity to get the item from the filtered list based off the position, then search your original list to get the proper position.