1

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);
    }
 }}
akhilesh0707
  • 6,709
  • 5
  • 44
  • 51
XCaMeLX
  • 11
  • 4
  • possible duplicate of https://stackoverflow.com/q/30398247/5370550 – Nik Sep 07 '20 at 13:25
  • @Nik look, I edit my question now, I was try some methods from the post that you put here, but after run it nothing happend when I try to search something. – XCaMeLX Sep 07 '20 at 13:43

1 Answers1

1

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

snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • add it "filter(query);" and still nothing happend0 – XCaMeLX Sep 07 '20 at 14:32
  • is `performFiltering` method gets called? – snachmsm Sep 07 '20 at 14:35
  • Yes but stop in the middle, (I check it with Log.... message) I got this error when i start to typing something in the search: W/Filter: An exception occured during performFiltering()! java.lang.ClassCastException: java.lang.String cannot be cast to com.camel.myclub.CustomAdapter And it's take me to this line: for "(CustomAdapter item : originalList).." – XCaMeLX Sep 07 '20 at 14:47
  • `List` - why do create list of your adapters? adapter is one and only (most oftenly) and data inside may be an array with multiple items. so inside `getFilteredResults` you should work on your data arrays (`ArrayList` `club_id`, `club_name` etc.), not adapter arrays. Remove `protected List list;` and `protected List originalList;`, they aren't needed for filtering, instead you may need some temproary arrays for filtered data (for keeping original ones untouched when someone clears query requesting whole list) – snachmsm Sep 07 '20 at 14:47
  • Ok, so i chagne every thing that you say, plus i change"list" and "originalList" to ArrayList After do some tests i found that the filter work ( I saw the changes in the LOG every time when I typing something), but it's not changing anything on RecyclerView... Do you have *discord* that we can talk? – XCaMeLX Sep 07 '20 at 16:16
  • post updated `Adapter`s code in question and let me know, I will inspect – snachmsm Sep 07 '20 at 17:14
  • Hey, I updated it, Two things i need to tell you 1) The Filter seems to work ( I can see in LOG after each typing the changes) 2) I was added Try and Catch to onBindViewHolder becuase if not i got error: "java.lang.IndexOutOfBoundsException: Index: 1, Size: 1" – XCaMeLX Sep 07 '20 at 17:32
  • Hey, sorry for delay, I must say that it's work perfect... I will take your tipe about create class named "Clubs" and put there all is options and after that in CustomAdapter work only with one array and not with 4. – XCaMeLX Sep 10 '20 at 18:36
  • consider upvoting/accepting my answer if helpful :) good luck! – snachmsm Sep 10 '20 at 18:38