Search code examples
androidfilterbaseadapter

Base Adapter Not Filtering Properly


I have a list view which uses a base adapter, this is how I initialise it:

if (mBuses.size() < 1) {
    // Initialising and loading data into adapter
    mBuses = buses;
    mBusesAdapter = new BusesArrayAdapter(BusesActivity.this, mBuses);
    mBusesView.setAdapter(mBusesAdapter);
} else {
    mBuses.addAll(buses);
    mBusesAdapter.notifyDataSetChanged();
}

I am using a listener to check when the user scrolls to the bottom it fetches more data from the server the server and appends to the existing set instead of using pagination. It loads 100 items per page.

Now my problem is when I try to filter the data it always only filters the first 100 items, it does not include the other items.

This is my base adapter:

public class BusesArrayAdapter extends BaseAdapter implements Filterable {

    private Context context;
    private int altColour;
    private ArrayList<BusModel> buses;
    private BusModel bus;
    private ValueFilter valueFilter;
    private ArrayList<BusModel> filterList;

    private static class ViewHolder{
        RelativeLayout container;
        TextView id;
        TextView busType;
        TextView registrationNo;
        TextView vehicleModel;
        TextView driverName;
        TextView driverContact;
        ImageButton btnEdit;
        ImageButton btnRemove;
    }

    public BusesArrayAdapter(@NonNull Context context, @NonNull ArrayList<BusModel> buses) {
        this.context = context;
        this.buses = buses;
        this.filterList = new ArrayList<>(buses);
    }

    @Override
    public int getCount() {
        return buses.size();
    }

    @Override
    public BusModel getItem(int position) {
        return buses.get(position);
    }

    @Override
    public long getItemId(int position) {
        return getItem(position).getId();
    }

    public void remove(BusModel bus) {
        buses.remove(bus);
    }

    @NonNull
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
        // Get the data item for this position
        bus = getItem(position);

        // Check if an existing view is being reused, otherwise inflate the view
        final ViewHolder viewHolder; // view lookup cache stored in tag
        if (convertView == null) {
            // If there's no view to re-use, inflate a brand new view for row
            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(context);
            convertView = inflater.inflate(R.layout.row_buses, parent, false);
            viewHolder.container = (RelativeLayout) convertView.findViewById(R.id.row_buses_ll_container);
            viewHolder.id = (TextView) convertView.findViewById(R.id.row_buses_tv_id);
            viewHolder.busType = (TextView) convertView.findViewById(R.id.row_buses_tv_bus_type);
            viewHolder.registrationNo = (TextView) convertView.findViewById(R.id.row_buses_tv_registration_no);
            viewHolder.vehicleModel = (TextView) convertView.findViewById(R.id.row_buses_tv_vehicle_model);
            viewHolder.driverName = (TextView) convertView.findViewById(R.id.row_buses_tv_driver_name);
            viewHolder.driverContact = (TextView) convertView.findViewById(R.id.row_buses_tv_driver_contact);
            viewHolder.btnEdit = (ImageButton) convertView.findViewById(R.id.row_buses_btn_edit);
            viewHolder.btnEdit.setTag(position);
            viewHolder.btnRemove = (ImageButton) convertView.findViewById(R.id.row_buses_btn_trash);
            viewHolder.btnRemove.setTag(position);

            // Cache the viewHolder object inside the fresh view
            convertView.setTag(viewHolder);
        } else {
            // View is being recycled, retrieve the viewHolder object from tag
            viewHolder = (ViewHolder) convertView.getTag();
        }
        // Populate the data from the data object via the viewHolder object
        // into the template view.

        if (altColour == 0) {
            viewHolder.container.setBackgroundColor(Color.parseColor("#FFFFFF"));
            altColour = 1;
        } else {
            viewHolder.container.setBackgroundColor(Color.parseColor("#EFEFEF"));
            altColour = 0;
        }

        viewHolder.id.setText(String.valueOf(bus.getId()));
        viewHolder.busType.setText(bus.getBusType());
        viewHolder.registrationNo.setText(bus.getRegistrationNo());
        viewHolder.vehicleModel.setText(bus.getVehicleModel());
        viewHolder.driverName.setText(bus.getDriverName());
        viewHolder.driverContact.setText(bus.getDriverContact());
        viewHolder.btnEdit.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                int position = Integer.parseInt(viewHolder.btnRemove.getTag().toString());
                BusModel bus = getItem(position);

                if (bus != null) {
                    Intent editBusIntent = new Intent(context, EditBusActivity.class);
                    editBusIntent.putExtra("bus", bus);
                    ((Activity) context).startActivityForResult(editBusIntent, 200);
                }
            }
        });

        viewHolder.btnRemove.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                int position = Integer.parseInt(viewHolder.btnRemove.getTag().toString());
                removeBus(position);
            }

        });

        // Return the completed view to render on screen
        return convertView;
    }

    private void removeBus(final int position) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);

        builder.setTitle("Confirm");
        builder.setMessage("Are you sure you want to delete this item?");

        builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {

            public void onClick(DialogInterface dialog, int which) {
                BusesRequest busesRequest = new BusesRequest(context);
                busesRequest.remove(getItem(position).getId(), mTrashOnSuccessListener, mTrashOnErrorListener);

                remove(getItem(position));
                notifyDataSetChanged();
            }

        });

        builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                // Do nothing
                dialog.dismiss();
            }
        });
        AlertDialog alert = builder.create();
        alert.show();
    }

    private Response.Listener<JSONObject> mTrashOnSuccessListener = new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            //
        }
    };

    Response.ErrorListener mTrashOnErrorListener = new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Utils.showNetworkResponse(context, error);
        }
    };

    @Override
    public Filter getFilter() {
        // TODO Auto-generated method stub
        if (valueFilter == null) {

            valueFilter = new ValueFilter();
        }

        return valueFilter;
    }

    private class ValueFilter extends Filter {

        //Invoked in a worker thread to filter the data according to the constraint.
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            FilterResults results = new FilterResults();
            if(constraint != null && constraint.length() > 0){
                ArrayList<BusModel> filterList = new ArrayList<BusModel>();
                for(int i = 0; i < BusesArrayAdapter.this.filterList.size(); i++)
                {
                    BusModel bus = BusesArrayAdapter.this.filterList.get(i);

                    if((bus.getRegistrationNo().toLowerCase())
                            .contains(constraint.toString().toLowerCase())
                            || (bus.getDriverName().toLowerCase())
                            .contains(constraint.toString().toLowerCase()) ) {

                        filterList.add(new BusModel(
                                bus.getId(),
                                bus.getBusType(),
                                bus.getRegistrationNo(),
                                bus.getVehicleModel(),
                                bus.getDriverName(),
                                bus.getDriverContact()
                        ));

                    }
                }

                results.count = filterList.size();
                results.values = filterList;
            } else{
                results.count = filterList.size();
                results.values = filterList;
            }
            return results;
        }


        //Invoked in the UI thread to publish the filtering results in the user interface.
        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint,
                                      FilterResults results) {
            buses = (ArrayList<BusModel>) results.values;
            notifyDataSetChanged();
        }
    }
}

Solution

  • because filterList init only once while you are creating an instance of Adapter. you have to put new 100 items in filterList also.

    this.filterList = new ArrayList<>(buses);
    

    stores only 100 records when you are creating a new instance like this.

    mBusesAdapter = new BusesArrayAdapter(BusesActivity.this, mBuses);
    

    try to add every fetched items filterList also.