Search in ListView populated with JSON data - android

My problem is the following: i want to implement a search mechanism into my JSON-data generated ListView, but i don't know what to do in my case.
I've found a lot of examples but i want to implement the simplest solution possible and, especially, an understandable one.
Here's my Pastebin with the Adapter and the MainActivity class.
http://pastebin.com/SSXHXK7m
Can you give me any suggestion? I'm stuck.

The search functionality would be something like this
public void searchList(String query){
List<Records> matchedActors = new ArrayList<Records>();
for(int i = 0; i < adapter.getCount(); i++){
if(adapter.getItem(i).getAuthor().equals(query)
matchedActors.add(adapter.getItem(i));
}
// See below for what to put here
}
You can then either modify the adapter by creating/calling a method in your adapter class that is something like
public void modifyList(List<Records> actorList){
this.actorList = actorList;
notifyDataSetChanged();
}
and call adapter.modifyList(matchedActors) after your search, or by instantiating a new instance of your adapter after your serach via,
adapter = new RecordsAdapter(MainActivity.this, R.layout."your_layout_resource", actorList);
listview.setAdapter(adapter);
EDIT
I didn't notice you were using a Filterable ArrayAdapter :P You can implement the following funcitionality in your adapter.
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#Override
protected void publishResults(CharSequence constraint,FilterResults results) {
actorList = (List<Records>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
List<Records> matchedActors = new ArrayList<Records>();
//NOTE mOriginalValues will be a class variable we use to keep track of our values
if (mOriginalValues == null) {
mOriginalValues = new ArrayList<Records>(actorList);
}
if (constraint == null || constraint.length() == 0) {
results.count = mOriginalValues.size();
results.values = mOriginalValues;
}
else {
constraint = constraint.toString().toLowerCase();
List<Records> matchedActors = new ArrayList<Records>();
for(int i = 0; i < getCount(); i++){
if(mOriginalValues.getAuthor().toLowerCase().startsWith(constraint)
matchedActors.add(getItem(i);
}
// set the Filtered result to return
results.count = matchedActors.size();
results.values = matchedActors;
}
return results;
}
return filter;
}

Related

How to Search in recyclerview with One Adapter in Android Studio?

I want to search in recyclerview.
I write the Filter method in recyclerview adapter like this:
public Filter getFilter() {
return new Filter() {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
final FilterResults oReturn = new FilterResults();
final ArrayList<Info_Filter> results = new ArrayList<Info_Filter>();
if (orig == null)
orig = items;
if (constraint != null) {
if (orig != null & orig.size() > 0) {
for (final Info_Filter g : orig) {
if (g.getName().toLowerCase().contains(constraint.toString()))
results.add(g);
}
}
oReturn.values = results;
}
return oReturn;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
items = (ArrayList<Info_Filter>) results.values;
notifyDataSetChanged();
}
};
}
Its ok, now I have another recycler that work with this adapter and this data model. Each one have a edit text to search. when I write something in first edit text, recyclerview number 2 changes.
How I can search for both recyclers?
First, create a class which extends Filter
public class NewFilter extends Filter {
private recyclerAdapter adapter;
private ArrayList<Info_Filter> filterList;
NewFilter(ArrayList<Info_Filter> filterList, recyclerAdapter adapter) {
this.adapter = adapter;
this.filterList = filterList;
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint != null && constraint.length() > 0) {
constraint = constraint.toString().toUpperCase();
ArrayList<Info_Filter> filteredInfo = new ArrayList<>();
for (int i = 0; i < filterList.size(); i++) {
if (filterList.get(i).getName().toLowerCase().contains(constraint)) {
filteredInfo.add(filterList.get(i));
}
}
results.count = filteredInfo.size();
results.values = filteredInfo;
} else {
results.count = filterList.size();
results.values = filterList;
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
adapter.mDataSet = (ArrayList<Info_Filter>) results.values;
adapter.notifyDataSetChanged();
}
}
Now implement the Filterable interface to your RecyclerView Adapter and override the getFilter() method.
#Override
public Filter getFilter() {
if (filter == null) {
filter = new NewFilter(filterMDataSet, this);
}
return filter;
}
filterMDataSet can be a copy of the ArrayList that you pass to the adapter when setting the adapter to you recyclerview. You could create another ArrayList in the adapter and initialize it in the constructor with the same data as the main ArrayList.
Now its tike to filter the results using a text. You can run this code with the text that you need to filter.
if (recyclerView.getAdapter() != null) {
((YourAdapter) yourAdapter).getFilter().filter(text_to_filter);
}
Check if this works?

when the search function starts and the adapter is updated the custom ListView removes every thing in the list item except the matching search value

Image 1 : before any search performed
Image 2 : after search performed
public class classBidAdapter extends ArrayAdapter<classBidAttributes>
implements Filterable {
ArrayList<classBidAttributes> data;
Context context;
CustemFilrer filter;
//------this is a snippit of my listview custom adapter related to the search/filter function----------------------------------------------------------
#NonNull
#Override
public Filter getFilter() {
if(filter == null){
filter = new CustemFilrer();
}
return filter;
}
class CustemFilrer extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if(constraint != null && constraint.length()>0){
constraint = constraint.toString();
ArrayList<classBidAttributes> filter = new ArrayList<classBidAttributes>();
for(int i =0; i<data.size();i++){
if(data.get(i).getTitle().contains(constraint)){
classBidAttributes filts = new classBidAttributes(data.get(i).getTitle());
filter.add(filts);
Toast.makeText(Context, "111"+filts, Toast.LENGTH_SHORT).show();
}
}
results.count = filter.size();
results.values=filter;
Toast.makeText(Context, "222"+filter.size() +" / "+filter, Toast.LENGTH_SHORT).show();
}else{
results.count = data.size();
results.values=data;
Toast.makeText(Context, ""+data.size() +" / "+data, Toast.LENGTH_SHORT).show();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
data.clear();
data.addAll((ArrayList<classBidAttributes>) results.values);
notifyDataSetChanged();
}
}
I'm guessing the problem is in "data.clear() , notifyDataSetChanged()" but im not sure whats wrong in here.
I've added images explaining what is happening when the search is performed.
hopfully this is every thing needed in this problem if u need to check any thing ask me.
Your guess is right. you are making mistake in data.clear(). By doing this you are clearing complete data of your adapter and adding the last search result. So next time you can't get original data to start filter again.
You should keep original data in another list to update it with your adapter data later.
Your code should be like this:
public class classBidAdapter extends ArrayAdapter<classBidAttributes>
implements Filterable {
ArrayList<classBidAttributes> data;
ArrayList<classBidAttributes> original= new ArrayList<>();
Context context;
CustemFilrer filter;
public classBidAdapter(Context context, ArrayList<classBidAttributes> data){
this.context = context;
this.data = data;
original.addAll(data);
}
}
//------this is a snippit of my listview custom adapter related to the search/filter function----------------------------------------------------------
#NonNull
#Override
public Filter getFilter() {
if(filter == null){
filter = new CustemFilrer();
}
return filter;
}
class CustemFilrer extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if(constraint != null && constraint.length()>0){
constraint = constraint.toString();
ArrayList<classBidAttributes> filter = new ArrayList<classBidAttributes>();
for(int i =0; i<data.size();i++) {
if (data.get(i).getTitle().contains(constraint)) {
classBidAttributes filts = new classBidAttributes(data.get(i).getTitle());
filter.add(filts);
Toast.makeText(Context, "111" + filts, Toast.LENGTH_SHORT).show();
}
}
results.count = filter.size();
results.values=filter;
Toast.makeText(Context, "222"+filter.size() +" / "+filter, Toast.LENGTH_SHORT).show();
}else{
results.count = original.size();
results.values=original;
Toast.makeText(Context, ""+original.size() +" / "+original, Toast.LENGTH_SHORT).show();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
data.clear();
data.addAll((ArrayList<classBidAttributes>) results.values);
notifyDataSetChanged();
}
}
Hope it helps:)
I have finally managed to make it work correctly with no bugs what so ever and it turned out to be the loop was the source of the problem, its best to avoid the normal for loop and instead use the foreach loop, i had to rewirte the whole code from scratch for the search adapter but as follows:
public void filter(CharSequence constraint) {
constraint = constraint.toString();
data.clear();
if(constraint.length() == 0){
data.addAll(original);
}else{
for(classBidAttributes filts : original){
if(filts.getTitle().contains(constraint)
|| filts.getbidid().contains(constraint)
|| filts.getbid_offer().contains(constraint)
|| filts.getCategory().contains(constraint)
|| filts.getDescription().contains(constraint)
|| filts.getQuantity().contains(constraint)
|| filts.getRepetitive_requests_yes().contains(constraint)
|| filts.getRepetitive_requests_no().contains(constraint)
|| filts.getExpiry_date().contains(constraint)){
data.add(filts);
}
}
}
notifyDataSetChanged();
};
nevertheless this snippet is based on your answer :)

