Reputation: 11
I followed some youtube tutorial about SQLite and RecyclerView (https://www.youtube.com/watch?v=VQKq9RHMS_0&ab_channel=Stevdza-San) I completed all the things and customize it as I need to my app and everything work fine.
Now all I want to do is add Search Button at the top (Action Bar) that i can search items from my RecyclerView.
So i was try to figure out how to implement it, but I struggle with the filter and all those things. Every tutorials that I saw about this work with List (E) while my CustomeAdpter geting ArrayList and I can't undrstand how to make it work with the ArrayList.
So if some one can help me with that and give me some little guidance I will be grateful
EDIT
I added the getFilter()
function in my adapter, but when I try to search something nothing happened in RecyclerView.
I am adding my code: MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activity = MainActivity.this;
noClubsImage = findViewById(R.id.noClubsImage);
noClubsText = findViewById(R.id.noClubsText);
recyclerView = findViewById(R.id.recyclerView);
add_button = findViewById(R.id.add_button);
add_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, AddActivity.class);
activity.startActivityForResult(intent,1);
}
});
myDB = new MyDatabaseHelper(MainActivity.this);
club_id = new ArrayList<>();
club_name = new ArrayList<>();
join_date = new ArrayList<>();
expire_date = new ArrayList<>();
storeDataInArrays();
customAdapter = new CustomAdapter(MainActivity.this, this, club_id, club_name, join_date, expire_date);
recyclerView.setAdapter(customAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
}
. . . EDIT***
@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) {
customAdapter.getFilter().filter(query);
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
customAdapter.getFilter().filter(newText);
return true;
}
});
return super.onCreateOptionsMenu(menu);
}
}
And this is my Adapter:
I added Try & catch function in onBindViewHolder becuase it's crash if not and get this error: "java.lang.IndexOutOfBoundsException: Index: 1, Size: 1" After adding it, I can search but something very weird, upload some pictures: When I start the app Start searching "Yasha" -> it change the two rows
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> implements Filterable{
private Context context;
private Activity activity;
private ArrayList club_id, club_name, join_date,expire_date,list,originalList;
int position;
Animation translate_anim;
CustomAdapter(Activity activity, Context context, ArrayList club_id, ArrayList club_name, ArrayList join_date, ArrayList expire_date){
this.activity = activity;
this.context = context;
this.club_id = club_id;
this.club_name = club_name;
this.join_date = join_date;
this.expire_date = expire_date;
this.list = club_name;
this.originalList = club_name;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.my_row,parent,false);
return new MyViewHolder(view);
}
public Filter getFilter(){
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
ArrayList filteredResults = null;
if (constraint.length() == 0) {
filteredResults = club_name;
} else {
filteredResults = getFilteredResults(constraint.toString().toLowerCase());
}
FilterResults results = new FilterResults();
results.values = filteredResults;
Log.d("Test",filteredResults.toString());
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
club_name = (ArrayList<String>) results.values;
notifyDataSetChanged();
}
};
}
protected ArrayList getFilteredResults(String constraint) {
ArrayList results = new ArrayList<>();
for (Object item : originalList) {
if (item.toString().toLowerCase().contains(constraint)) {
results.add(item.toString());
}
}
//Log.d("Test",results.toString());
return results;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
try{
this.position = position;
holder.club_name_txt.setText(String.valueOf(club_name.get(position)));
holder.join_txt.setText(String.valueOf(join_date.get(position)));
holder.expire_txt.setText(String.valueOf(expire_date.get(position)));
holder.mainLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context,UpdateActivity.class);
intent.putExtra("club_id",String.valueOf(club_id.get(position)));
intent.putExtra("club_name",String.valueOf(club_name.get(position)));
intent.putExtra("join_date",String.valueOf(join_date.get(position)));
intent.putExtra("expire_date",String.valueOf(expire_date.get(position)));
activity.startActivityForResult(intent,1);
}
});
}catch(Exception ex) {
Log.e("TAG", "EXCEPTION CAUGHT WHILE EXECUTING DATABASE TRANSACTION");
ex.printStackTrace();
}
}
@Override
public int getItemCount() {
return club_id.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
LinearLayout mainLayout;
TextView club_id_txt, club_name_txt, join_txt,expire_txt;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
club_name_txt = itemView.findViewById(R.id.club_name_txt);
join_txt = itemView.findViewById(R.id.jDate_txt);
expire_txt = itemView.findViewById(R.id.eDate_txt);
mainLayout = itemView.findViewById(R.id.mainLayout);
translate_anim = AnimationUtils.loadAnimation(context, R.anim.translate_anim);
mainLayout.setAnimation(translate_anim);
}
}}
Upvotes: 1
Views: 551
Reputation: 19223
you have customAdapter.getFilter();
line in both methods of OnQueryTextListener
and it returns filter for you. thats all what you have requested, you've never passed a String
to this filter. this line should look like this
customAdapter.getFilter().filter(query); // or newText, depend on OnQueryTextListener method
edit due to comments:
remove this try{}catch()
and fix your exception. this method is reliable and only your mistake can lead to crash.
you are filtering and publishing only one of arrays (club_name = (ArrayList<String>) results.values;
), but still you are informing adapter
that list have number of items equal to club_id.size();
in getItemCount
. thats why you are getting IndexOutOfBoundsException
- club_id
is untouched and have multiple items, even after filtering when club_name
have only few or one item inside. you should filter all four arrays, in fact this should be one array with custom object carrying all four params
some tutorial for fixing:
keep your data in some additional arrays which will stay untouched and keep all data even after filtering. for start make "original" and "working" arrays
CustomAdapter(Activity activity, Context context, ArrayList club_id, ArrayList club_name, ArrayList join_date, ArrayList expire_date){
this.activity = activity;
this.context = context;
this.club_id_org = club_id;
this.club_name_org = club_name;
this.join_date_org = join_date;
this.expire_date_org = expire_date;
this.club_id = new ArrayList<>();
this.club_id.addAll(this.club_id_org);
this.club_name = new ArrayList<>();
this.club_name.addAll(this.club_name_org);
this.join_date = new ArrayList<>();
this.join_date.addAll(this.join_date_org);
this.expire_date = new ArrayList<>();
this.expire_date.addAll(this.expire_date_org);
}
note how much duplicated code we have in here, thats why this should be one ArrayList
with some custom object... but nvm, lets stay (for now) with four arrays
dont use this construction: this.list = club_name;
- this makes list
and club_name
are same arrays (objects), when you remove some item from one then it will be removed from second (in fact same) array, and we need here two separated arrays (x4)
now all arrays with _org
suffix are carrying whole lists, but adapter should work on "working" arrays, these without _org
, like you have currently in code
now filtering: in performFiltering
you should create, again, four temporary arrays and fill them with filtered items. for filtering iterate through _org
arrays, these with all items
@Override
protected FilterResults performFiltering(CharSequence constraint) {
ArrayList club_id_temp = new Arraylist<>(),
club_name_temp = new Arraylist<>(),
join_date_temp = new Arraylist<>(),
expire_date_temp = new Arraylist<>();
if (constraint.length() == 0) {
club_id_temp.addAll(club_id_org);
club_name_temp.addAll(club_name_org);
join_date_temp.addAll(join_date_org);
expire_date_temp.addAll(expire_date_org);
} else {
for(int i=0; i<club_name_org.size(); i++){
if(club_name_org.get(i).toLowerCase().contains(constraint.toLowerCase())){
club_id_temp.add(club_id_org.get(i));
club_name_temp.add(club_name_org.get(i));
join_date_temp.add(join_date_org.get(i));
expire_date_temp.add(expire_date_org.get(i));
}
}
}
FilterResults results = new FilterResults();
results.values = ...
and now we have a small problem... results.values
can carry only one object, e.g. one ArrayList
and you have four... lets make come class carrying all four for your purposes, declare such e.g. on the bottom of adapter (just before last bracket closing whole adapter }
)
public static class TempArrays{
public ArrayList club_id, club_name, join_date, expire_date;
}
and pack all four filtered arrays into:
FilterResults results = new FilterResults();
TempArrays ta = new TempArrays();
ta.club_id = club_id_temp;
ta.club_name = club_name_temp;
ta.join_date = join_date_temp;
ta.expire_date = expire_date_temp;
results.values = ta;
return results;
}
and unpack this construction in publishResults
:
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
TempArrays ta = (TempArrays) results.values;
club_id = ta.club_id;
club_name = ta.club_name;
join_date = ta.join_date;
expire_date = ta.expire_date;
notifyDataSetChanged();
}
again: four arrays isn't proper way, look how much duplicated lines we have here... + some small workaround for passing all four after filtering instead of one... in fact there should be some ClubModel
class, similar to this
public static class ClubModel{
public String club_id, club_name, join_date, expire_date;
}
and then you may work on one array with ClubModel
items, instead of four arrays. check out POJO definition
Upvotes: 1