I am trying to search a RecylerView with EditText, i follow some tutorial and i get Error NullPointerException. my RecyclerView is working good , but my search program is not working.
this is my Adapter Code
public class ListDefoodAdapter extends RecyclerView.Adapter<ListDefoodAdapter.CategoryViewHolder> implements Filterable {
private Context context;
private ArrayList<DefoodListData> listDefood;
private ArrayList<DefoodListData> listFilter;
public ListDefoodAdapter(Context context) {
this.context = context;
}
public ArrayList<DefoodListData> getListDefood() {
return listDefood;
}
public void setListDefood(ArrayList<DefoodListData> listDefood) {
this.listDefood = listDefood;
}
#NonNull
#Override
public CategoryViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemRow = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_transaksidefood, viewGroup, false);
return new CategoryViewHolder(itemRow);
}
public Filter getFilter(){
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String charString = constraint.toString();
if (charString.isEmpty()){
listDefood = listFilter;
} else {
ArrayList<DefoodListData> filteredList = new ArrayList<>();
for (DefoodListData row : listFilter){
String id = String.valueOf(row.getId());
if (id.toLowerCase().contains(charString.toLowerCase()) ||
row.getNamaUser().toLowerCase().contains(charString.toLowerCase()) ||
row.getNamaDriver().toLowerCase().contains(charString.toLowerCase())){
filteredList.add(row);
}
}
listDefood = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = listDefood;
return filterResults;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
listDefood = (ArrayList<DefoodListData>) results.values;
notifyDataSetChanged();
}
};
}
#Override
public int getItemCount() {
return getListDefood().size();
}
}
and this is my code in fragment
private void searchData(){
btn_search.setOnClickListener(v -> {
String keySearch = edt_cari.getText().toString();
listDefoodAdapter.getFilter().filter(keySearch);
});
}
but i get error null on method getItemCount(). Please help me to solve this problem
there is an issue in your data assign in listFilter.
you are adding data in listDefood in below method
public void setListDefood(ArrayList<DefoodListData> listDefood) {
this.listDefood = listDefood;
}
but you are not adding a list in listFilter.
You need to add list in Both array.
public void setListDefood(ArrayList<DefoodListData> listDefood) {
this.listDefood = listDefood;
this.listFilter = listDefood;
}
Assign filtered list size to filter result.
try with this
public void setListDefood(ArrayList<DefoodListData> listDefood) {
this.listDefood = listDefood;
this.listFilter = listDefood;
}
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
listDefood = (List<DefoodListData >) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String charString = constraint.toString().toLowerCase();
FilterResults results = new FilterResults();
ArrayList<DefoodListData> filteredList = new ArrayList<>();
for (DefoodListData row : listFilter) {
if (String.valueOf(row.getId().toLowerCase().trim().contains(charString)) ||
row.getNamaUser().toLowerCase().trim().contains(charString) ||
row.getNamaDriver().toLowerCase().trim().contains(charString)) {
filteredList.add(row);
}
}
results.count = filteredList.size();
results.values = filteredList;
return results;
}
};
}
Related
I have a RecyclerView. I want to make it filterable with SearchView. My application is based on Android Architecture Components.
I created a filter method for searchView .When i search something it's work but when i clear text from searchView ,list do not change and still shows search data.
Activity:
private StudentAdapter mAdapter;
mAdapter = new StudentAdapter(this);
recyclerView.setAdapter(mAdapter);
//ViewModel
StudentViewModelFactory factory = new StudentViewModelFactory(mDb, mClassId);
StudentViewModel viewModel = ViewModelProviders.of(this, factory).get(StudentViewModel.class);
viewModel.getStudentEntries().observe(this, studentEntries -> mAdapter.setStudents(studentEntries));
//SearchView
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener(){
#Override
public boolean onQueryTextSubmit(String query){
return false;
}
#Override
public boolean onQueryTextChange(String newText){
mAdapter.getFilter().filter(newText);
return false;
}
});
Adapter:
public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.itemHolder> {
private List<StudentEntry> mStudentEntries;
private List<StudentEntry> filteredStudentEntries;
public StudentAdapter(Context context) {
this.context = context;
}
public void setStudents(List<StudentEntry> studentEntries) {
this.mStudentEntries = studentEntries;
notifyDataSetChanged();
}
public void setPerformances(List<PerformanceEntry> performanceEntries) {
this.mPerformanceEntries = performanceEntries;
notifyDataSetChanged();
}
.
.
.
public Filter getFilter() {
return new Filter() {
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mStudentEntries = (List<StudentEntry>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
filteredStudentEntries = null;//avaz shod vali test nashod
if (constraint.length() == 0) {
filteredStudentEntries = mStudentEntries;
} else {
filteredStudentEntries = getFilteredResults(constraint.toString().toLowerCase());
}
FilterResults results = new FilterResults();
results.values = filteredStudentEntries;
return results;
}
};
}
protected List<StudentEntry> getFilteredResults(String constraint) {
List<StudentEntry> results = new ArrayList<>();
for (StudentEntry item : mStudentEntries) {
if (item.getStudentName().toLowerCase().contains(constraint)
|| item.getStudentId().toLowerCase().contains(constraint)) {
results.add(item);
}
}
return results;
}
}
Change :
private List<StudentEntry> filteredStudentEntries;
To :
private ArrayList<StudentEntry> arrayList = new ArrayList<>();
And Change :
public void setStudents(List<StudentEntry> studentEntries) {
this.mStudentEntries = studentEntries;
notifyDataSetChanged();
}
to :
public void setStudents(List<StudentEntry> studentEntries) {
this.mStudentEntries = studentEntries;
arrayList.addAll(mStudentEntries);
notifyDataSetChanged();
}
And change filter method :
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
constraint = constraint.toString().toLowerCase().trim();
mStudentEntries.clear();
if (constraint.length() == 0) {
mStudentEntries.addAll(arrayList);
} else {
for (StudentEntry item : arrayList) {
if (item.getStudentName().toLowerCase(Locale.getDefault()).contains(constraint)
|| item.getStudentId().toLowerCase(Locale.getDefault()).contains(constraint)) {
mStudentEntries.add(item);
}
}
}
FilterResults results = new FilterResults();
results.values = mStudentEntries;
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyDataSetChanged();
}
};
}
And don't forget to implements Filterable in adapter class.
Refer the below answer it will help,
Search text will pass to recyclerView by below code.
#Override
public boolean onQueryTextChange(String newText) {
adapter.getFilter().filter(newText);
return true;
}
Then RecyclerViewAdapter class should implements Filterable
public class DashboardRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {
//constructor loads data to dataList
DashboardRecyclerViewAdapter(ArrayList<Object> data,Context context)
{
this.context = context;
this.dataList = data;
this.filteredDataList = new ArrayList<>(data.size());
this.filteredDataList.addAll(dataList);
}
//override this method, it gets the SerchText and then compares with dataList
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence charSequence) {
ArrayList<Object> filteredDataList_temp = new ArrayList<>();
String charString = charSequence.toString().toLowerCase();
if (charString.isEmpty() || charString.trim().length() == 0 ) {
filteredDataList_temp = dataList;
} else {
for (Object row : dataList) {
FoodDetail r = (FoodDetail) row;
if (foodName.contains(charString)) {
filteredDataList_temp.add(row);
}
}
}
FilterResults filterResults = new FilterResults();
filterResults.values = filteredDataList_temp;
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (filteredDataList != null) {
filteredDataList.clear();
filteredDataList.addAll((ArrayList<Object>) results.values);
notifyDataSetChanged(); //once completed Then RecyclerView updated with only searched List
}
}
};
}
I got this problem from my AutoCompleteTextView when I select.
How can I solve this error ?
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes.
Code:
public class AutoSuggestAdapter extends ArrayAdapter<String> implements Filterable {
List<String> shippers;
public AutoSuggestAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
shippers = new ArrayList<String>();
}
public void setData(List<String> stringList) {
}
#Override
public int getCount() {
return shippers.size();
}
#Override
public String getItem(int index) {
return shippers.get(index);
}
#Override
public Filter getFilter() {
Filter myFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
Logger.d("Start load address");
new QueryAddressRepository(getContext()).getAdressList(constraint.toString(), address_list -> {
shippers = address_list;
Logger.d("Done");
});
// Now assign the values and count to the FilterResults object
filterResults.values = shippers;
filterResults.count = shippers.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence contraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return myFilter;
}
}
I want to make a searchView like this image below. The searchView is received data from local database then it filters data like AutoCompleteTextView.
How can i make it?
Here is image:
enter image description here
I use RecyclerView for list display item, and in Adapter I implements a Filterable:
private List<Bank> banksList;
private List<Bank> bankListFiltered;
#Override
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
String charString = constraint.toString();
if (charString.isEmpty()) {
bankListFiltered = banksList;
} else {
List<Bank> filteredList = new ArrayList<>();
for (Bank row : banksList) {
String shortName = row.getShortName().toLowerCase();
if (shortName.contains(charString)) {
filteredList.add(row);
}
}
bankListFiltered = filteredList;
}
FilterResults filterResults = new FilterResults();
filterResults.values = bankListFiltered;
return filterResults;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
bankListFiltered = (ArrayList<Bank>) results.values;
notifyDataSetChanged();
}
};
}
#Override
public int getItemCount() {
return bankListFiltered.size();
}
public BanksAdapter(List<Bank> banksList) {
this.banksList = banksList;
this.bankListFiltered = banksList;
}
And in my activity, add event for SearchView:
new SearchView(this).setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
banksAdapter.getFilter().filter(newText);
return true;
}
});
i want set filter (search) in list view and use "extends BaseAdapter implements Filterable" for adapter class .
this my code :
public class UserList extends BaseAdapter implements Filterable {
private Context context;
private List<Users> user;
public UserList(Context context, List<Users> user) {
this.context = context;
this.user = user;
}
#Override
public int getCount() {
return user.size();
}
#Override
public Object getItem(int position) {
return user.get(position);
}
#Override
public long getItemId(int position) {
return user.get(position).getId();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(context, R.layout.listview_item, null);
ImageView imgUser = (ImageView) view.findViewById(R.id.list_image);
TextView txtName = (TextView) view.findViewById(R.id.list_item_title);
TextView txtDesc = (TextView) view.findViewById(R.id.list_item_short);
TextView txtSex = (TextView) view.findViewById(R.id.list_item_sex);
TextView txtAge = (TextView) view.findViewById(R.id.list_item_age);
//get byte[] from db , resize and convert to bitmap
Bitmap bmp = BitmapFactory.decodeByteArray(user.get(position).getPic(), 0, user.get(position).getPic().length);
imgUser.setImageBitmap(getResizedBitmap(bmp, 300, 150));
txtName.setText(user.get(position).getUserName());
txtDesc.setText(user.get(position).getUserDesc());
txtSex.setText(user.get(position).getGender());
txtAge.setText(user.get(position).getAge() + " ساله ");
return view;
}
and this my filter method :
private Filter filterResult = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
ArrayList<Users> tempList = new ArrayList<>();
if (constraint != null && user != null) {
int length = user.size();
int i = 0;
while (i < length) {
Users item = user.get(i);
tempList.add(item);
i++;
}
filterResults.values = tempList;
filterResults.count = tempList.size();
}
return filterResults;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
user = (ArrayList<Users>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
#Override
public Filter getFilter() {
return filterResult;
}
but no happen when enter text for filter list view ... see this image :
There a few mistake in your code.
You must keep original array which did not have filter.
You should change your code like this
private List<Users> original;
private List<Users> user;
public UserList(Context context, List<Users> user) {
this.context = context;
this.user = user;
this.original = user;
}
You did not perform filter in your code. You need to correct your code
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
ArrayList<Users> tempList = new ArrayList<>();
if (original != null) {
if (TextUtil.isEmpty(constraint)){
tempList = original;
} else {
int length = user.size();
int i = 0;
while (i < length) {
User item = original.get(i);
// check item again your constraint see example below
if (item.name.contain(constraint))
tempList.add(item);
i++;
}
}
}
filterResults.values = tempList;
filterResults.count = tempList.size();
return filterResults;
}
in this case your dataset reference is changing on filter so adapter can not getting reference to your data as per my solution i will do follow
declare temp list in adapter which contain all data
private List<Users> tempuser;
now fill this with looping to avoid reference problem and call it in adapter constructor
private void fillData()
{
// initialize tempuserhere
tempuser=new List<Users>() ; //check as per your model
for(Users obj:user)
{
tempuser.add(obj);
}
}
in constructor
public UserList(Context context, List<Users> user) {
this.context = context;
this.user = user;
fillData();
}
now in filter process class
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null && tempuser != null) {
boolean fillAll=false;
user.clear();
if(constraint.toString().trim().length==0)
fillAll=true;
for(Users objUser:tempuser)
{
if(fillAll || constraint.toString().trim().toLowerCase().contains(objUser.value.toLowerCase()))///the objUser value replaced with your compare string
{
user.add(objUser);
}
}
filterResults.values = user;
filterResults.count = user.size();
}
return filterResults;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
user = (ArrayList<Users>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
you can try this if any problem let me know, also
if any user know the standarad approach let me know
I am using a autocompletetextview with custom adapter in android. it works fine. But when i click backspace to clear selected item from autocomplete textview it freezes, or there is a delay in deleting. How can i overcome this ? My filter codes are given below
#Override
protected FilterResults performFiltering(CharSequence constraint) {
if (constraint != null) {
suggestions.clear();
FilterResults filterResults = new FilterResults();
for (Names people : tempItems) {
if(people.getName().toLowerCase().contains(constraint.toString()
.toLowerCase())) {
suggestions.add(people);
}
}
// FilterResults filterResults = new FilterResults();
filterResults.values = suggestions;
filterResults.count = suggestions.size();
return filterResults;
} else {
return new FilterResults();
}
}
#Override
protected void publishResults(CharSequence constraint, FilterResults
results) {
List<Names> filterList = (ArrayList<Names>) results.values;
if (results != null && results.count > 0) {
clear();
for (Names people : filterList) {
add(people);
notifyDataSetChanged();
}
}
}
};
What am i doing wrong ? Thanks in Advance.
Modify you filter according to the below code. You are notifying the adapter inside the loop, it should be notified once the whole list is added into the other list i.e. outside the loop.
// This is my whole adapter and works like a charm and about the updation on the UI thread, yes I am notifying the adapter on the UI thread.
public class SuburbSuggestionsAdapter extends ArrayAdapter<String> implements Filterable {
protected static final String TAG = "SuggestionAdapter";
private List<String> suggestions;
private Context context;
private List<String> SuburbList = new ArrayList<>();
public SuburbSuggestionsAdapter(Context context) {
super(context, android.R.layout.simple_dropdown_item_1line);
suggestions = new ArrayList<String>();
this.context = context;
}
#Override
public int getCount() {
return (null != suggestions ? suggestions.size() : 0);
}
#Override
public String getItem(int index) {
return suggestions.get(index);
}
#Override
public Filter getFilter() {
Filter myFilter = new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if(!TextUtils.isEmpty(constraint)){
WebServiceHandler.hitFetchSuburbList(context, constraint.toString(), new CallbackRest() {
#Override
public void onDone(String response) {
if(SuburbList.size()!=0){
SuburbList.clear();
}
try {
JSONArray jsonArray = new JSONArray(response);
for(int i=0; i<jsonArray.length(); i++){
SuburbList.add(jsonArray.get(i).toString());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
// A class that queries a web API, parses the data and
// returns an ArrayList<GoEuroGetSet>
List<String> new_suggestions = SuburbList;
suggestions.clear();
for (int i=0;i<new_suggestions.size();i++) {
suggestions.add(new_suggestions.get(i));
}
// Now assign the values and count to the FilterResults
filterResults.values = suggestions;
filterResults.count = suggestions.size();
}
return filterResults;
}
#Override
protected void publishResults(CharSequence contraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return myFilter;
}
}