In my android app, I encounter a problem during the implementation of the message "no result found" for my search bar. When I type a character that already should produce no result the message doesn't appear. To make it appears I should type another character But if now I delete this second character it shows the no result message (as it should have shown from the beginning). Furthermore, if I delete this character, making the search text empty, instead to show the full list it shows nothing. Making some debugging it seems that the getItemCount used in the onQueryText produce a size that corresponds to the list before the filtering operation (it is like it is always a step behind), but I don't understand why.
This is my code:
Custom Adapter:
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> implements Filterable {
private final Activity destinationActivity;
private ArrayList<HashMap<String, String>> items;
private ArrayList<HashMap<String, String>> itemsFiltered = new ArrayList<>();
private Fragment activity;
private Context context;
private static final String KEY_TITOLO = "titolo";
private static final String KEY_FOTO = "foto";
private static final String KEY_DESCRIZIONE = "descrizione";
private static final String KEY_INDIRIZZO = "indirizzo";
private static final String KEY_MAPS = "maps";
private static final String KEY_LAT = "latitudine";
private static final String KEY_LONG = "longitudine";
private static final String KEY_LAT_START = "latitudineinizio";
private static final String KEY_LONG_START = "longitudineinizio";
private static final String KEY_LAT_END = "latitudinefine";
private static final String KEY_LONG_END = "longitudinefine";
// Provide a suitable constructor (depends on the kind of dataset)
public CustomAdapter(Context context,ArrayList<HashMap<String, String>> items, Fragment fragment, Activity destinationActivity) {
this.items = items;
this.activity = fragment;
this.destinationActivity = destinationActivity;
this.context = context;
this.itemsFiltered = items;
//inflater = (LayoutInflater) this.activity.get getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
// set the view's size, margins, paddings and layout parameters
return new MyViewHolder(v);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
HashMap<String, String> mapPosition = itemsFiltered.get(position);
holder.title.setText(mapPosition.get(KEY_TITOLO));
Resources res = Objects.requireNonNull(activity.getActivity()).getResources();
int resID = res.getIdentifier(mapPosition.get(KEY_FOTO), "drawable","myPackage");
Drawable drawable = res.getDrawable(resID);
holder.imageInList.setImageDrawable(drawable);
holder.picture.setText(mapPosition.get(KEY_FOTO));
holder.description.setText(mapPosition.get(KEY_DESCRIZIONE));
holder.address.setText(mapPosition.get(KEY_INDIRIZZO));
holder.maps.setText(mapPosition.get(KEY_MAPS));
holder.latitude.setText(mapPosition.get(KEY_LAT));
holder.longitude.setText(mapPosition.get(KEY_LONG));
holder.latitudeStartingPoint.setText(mapPosition.get(KEY_LAT_START));
holder.longitudeStartingPoint.setText(mapPosition.get(KEY_LONG_START));
holder.latitudeEndingPoint.setText(mapPosition.get(KEY_LAT_END));
holder.longitudeEndingPoint.setText(mapPosition.get(KEY_LONG_END));
}
@Override
public int getItemCount() {
return itemsFiltered != null ? itemsFiltered.size() : 0;
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
String charString = charSequence.toString();
ArrayList<HashMap<String, String>> filteredList = new ArrayList<>();
if (charString.isEmpty()) {
itemsFiltered = items;
} else {
for (HashMap<String, String> row : items) {
// name match condition. this might differ depending on your requirement
// here we are looking for name or phone number match
if (row.get("titolo").toLowerCase().contains(charString.toLowerCase())) {
filteredList.add(row);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
results.count = filteredList.size();
return results;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults results) {
itemsFiltered = (ArrayList<HashMap<String, String>>) results.values;
notifyDataSetChanged();
}
};
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// each data item is just a string in this case
private TextView title;
private ImageView imageInList;
private TextView picture;
private TextView description;
private TextView address;
private TextView maps;
private TextView latitude;
private TextView longitude;
private TextView latitudeStartingPoint;
private TextView longitudeStartingPoint;
private TextView latitudeEndingPoint;
private TextView longitudeEndingPoint;
MyViewHolder(View v) {
super(v);
title = v.findViewById(R.id.title);
imageInList = v.findViewById((R.id.imageInList));
picture = v.findViewById((R.id.picture));
description = v.findViewById((R.id.description));
address = v.findViewById((R.id.address));
maps = v.findViewById((R.id.maps));
latitude = v.findViewById((R.id.latitude));
longitude = v.findViewById((R.id.longitude));
latitudeStartingPoint = v.findViewById((R.id.latitude_starting_point));
longitudeStartingPoint = v.findViewById((R.id.longitude_starting_point));
latitudeEndingPoint = v.findViewById((R.id.latitude_ending_point));
longitudeEndingPoint = v.findViewById((R.id.longitude_ending_point));
v.setOnClickListener(this);
}
@Override
public void onClick(View view) {
String title = ((TextView) view.findViewById(R.id.title)).getText().toString();
String picture = ((TextView) view.findViewById(R.id.picture)).getText().toString();
String description = ((TextView) view.findViewById(R.id.description)).getText().toString();
String address = ((TextView) view.findViewById(R.id.address)).getText().toString();
String maps = ((TextView) view.findViewById(R.id.maps)).getText().toString();
String latitude = ((TextView) view.findViewById(R.id.latitude)).getText().toString();
String longitude = ((TextView) view.findViewById(R.id.longitude)).getText().toString();
String latitudeStartingPoint = ((TextView) view.findViewById(R.id.latitude_starting_point)).getText().toString();
String longitudeStartingPoint = ((TextView) view.findViewById(R.id.longitude_starting_point)).getText().toString();
String latitudeEndingPoint = ((TextView) view.findViewById(R.id.latitude_ending_point)).getText().toString();
String longitudeEndingPoint = ((TextView) view.findViewById(R.id.longitude_ending_point)).getText().toString();
Intent in = new Intent(context,destinationActivity.getClass());
in.putExtra(KEY_TITOLO, title);
in.putExtra(KEY_FOTO, picture);
in.putExtra(KEY_DESCRIZIONE, description);
in.putExtra(KEY_INDIRIZZO, address);
in.putExtra(KEY_MAPS, maps);
in.putExtra(KEY_LAT, latitude);
in.putExtra(KEY_LONG, longitude);
in.putExtra(KEY_LAT_START, latitudeStartingPoint);
in.putExtra(KEY_LONG_START, longitudeStartingPoint);
in.putExtra(KEY_LAT_END, latitudeEndingPoint);
in.putExtra(KEY_LONG_END, longitudeEndingPoint);
activity.startActivity(in);
}
}
}
Class where I am using the search bar:
public class TouristicPointFragment extends Fragment {
private SearchView searchView;
private CustomAdapter mAdapter;
private RecyclerView recyclerView;
private TextView textView;
private SearchView getSearchView() {
return searchView;
}
private void setSearchView(SearchView searchView) {
this.searchView = searchView;
}
public TouristicPointFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.fragment_turisticpoint, container, false);
ArrayList<HashMap<String, String>> hashMapArrayList;
recyclerView = view.findViewById(R.id.my_recycler_view);
textView = view.findViewById(R.id.noResultsFoundView);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
recyclerView.setLayoutManager(layoutManager);
hashMapArrayList = XMLParser.getData("turisticpoint",getContext());
Activity singlePlaceActivity = new SinglePlaceActivity();
mAdapter = new CustomAdapter(getContext(), hashMapArrayList, this, singlePlaceActivity);
recyclerView.setAdapter(mAdapter);
return view;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.search_menu, menu);
MenuItem item = menu.findItem(R.id.action_search);
// MenuItemCompat.setShowAsAction(item, //MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | //MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
// MenuItemCompat.setActionView(item, searchView);
// These lines are deprecated in API 26 use instead
//item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItem.SHOW_AS_ACTION_IF_ROOM);
setSearchView(new SearchView(Objects.requireNonNull(((MainActivity) Objects.requireNonNull(getContext())).getSupportActionBar()).getThemedContext()));
item.setActionView(getSearchView());
getSearchView().setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
mAdapter.getFilter().filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String query) {
mAdapter.getFilter().filter(query);
if((mAdapter.getItemCount == 0)&&(!query.equals(""))){
recyclerView.setVisibility(View.GONE);
textView.setVisibility(View.VISIBLE);
textView.setText(String.format("%s '%s'", getResources().getString(R.string.no_result), query));
}else{
recyclerView.setVisibility(View.VISIBLE);
textView.setVisibility(View.GONE);
}
return false;
}
});
getSearchView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
}
);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_search) {
return true;
}
return super.onOptionsItemSelected(item);
}
// close search view on back button pressed
boolean collapseSearch() {
boolean searchViewOpen = false;
SearchView searchView = getSearchView();
if(searchView!=null){
String searchViewContent = searchView.getQuery().toString();
if (!searchViewContent.equals("")||(!searchView.isIconified())) {
searchView.setQuery("", false);
//Collapse the action view
searchView.onActionViewCollapsed();
//Collapse the search widget
//searchView.collapseActionView();
searchView.setIconified(true);
searchViewOpen = true;
}
}
return searchViewOpen;
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView
android:id="@+id/noResultsFoundView"
android:textSize="16dp"
android:layout_marginTop="30px"
android:textAlignment="center"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/no_result"
android:visibility="gone"
android:gravity="center_horizontal" />
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_recycler_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Edited version:
private ArrayList<HashMap<String, String>> mOriginalList;
private ArrayList<HashMap<String, String>> mFilteredList;
public CustomAdapter(Context context,ArrayList<HashMap<String, String>> items, Fragment fragment, Activity destinationActivity) {
this.activity = fragment;
this.destinationActivity = destinationActivity;
this.context = context;
this.mFilteredList = items;
//inflater = (LayoutInflater) this.activity.get getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getItemCount() {
return mFilteredList != null ? mFilteredList.size() : 0;
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
String constraint = charSequence.toString().toLowerCase();
FilterResults result = new FilterResults();
if (mOriginalList == null) {
//mOriginalList = mFilteredList;
mOriginalList = new ArrayList<>(mFilteredList);
}
if (constraint.isEmpty()) {
result.values = mOriginalList;
result.count = mOriginalList.size();
} else {
ArrayList < HashMap < String, String >> list = new ArrayList<>();
for (HashMap < String, String > item: mOriginalList) {
String title = item.get("titolo");
boolean contains = false;
if (title != null) {
contains = title.toLowerCase().contains(constraint);
}
if (contains) {
list.add(item);
}
}
result.values = list;
result.count = list.size();
}
return result;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mFilteredList.clear();
mFilteredList.addAll((ArrayList <HashMap<String,String >>) results.values);
notifyDataSetChanged();
}
};
}