Search code examples
androidandroid-recyclerviewonclicklistenerandroid-adaptersearchview

RecyclerView + onClickListener + searchView showing wrong results


basically my code is about displaying user data on a recyclerView, & when clicked, would intent to an activity that displays more of that particular user's data.

onClickListener works fine if I do not use the searchView. It intents the correct data.

However, when using searchView, when first result is clicked, it always takes the data of the first item of the recyclerView that was displayed before i entered any searchView query, and so on for positions 2,3,4...

Adapter Class

public class OwnerAdapter extends RecyclerView.Adapter<OwnerAdapter.ViewHolder> {
    private final RecyclerInterface recyclerInterface;

    private ArrayList<Owners> ownerList;



    public OwnerAdapter(ArrayList<Owners> ownerList, RecyclerInterface recyclerInterface){
        this.recyclerInterface = recyclerInterface;
        this.ownerList = ownerList;
    }

    public void setFilteredList(ArrayList<Owners> filteredList) {
        this.ownerList = filteredList;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public OwnerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.owner_recycler_row, parent, false);
        return new ViewHolder(view);

    }

    @Override
    public void onBindViewHolder(@NonNull OwnerAdapter.ViewHolder holder, int position) {
        int resource = R.drawable.ic_baseline_person_24;
        String name=ownerList.get(position).getOwner_name();
        int owner_num = ownerList.get(position).getOwner_number();
        String category = ownerList.get(position).getCategory();

        holder.setData(resource,name,owner_num,category);
    }

    @Override
    public int getItemCount() {
        return ownerList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        private ImageView imageView;
        private TextView tvName, tvOwner_num, tvCategory;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            imageView=itemView.findViewById(R.id.imageview1);
            tvName=itemView.findViewById(R.id.tvName);
            tvOwner_num=itemView.findViewById(R.id.tvOwner_num);
            tvCategory=itemView.findViewById(R.id.tvCategory);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (recyclerInterface != null) {
                        int pos = getAdapterPosition();

                        if (pos != RecyclerView.NO_POSITION){
                            recyclerInterface.onRecyclerClick(pos);
                        }
                    }
                }
            });

        }

        public void setData(int resource, String name, int owner_num, String category) {

            imageView.setImageResource(resource);
            tvName.setText(name);
            tvOwner_num.setText(Integer.toString(owner_num));
            tvCategory.setText(category);



        }
    }


} 

ViewOwners Class (displays the recycler view)

public class ViewOwnerDetails extends AppCompatActivity implements RecyclerInterface {
    RecyclerView recyclerView;
    LinearLayoutManager layoutManager;
    OwnerAdapter ownerAdapter;
    ArrayList<Owners> ownerList = new ArrayList<>();
    ArrayList<Owners> filteredList = new ArrayList<>();
    private SearchView searchView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.owner_recylcer_view);

        new ViewOwnerDetails.Async().execute();

        searchView = findViewById(R.id.searchView);
        searchView.clearFocus();
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                filteredList.clear();
                filterList(newText);
                return true;
            }
        });




    }

    class Async extends AsyncTask<Void, Void, Void> {

        ProgressDialog pd;

        @Override
        protected void onPreExecute() { //before completing task
            //loading message
            super.onPreExecute();
            pd = new ProgressDialog(ViewOwnerDetails.this);
            pd.setMessage("Loading");
            pd.show();
        }

        @Override

        protected Void doInBackground(Void... voids) { //
            load(); //load data to userList

            return null;
        }

        @Override

        protected void onPostExecute(Void aVoid) { //after completing task

            initRecyclerView();
            pd.dismiss();
        }


    }

    public void load() {
        try {
            Class.forName("com.mysql.jdbc.Driver");

            Connection conn = DriverManager.getConnection("jdbc:mysql://0104-acrs.mysql.database.azure.com:3306/azuredb", "VSVP", "VVSP0104!");
            Statement statement = conn.createStatement();
            ResultSet rs = statement.executeQuery("SELECT * FROM owner_data O INNER JOIN category C on O.catId = C.cat_id;");

            while (rs.next()) {
                String category = rs.getString("cat_name"), owner_name = rs.getString("owner_name");
                int owner_num = rs.getInt("owner_num");
                Boolean enabled = rs.getBoolean("isEnabled");

                Owners owner = new Owners(owner_name, category, enabled, owner_num);
                ownerList.add(owner);
                Log.i("hi", "load: " + ownerList);
            }
        } catch (Exception e) {
            Log.i("hi", "ERROR: " + e.getMessage());

        }

    }

    private void initRecyclerView() {
        recyclerView = findViewById(R.id.recyclerView);
        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        ownerAdapter = new OwnerAdapter(ownerList, this);
        recyclerView.setAdapter(ownerAdapter);
        ownerAdapter.notifyDataSetChanged();
    }

    public void onRecyclerClick(int position) {
        Intent intent = new Intent(ViewOwnerDetails.this, ViewFullOwnerDetails.class);

        if(searchView.toString().isEmpty()) {
            intent.putExtra("NAME", ownerList.get(position).getOwner_name());
            intent.putExtra("OWNER_NUM", ownerList.get(position).getOwner_number());
            intent.putExtra("CATEGORY", ownerList.get(position).getCategory());
            intent.putExtra("ENABLED", ownerList.get(position).isEnabled());
        } else if(!searchView.toString().isEmpty()){
            intent.putExtra("NAME", filteredList.get(position).getOwner_name());
            intent.putExtra("OWNER_NUM", filteredList.get(position).getOwner_number());
            intent.putExtra("CATEGORY", filteredList.get(position).getCategory());
            intent.putExtra("ENABLED", filteredList.get(position).isEnabled());
        }

        startActivity(intent);
    }

    private void filterList(String text) {
        for(Owners o : ownerList){
            if(o.getOwner_name().toLowerCase().contains(text.toLowerCase())){
                filteredList.add(o);
            }
        }
        if(filteredList.isEmpty()) {
            Toast.makeText(this, "No results found", Toast.LENGTH_SHORT).show();
        } else {
            ownerAdapter.setFilteredList(filteredList);
        }
    }
}


