Reputation: 5655
I am trying to create implement multiple filters for searching data in Recyclerview.
For instance, in a shopping list, I can put filter like computers falling under the following criteria.
Brand = apple , ScreenSize = 10 to 13 inch , HardDisk Size = 250 to 500
Now my plane ViewModel will be having list of computers with all these criteria inside model class ResponseBase, which I am getting from backend.
class ItemViewModel : ViewModel() {
var mResponse : MutableLiveData<ResponseBase>? = null
fun getData() : MutableLiveData<ResponseBase> {
if(null == mResponse) {
mResponse = NetworkProcessor().loadSearchData()
}
return mResponse as MutableLiveData<ResponseBase>
}
}
ResponseBase.kt
data class ResponseBase(
val matches: List<ComputersData>
)
ComputersData.kt
data class ComputersData(
val brand: String,
val screenSize: Int,
val hardDisk: Int,
val processor: String,
val display_name: String,
val ram: Int,
)
Now as per Android Sunflower sample, RecyclerView can have DiffUtil , which can be used for efficient filtering.
But how to filter ComputersData based on criteria like this with DiffUtil inside RecyclerView Adapter?
brand = apple && screenSize = 10 to 13 , hardDisk = 250 to 500
Any idea would be highly appreciated!
Upvotes: 3
Views: 2081
Reputation: 10214
MainActivity
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private EmployeeRecyclerViewAdapter mRecyclerViewAdapter;
private List<Employee> employeeList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
employeeList = DummyEmployeeDataUtils.getEmployeeList();
mRecyclerViewAdapter = new EmployeeRecyclerViewAdapter(
employeeList);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mRecyclerViewAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.sort_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.age_above_employees:
mRecyclerViewAdapter.updateEmployeeListItems(DummyEmployeeDataUtils.getEmployessWithAgeAbove50());
return true;
}
return super.onOptionsItemSelected(item);
}
}
Create dummy employees list with age
public class DummyEmployeeDataUtils {
public static List<Employee> getEmployessWithAgeAbove50() {
final List<Employee> employeeList = getEmployeeList();
final List<Employee> filteredEmployees = new ArrayList<>();
for (int i = 0; i < employeeList.size(); i++) {
if (employeeList.get(i).getAge() > 50)
filteredEmployees.add(employeeList.get(i));
}
return filteredEmployees;
}
public static List<Employee> getEmployeeList() {
final List<Employee> employees = new ArrayList<>();
employees.add(new Employee(1, "Employee 1", "Developer", 12));
employees.add(new Employee(2, "Employee 2", "Tester", 52));
employees.add(new Employee(3, "Employee 3", "Support", 72));
employees.add(new Employee(4, "Employee 4", "Sales Manager", 11));
employees.add(new Employee(5, "Employee 5", "Manager", 64));
employees.add(new Employee(6, "Employee 6", "Team lead", 99));
employees.add(new Employee(7, "Employee 7", "Scrum Master", 89));
employees.add(new Employee(8, "Employee 8", "Sr. Tester", 23));
employees.add(new Employee(9, "Employee 9", "Sr. Developer", 21));
return employees;
}
}
Here goes our Adapter
public class EmployeeRecyclerViewAdapter extends
RecyclerView.Adapter<EmployeeRecyclerViewAdapter
.ViewHolder> {
private List<Employee> mEmployees = new ArrayList<>();
public EmployeeRecyclerViewAdapter(List<Employee> employeeList) {
this.mEmployees.addAll(employeeList);
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final View view = inflater.inflate(R.layout.list_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
final Employee employee = mEmployees.get(position);
holder.bindView(employee);
}
public void updateEmployeeListItems(List<Employee> employees) {
final EmployeeDiffCallback diffCallback = new EmployeeDiffCallback(this.mEmployees, employees);
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);
this.mEmployees.clear();
this.mEmployees.addAll(employees);
diffResult.dispatchUpdatesTo(this);
}
@Override
public int getItemCount() {
return mEmployees.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView role;
private final TextView name;
private final TextView age;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.employee_name);
role = (TextView) itemView.findViewById(R.id.employee_role);
age = (TextView) itemView.findViewById(R.id.employee_age);
}
void bindView(Employee employee) {
name.setText(employee.getName());
role.setText(employee.getRole());
age.setText("Age ".concat(employee.getAge()+""));
}
}
}
This is the DiffUtil Adapter
public class EmployeeDiffCallback extends DiffUtil.Callback {
private final List<Employee> mOldEmployeeList;
private final List<Employee> mNewEmployeeList;
public EmployeeDiffCallback(List<Employee> oldEmployeeList, List<Employee> newEmployeeList) {
this.mOldEmployeeList = oldEmployeeList;
this.mNewEmployeeList = newEmployeeList;
}
@Override
public int getOldListSize() {
return mOldEmployeeList.size();
}
@Override
public int getNewListSize() {
return mNewEmployeeList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldEmployeeList.get(oldItemPosition).getId() == mNewEmployeeList.get(
newItemPosition).getId();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
final Employee oldEmployee = mOldEmployeeList.get(oldItemPosition);
final Employee newEmployee = mNewEmployeeList.get(newItemPosition);
return oldEmployee.getName().equals(newEmployee.getName());
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
// Implement method if you're going to use ItemAnimator
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
EDIT TO ANSWER from Manoj. Updated by Author for future reference
The above answer works fine in case of RecyclerView without ViewModels. Once viewmodel comes to picture and RecyclerView listens to changes in VM, the logic should be to update the filtered list with new results in ViewModel itself. So that recycler view listens to the updates all time and keep data updated.
In this scenario, Diff Utils may not come in handly. Rather efficient way (As the Question is in Kotlin) is with Filters
I found filters much more simpler than Diff Util implementation.
For future reference , Sample filter approach with multiple predicates are as follows
mViewModelObject is the instance of View Model object
val valueOne = mViewModelObject.criteriaOne.value
val valueTwo = mViewModelObject.criteriaTwo.value
val newList = originalList.filter {
it.listVariableABC == valueOne
&&
it.listVariablePQR == valueTwo
}
Once the new list is created out of multiple criteria, update the same in ViewModel. So that Recycler View will get updated list.
Upvotes: 2