Android Как применить несколько фильтров с помощью androidx.recyclerview.widget.DiffUtil

Я пытаюсь создать несколько фильтров для поиска данных в Recyclerview.

Например, в списке покупок я могу отфильтровать компьютеры, подпадающие под следующие критерии.

Марка = яблоко, размер экрана = от 10 до 13 дюймов, размер жесткого диска = от 250 до 500

Теперь моя модель ViewModel будет иметь список компьютеров со всеми этими критериями внутри класса модели ResponseBase, который я получаю из бэкэнда.

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>
)

КомпьютерыData.kt

data class ComputersData(
    val brand: String,
    val screenSize: Int,
    val hardDisk: Int,
    val processor: String,
    val display_name: String,
    val ram: Int,
)

Теперь, согласно образцу Android-подсолнух, RecyclerView может иметь DiffUtil, который можно использовать для эффективной фильтрации.

Но как фильтровать ComputersData на основе подобных критериев с помощью DiffUtil внутри адаптера RecyclerView?

brand = apple && screenSize = от 10 до 13 , hardDisk = от 250 до 500

Любая идея будет высоко оценена!

Сначала вы должны создать отфильтрованный список, а затем отправить его в diffutil с помощью submitlist.

Manoj Perumarath 09.07.2019 08:13

какие-либо образцы или ссылки для справки, пожалуйста?

Sreehari 09.07.2019 08:20

DiffUtil — это служебный класс, который может вычислить разницу между двумя списками и вывести список операций обновления, которые преобразуют первый список во второй. от developer.android.com/reference/android/support/v7/util/…. Если вы хотите отфильтровать список по своим критериям, вы должны отфильтровать его самостоятельно.

JingJoeh 09.07.2019 08:21

Я опубликую один на Java, это нормально для вас?

Manoj Perumarath 09.07.2019 08:28

Конечно, пожалуйста, продолжайте..

Sreehari 09.07.2019 08:29

Братан, пожалуйста, проверьте ответ, если вам нужна помощь, просто пропингуйте комментарий

Manoj Perumarath 09.07.2019 09:06

Конечно. Спасибо за ваш вклад. Дай мне проверить.

Sreehari 09.07.2019 09:14
3
7
1 490
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Основная деятельность

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);
   }
 }

Создать фиктивный список сотрудников с указанием возраста

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;
  }
}

Вот наш адаптер

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()+""));
    }
  }
}

Это адаптер DiffUtil

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);
   }
}

ОБНОВЛЕННЫЙ ОТВЕТ

РЕДАКТИРОВАТЬ ОТВЕТ от Manoj. Обновлено Автор для дальнейшего использования.

Приведенный выше ответ отлично работает в случае RecyclerView без ViewModels. Как только модель просмотра приходит к изображению, а RecyclerView прослушивает изменения в виртуальной машине, логика должна заключаться в обновлении отфильтрованного списка новыми результатами в самой ViewModel. Таким образом, представление ресайклера все время прослушивает обновления и обновляет данные.

В этом случае Диф Утилиты может не подойти. Достаточно эффективный способ (поскольку вопрос в Котлине) - с Фильтры

Я обнаружил, что фильтры намного проще, чем реализация Diff Util.

Для справки в будущем подход к выборке фильтра с несколькими предикатами выглядит следующим образом.

mViewModelObject — это экземпляр объекта View Model.

val valueOne =  mViewModelObject.criteriaOne.value
val valueTwo =  mViewModelObject.criteriaTwo.value

 val newList  = originalList.filter {
                    it.listVariableABC == valueOne 
                    &&
                    it.listVariablePQR == valueTwo
}

Как только новый список будет создан из нескольких критериев, обновите его в ViewModel. Так что Recycler View получит обновленный список.

Для EmployeeDiffCallback в методе areItemsTheSame образец сравнивает getId . Является ли это членом класса Employee? Или какие-то методы по умолчанию?

Sreehari 09.07.2019 09:45

@Sreehari, братан, ты видел класс DTO, я только что создал ключ идентификатора для сотрудника для сравнения, вы также можете сравнить, используя какой-либо другой параметр сотрудника

Manoj Perumarath 09.07.2019 09:48

:Спасибо за ответ! Ваша логика отлично работает без реализации ViewModel, если пользовательский интерфейс не слушает ViewModel. Несмотря на то, что логика фильтрует идеально, когда ViewModel приходит к picture , данные возвращаются обратно с данными из ViewModel. Поэтому я манипулировал ViewModel, чтобы иметь 2 набора. Первый отфильтрованный, второй оригинальный! Это работает как очарование. Даю +10 к вашему ведру за потрясающее объяснение.

Sreehari 09.07.2019 16:01

Другие вопросы по теме