ViewFullOwnerInfo Class (to be intented to after clicking recyclerView item)

public class ViewFullOwnerDetails extends AppCompatActivity {

    TextView tvTitle, tvName, tvOwner_num, tvCategory, tvMember;

    Button btBack, btEdit, btDeleteOwner, btView;

    ArrayList<String> associatedList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.full_owner_details);

        initComponents();
        intentData();
        new asyncsetUpAssociatedList().execute();

        btBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ViewFullOwnerDetails.this, ViewOwnerDetails.class);
                startActivity(intent);
            }
        });

        btDeleteOwner.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                deleteConfirmationOwner();
            }
        });

        btEdit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(ViewFullOwnerDetails.this, ViewOwnerDetails.class);
                startActivity(intent);

            }
        });

        btView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewDialog(associatedList);
            }
        });


    }

    public void intentData() {

        String name = getIntent().getStringExtra("NAME");
        int owner_num = getIntent().getIntExtra("OWNER_NUM", 0);
        String category = getIntent().getStringExtra("CATEGORY");
        Boolean enabled = getIntent().getBooleanExtra("ENABLED", false);


        tvTitle.setText("FULL INFORMATION");
        tvName.setText(name);
        tvOwner_num.setText(String.valueOf(owner_num));
        tvCategory.setText(category);



        if (enabled.toString() == "false") {
            tvMember.setText("Membership Disabled");
        } else if (enabled.toString() == "true") {
            tvMember.setText("Membership Enabled");
        }


    }

    private void initComponents() {
        tvTitle = (TextView) findViewById(R.id.tvTitle);
        tvName = (TextView) findViewById(R.id.tvName);
        tvOwner_num = (TextView) findViewById(R.id.tvCategory);
        tvCategory = (TextView) findViewById(R.id.tvOwner_num);
        tvMember = (TextView) findViewById(R.id.tvMember);

        btBack = (Button) findViewById(R.id.btBack);
        btDeleteOwner = (Button) findViewById(R.id.buttonDeleteOwner);
        btEdit = (Button) findViewById(R.id.btResolve);
        btView = (Button) findViewById(R.id.btViewVehicles);
    }

    private void viewDialog(List<String> associatedList) {
        String output = "";
        if (associatedList.isEmpty()) {
            output = "List empty";
        } else {
            for (int i = 0; i < associatedList.size(); i++) {
                String vehicle = associatedList.get(i);
                output += vehicle + "\n";
            }
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setMessage("ASSOCIATED VEHICLES: \n" + output)
                .setPositiveButton("Close", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
        AlertDialog alertDialog = builder.create();
        alertDialog.show();
    }

    private void deleteConfirmationOwner() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setMessage("Are you sure you want to delete this Owner? It will delete all related vehicles also")
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        new ViewFullOwnerDetails.asyncDeleteOwner().execute();
                        Intent intent = new Intent(ViewFullOwnerDetails.this, ViewOwnerDetails.class);
                        startActivity(intent);
                        Toast.makeText(ViewFullOwnerDetails.this, "Delete Succesful!", Toast.LENGTH_LONG).show();
                    }
                })

                .setNegativeButton("No", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        dialog.cancel();
                    }
                });
        AlertDialog alertDialog = builder.create();
        alertDialog.show();
    }

    class asyncDeleteOwner extends AsyncTask<Void, Void, Void> {
        protected Void doInBackground(Void... voids) {
            try {
                Class.forName("com.mysql.jdbc.Driver");

                Connection conn = DriverManager.getConnection("url", "user", "pass");
                String query = String.format("DELETE FROM owner_data WHERE owner_num = '%d'", Integer.parseInt(tvOwner_num.getText().toString()));
                PreparedStatement statement = conn.prepareStatement(query);
                statement.executeUpdate();


            } catch (Exception e) {


            }
            return null;
        }

    }

    class asyncsetUpAssociatedList extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... voids) {
            try {
                Class.forName("com.mysql.jdbc.Driver");

                Connection conn = DriverManager.getConnection("url", "pass", "user");
                Statement statement = conn.createStatement();
                //String query = String.format("SELECT * FROM vehicle_data V INNER JOIN owner_data O on O.owner_num = V.owner_num WHERE V.owner_num = '%d'", Integer.parseInt(tvOwner_num.getText().toString()));
                ResultSet rs = statement.executeQuery("SELECT * FROM owner_data O INNER JOIN category C on O.catId = C.cat_id;");

                while (rs.next()) {
                    String reg_num = rs.getString("reg_num");
                    associatedList.add(reg_num);
                }
            } catch (Exception e) {
                Log.i("v", "Error: " + e.getMessage());
            }
            return null;
        }
    }
}

