1

I have a program that I am writing to be able to view the files on the device in the app folder via a recyclerview, and delete them.

The mainactivity handles the file deleting and removal, and the adapter handles finding postition and visual changes in it's onclicks

I implemented a search filter so the user can find the files. My mainactivity actually gets the correct position from the filter and deletes the files when I delete them, as well as displays the correct path in the toast message when I click it. As well, the row is deleted from the view temporarily, until I change the search.

However the visual changes are not handled in the adapter correctly. The filename stays in the view after being deleted (though it disappears if I restart the program obviously, as the rowItem list is recreated from files on the device). As well the visual changes that are applied to the deleted item (ie expanding the menu by clicking the menu icon) appear on otherfilenames at the same index that the deleted file had in the search after I attempt to remove the filename from the list. I saw other similar questions but couldn't use them to solve my problem

I am confused as it seems I am updating my list when filtering

My RowAdapter:

public class RowAdapter extends RecyclerView.Adapter<RowAdapter.RowViewHolder> implements Filterable{

    private ArrayList<RowItem> rowList;
    public ArrayList<RowItem> rowListAll;
    private OnItemClickListener rowListener;

    public RowAdapter(ArrayList<RowItem> rowList){
        this.rowList = rowList;
        this.rowListAll = new ArrayList<>(rowList);
    }


    public interface OnItemClickListener{
        void onItemClick(int position);
        void onDeleteClick(int position);
        void onMenuClick(int position);
    }

    public void setOnItemClickListener(OnItemClickListener listener){
        rowListener = listener;
    }

    public static class RowViewHolder extends RecyclerView.ViewHolder{

        public ImageView rowImageView;
        public TextView rowTextView;
        public Button rowDeleteButton;
        public Button rowSendButton;
        public ImageView rowMenuIcon;

        public RowViewHolder(@NonNull View itemView, OnItemClickListener listener) {
            super(itemView);
            //find views in row XML
            rowImageView = itemView.findViewById(R.id.fileImage);
            rowTextView = itemView.findViewById(R.id.fileName);
            rowDeleteButton = itemView.findViewById(R.id.deleteFile);
            rowSendButton = itemView.findViewById(R.id.editButton);
            rowMenuIcon = itemView.findViewById(R.id.menuIcon);

            //handle adapters onclick behavior of the recyclerview. Using to handle visual changes in the XML as well
            itemView.setOnClickListener(v -> {
                if(listener != null){

                    int position = getAdapterPosition();
                    if(position != RecyclerView.NO_POSITION){
                        listener.onItemClick(position);

                        rowDeleteButton.setVisibility(View.INVISIBLE); rowSendButton.setVisibility(View.INVISIBLE); rowMenuIcon.setVisibility(View.VISIBLE);
                        rowTextView.setTextColor(Color.parseColor("#000000"));
                    }
                }
            });

            rowMenuIcon.setOnClickListener(v -> {
                if(listener != null){
                    int position = getAdapterPosition();
                    if(position != RecyclerView.NO_POSITION){
                        listener.onMenuClick(position);
                        rowDeleteButton.setVisibility(View.VISIBLE); rowSendButton.setVisibility(View.VISIBLE); rowMenuIcon.setVisibility(View.INVISIBLE);
                        rowTextView.setTextColor(Color.parseColor("#808e95"));
                    }
                }

            });
            rowDeleteButton.setOnClickListener(v -> {
                if(listener != null){
                    int position = getAdapterPosition();
                    if(position != RecyclerView.NO_POSITION){
                        listener.onDeleteClick(position);
                    }
                }
            });
        }
    }

    @NonNull
    @Override
    public RowViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row, parent, false);
        return new RowViewHolder(v, rowListener);
    }

    @Override
    public void onBindViewHolder(@NonNull RowViewHolder holder, int position) {
        String currentItem = rowList.get(position).getFileName();
        holder.rowTextView.setText(currentItem);
    }
    
    //return the # of items in the recyclerview
    @Override
    public int getItemCount() {
        return rowList.size();
    }

    /////////////SEARCH FILTER METHODS////////////////////
    @Override
    public Filter getFilter() {
        return filter;
    }

    Filter filter = new Filter() {
        //runs on background thread
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {

            List<RowItem> filtredList = new ArrayList<>();

            //add all to the filtred list if searchtext is empty
            if(constraint.toString().isEmpty()){
                filtredList.addAll(rowListAll);
            }
            //else check if filename in the row item matches the searchtext, if it does add the row item to the filtred list
            else{
                for (RowItem row: rowListAll){
                    if(row.getFileName().toLowerCase().contains(constraint.toString().toLowerCase())){
                       filtredList.add(row);
                    }
                }
            }

            //create a filterResults variable to hold the filtred results to the publishResults method
            FilterResults filterResults = new FilterResults();
            filterResults.values = filtredList;
            return filterResults;
        }

        //runs on UI thread
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            rowList.clear();
            rowList.addAll((Collection<? extends RowItem>) results.values);
            notifyDataSetChanged();
        }
    };
}