android filter listview works only for the first search

i am been trying to figure what seems to be the issue, but can get my head round it, basically i am filtering my listview which uses custom adapter that has images and text. its works first time e.g. when the activity runs for the first time it has all the data and when i type something in the search box it filters it. but then from there if i now enter something else it doesn print anything and the array is empty which hold the data. any suggestion would be appreciated.
filter code fragment
#Override
public Filter getFilter() {
Filter filter = new Filter() {
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
data= (List<Data>) results.values;
notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<Item> FilteredArrayNames = new ArrayList<Item>();
// perform your search here using the searchConstraint String.
constraint = constraint.toString().toLowerCase();
for (int i = 0; i < data.size(); i++) {
Item dataNames = data.get(i);
if (dataNames.title.toLowerCase().contains(constraint.toString())) {
FilteredArrayNames.add(dataNames);
}
}
results.count = FilteredArrayNames.size();
results.values = FilteredArrayNames;
Log.e("VALUES", results.values.toString());
return results;
}
};
return filter;
}
activity code fragment
text =(SearchView)findViewById(R.id.searchData);
text.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
adapter.getFilter().filter(newText.toString());
return true;
}
});
try returning false in onQueryTextChange() method.

AutoComplete with vectors - android

I've have a Vector like this in my app:
Vector<Firm> firmVector= new Vector<Firm>();
As you may see, this is a vector of objects from my own class Firm
So to my question, is it possible to add a AutoComplete to this `Vector?
Like this one, from developer.android.com:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
AutoCompleteTextView textView = (AutoCompleteTextView)findViewById(R.id.autocomplete_country);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, COUNTRIES);
textView.setAdapter(adapter);
}
Yes, you can do it by implementing an ArrayAdapter, but you'll need to implement the getFilter() which is used by the AutoCompleteTextView. Something similar to this might work:
private class FirmAdapter extends ArrayAdapter<Firm> {
private Filter filter;
#Override
public Filter getFilter()
{
if(filter == null){
filter = new Filter(){
int lastlen = 0;
boolean initialized = false;
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results)
{
filteredItems = (List<Firm>)results.values;
ArrayAdapter.this.notifyDataSetChanged();
}
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
if(constraint == null || constraint.length() == 0){
results.values = null;
}else{
String val;
Firm obj;
constraint = constraint.toString().toUpperCase();
int newlen = constraint.length();
LinkedList<Firm> filteredResults = new LinkedList<Firm>();
if(newlen < lastlen){
int i = 0, size = ArrayAdapter.super.getCount();
for(i=0;i<size;i++){
obj = ArrayAdapter.super.getItem(i);
val = obj.toString(); // CUSTOMIZE THIS
if(val.contains(constraint)) filteredResults.add(obj);
}
}else{
int i = 0, size = getCount();
for(i=0;i<size;i++){
obj = getItem(i);
val = obj.toString(); // CUSTOMIZE THIS
if(val.contains(constraint)) filteredResults.add(obj);
}
}
lastlen = newlen;
results.values = filteredResults;
}
return results;
}
};
}
return filter;
}
}
}
I ripped this out of a project I have, so it will need some testing and cleanup to get working (in my case I have a Filter on a set of JSONObject instances, where you have a Firm) but try something like that. The comments with CUSTOMIZE THIS are where you want to actually perform the test based on the input into the AutoCompleteTextView.

android AutoCompleteTextView seems to ignore the Filter of the custom ArrayAdapter

All I want to do is displaying a CheckBox at each result of the AutoCompleteTextView's results (which are strings).
I wrote an custom Array Adapter which implements Filterable. I added a simple Filter wich gets called (I checked that) and returns the expected results. However the displayed results are completely different ones.
Here is my Filter-Code:
private class MyFilter extends Filter
{
#Override
protected FilterResults performFiltering(CharSequence constraint)
{
FilterResults results = new FilterResults();
if ((constraint == null) || (constraint.length() == 0))
{
synchronized (mLock)
{
ArrayList<String> list = new ArrayList<String>();
results.values = list;
results.count = list.size();
}
}
else
{
String constr = constraint.toString().toLowerCase();
final ArrayList<String> newItems = new ArrayList<String>();
for (String temp : items)
{
if (temp.toLowerCase().startsWith((constr)))
{
newItems.add(temp);
}
}
results.values = newItems;
results.count = newItems.size();
}
return results;
}
#Override
protected void publishResults(CharSequence constraint,
FilterResults results)
{
if (results.count > 0)
{
notifyDataSetChanged();
}
else
{
notifyDataSetInvalidated();
}
}
}
Do I miss something?
Thank you!
I forgot to set my results as the new items of the adapter.

Categories

Resources