error when i click on recyclerView item without search query:

2022-08-01 18:13:39.119 26727-26727/? E/.example.acrs3: Unknown bits set in runtime_flags: 0x8000
2022-08-01 18:13:39.828 26727-26727/com.example.acrs30 E/RecyclerView: No adapter attached; skipping layout
2022-08-01 18:13:41.039 26727-26727/com.example.acrs30 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.acrs30, PID: 26727
    java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.get(ArrayList.java:437)
        at com.example.acrs30.ViewOwnerDetails.onRecyclerClick(ViewOwnerDetails.java:131)
        at com.example.acrs30.OwnerAdapter$ViewHolder$1.onClick(OwnerAdapter.java:73)
        at android.view.View.performClick(View.java:7125)
        at android.view.View.performClickInternal(View.java:7102)
        at android.view.View.access$3500(View.java:801)
        at android.view.View$PerformClick.run(View.java:27336)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Solution

  • when using searchView, when first result is clicked, it always takes the data of the first item of the recyclerView that was displayed before i entered any searchView query, and so on for positions 2,3,4...

    This means that your recyclerView list is not getting updated properly when you search for an item. The list you are showing in the search view is different than the list that you are making changes to upon clicking.

      public void onRecyclerClick(int position) {
            Intent intent = new Intent(ViewOwnerDetails.this, ViewFullOwnerDetails.class);
    
            intent.putExtra("NAME", ownerList.get(position).getOwner_name());
            intent.putExtra("OWNER_NUM", ownerList.get(position).getOwner_number());
            intent.putExtra("CATEGORY", ownerList.get(position).getCategory());
            intent.putExtra("ENABLED", ownerList.get(position).isEnabled());
    
            startActivity(intent);
        }
    

    So, the problem here is you are accessing the first item of your ownerList which is not the same as the filtered list. So, in order to get the correct item, you should access the filtered list rather than ownerList. You can initialise the filteredList for the whole activity and then you can use recyclerView on click accordingly. If the search view is empty, you can access items from ownerList and if it is not, you can access items from filtered list.