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