MainActivity (called FileView)

public class FileView extends AppCompatActivity {

    private RecyclerView fileRecyclerView;
    private RowAdapter fileAdapter;
    private RecyclerView.LayoutManager fileLayoutManager;
    private ArrayList<RowItem> rowItem;
    private File[] files;
    private File tempFile;
    private File selectedFileData;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_file_view);
        //Create a array of files in the app folder named PDF_files sorted in descending order
        File file = new File(getExternalFilesDir("PDF_files").toString());
        files = file.listFiles();
        Arrays.sort(files, Collections.reverseOrder());

        createRows();
        buildRecyclerView();
    }

    public void createRows(){
        rowItem = new ArrayList<>();
        for (int i = 0; i < files.length; i++) {
            tempFile = files[i];
            rowItem.add(new RowItem(tempFile.getName().replace("__", "\n").replace('_',' ').replace('-','/').replace(".pdf",""), tempFile));
        }
    }

    public void buildRecyclerView() {

        fileRecyclerView = findViewById(R.id.recyclerView);
        fileRecyclerView.setHasFixedSize(true);
        fileLayoutManager = new LinearLayoutManager(this);
        fileAdapter = new RowAdapter(rowItem);
        fileRecyclerView.setLayoutManager(fileLayoutManager);
        fileRecyclerView.setAdapter(fileAdapter);

        fileAdapter.setOnItemClickListener(new RowAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                selectedFileData = rowItem.get(position).getFileData();
                Toast.makeText(FileView.this, "Clicked: " + selectedFileData.getPath(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDeleteClick(int position) {

                selectedFileData = rowItem.get(position).getFileData();
                selectedFileData.delete();

                Toast.makeText(FileView.this,"Deleted: " + selectedFileData.getPath() , Toast.LENGTH_SHORT).show();

                removeItem(position);
            }

            @Override
            public void onMenuClick(int position) {

            }
        });
    }
    public void removeItem(int position) {
        rowItem.remove(position);
        fileAdapter.notifyItemRemoved(position);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        MenuItem item = menu.findItem(R.id.action_search);
        SearchView searchView = (SearchView) item.getActionView();
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                fileAdapter.getFilter().filter(newText);
                return false;
            }
        });
        return super.onCreateOptionsMenu(menu);
    }
}
MKer
  • 83
  • 2
  • 8

1 Answers1

0

So I implemented a fix but it seems a little messy to me to have to pass the value of the arraylist again instead of having it removed in the adapter onclick

changed my removeItem method in FileView/the mainactivity:

    public void removeItem(int position) {
        rowItem.remove(position);
        fileAdapter = new RowAdapter(rowItem);
    }

And also, I handle the visual changes in the Adapter differently by putting them in the onBindViewHolder

 @Override
    public void onBindViewHolder(@NonNull RowViewHolder holder, int position) {
        RowItem currentRow = rowList.get(position);
        String currentItem = rowList.get(position).getFileName();
        holder.rowTextView.setText(currentItem);

        //handle visual changes
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.rowDeleteButton.setVisibility(View.INVISIBLE); holder.rowSendButton.setVisibility(View.INVISIBLE); holder.rowMenuIcon.setVisibility(View.VISIBLE);
                holder.rowTextView.setTextColor(Color.parseColor("#000000"));
                Log.d("LOG", currentItem.toString());
            }
        });

        holder.rowMenuIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                holder.rowDeleteButton.setVisibility(View.VISIBLE); holder.rowSendButton.setVisibility(View.VISIBLE); holder.rowMenuIcon.setVisibility(View.INVISIBLE);
                holder.rowTextView.setTextColor(Color.parseColor("#808e95"));
            }
        });
    }

Would love to know if there is a better way

MKer
  • 83
  • 2
  • 8