使用 SearchView 使用 LiveData 内容筛选回收器视图的列表



我已经创建了RecyClerview,其中包含单词的简单列表(groupVc对象的字符串名称(。由于列表可能很长,因此我想在工具栏中使用搜索视图使其可过滤。我的应用程序的体系结构基于Android体系结构组件,其中所有GroupVC对象都存在于房间数据库中,并且通过ViewModel和Repository对象提供了从UI访问数据库的访问。我正在提交Recyclerview,其中包含在Livedata中包装的所有组VC的列表,以使其更新。我的问题是我不知道如何使RecyClerview可过滤。我尝试通过在适配器中实现可过滤接口来做到这一点:

public class GroupsVcAdapter extends
    RecyclerView.Adapter<GroupsViewHolder> implements Filterable{
private LayoutInflater mInflater;
private List<GroupVc> mGroupsVc;
private List<GroupVc> filteredGroupsVc;
private OnItemClicked onClick;
public GroupsVcAdapter(Context context, OnItemClicked onClick) {
    mInflater = LayoutInflater.from(context);
    this.onClick = onClick;
}
public List<GroupVc> getmGroupsVc() {
    return mGroupsVc;
}
@Override
public GroupsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = mInflater.inflate(R.layout.layout_list_of_groups, parent, false);
    return new GroupsViewHolder(itemView, onClick);
}
@Override
public void onBindViewHolder(final GroupsViewHolder holder, int position) {
    if (mGroupsVc != null) {
        GroupVc current = mGroupsVc.get(position);
        holder.getNameView().setText(current.getNameGroup());
    } else {
        holder.getNameView().setText(R.string.nogroups);
    }
}
public void setGroupsVc(List<GroupVc> mGroupsVc) {
    this.mGroupsVc = mGroupsVc;
    notifyDataSetChanged();
}
@Override
public int getItemCount() {
    if (mGroupsVc != null)
        return mGroupsVc.size();
    else return 0;
}
@Override
public Filter getFilter() {
    return new Filter() {
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
                mGroupsVc = (List<GroupVc>) results.values;
                notifyDataSetChanged();
        }
        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            filteredGroupsVc = null;
            if (constraint.length() == 0) {
                filteredGroupsVc = mGroupsVc;
            } else {
                filteredGroupsVc = getFilteredResults(constraint.toString().toLowerCase());
            }
            FilterResults results = new FilterResults();
            results.values = filteredGroupsVc;
            return results;
        }
    };
}
protected List<GroupVc> getFilteredResults(String constraint) {
    List<GroupVc> results = new ArrayList<>();
    for (GroupVc item : mGroupsVc) {
        if (item.getNameGroup().toLowerCase().contains(constraint)) {
            results.add(item);
        }
    }
    return results;
}
}

然后在活动中我写了一种方法:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_activity_words, menu);
    // Associate searchable configuration with the SearchView
    SearchManager searchManager =
            (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView =
            (SearchView) menu.findItem(R.id.action_search).getActionView();
    searchView.setSearchableInfo(
            searchManager.getSearchableInfo(getComponentName()));
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String text) {
            return false;
        }
        @Override
        public boolean onQueryTextChange(String text) {
            adapter.getFilter().filter(text);
            return true;
        }
    });
    return true;
}
The result was that my RecyclerView got filtered correctly but it wasn't restored after I closed SearchView. The only way to do it is to reload activity. **So I'm interested in How can I restore RecyclerView's list and can I use for filtering any LiveData's capacities?** Below I post the complete code of Activity, ViewModel and Repository:

活动

public class GroupsActivity extends AppCompatActivity {
private static final String DELETE_DIALOG = "Delete dialog";
private static final String EDIT_DIALOG = "Edit dialog";
private RecyclerView mRecyclerView;
private GroupsVcAdapter adapter;
private GroupsViewModel mGroupsViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_groups);
    //Creating of toolbar with title Groups
    Toolbar myToolbar = findViewById(R.id.toolbar_groups);
    setSupportActionBar(myToolbar);
    //Enable Up Button
    ActionBar ab = getSupportActionBar();
    ab.setDisplayHomeAsUpEnabled(true);
    //RecyclerView containing the list of groups with sound icons
    mRecyclerView = initRecyclerView();
    //Using ViewModel to observe GroupVc data
    mGroupsViewModel = ViewModelProviders.of(this).get(GroupsViewModel.class);
    mGroupsViewModel.getAllGroups().observe(this, new Observer<List<GroupVc>>() {
        @Override
        public void onChanged(@Nullable List<GroupVc> groupVcs) {
            adapter.setGroupsVc(groupVcs);
        }
    });
}
private RecyclerView initRecyclerView() {
    RecyclerView recyclerView = findViewById(R.id.groups_recycler_view);
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(layoutManager);
    OnItemClicked listener = (v, position) -> {
        switch (v.getId()) {
            case R.id.delete_group:
                Log.i(DELETE_DIALOG, "Delete button");
                break;
            case R.id.edit_group:
                Log.i(EDIT_DIALOG, "Edit button");
                break;
            case R.id.surface:
                Log.i(SURFACE, "Surface button");
                break;
        }
    };
    adapter = new GroupsVcAdapter(this, listener);
    recyclerView.setAdapter(adapter);
    return recyclerView;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_activity_words, menu);
    // Associate searchable configuration with the SearchView
    SearchManager searchManager =
            (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView =
            (SearchView) menu.findItem(R.id.action_search).getActionView();
    searchView.setSearchableInfo(
            searchManager.getSearchableInfo(getComponentName()));
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String text) {
            return false;
        }
        @Override
        public boolean onQueryTextChange(String text) {
            adapter.getFilter().filter(text);
            return true;
        }
    });
    return true;
}
}

ViewModel

public class GroupsViewModel extends AndroidViewModel {
private LiveData<List<GroupVc>> mAllGroups;
private GroupRepository mRepository;
public GroupsViewModel(@NonNull Application application) {
    super(application);
    mRepository = new GroupRepository(application);
    mAllGroups = mRepository.getAllGroupVc();
}
public LiveData<List<GroupVc>> getAllGroups() {
    return mAllGroups;
}
}

存储库

public class GroupRepository {
private LiveData<List<GroupVc>> mAllGroups;
private GroupVcDao mGroupVcDao;
public GroupRepository(Application application) {
    AppDatabase db = AppDatabase.getInstance(application);
    mGroupVcDao = db.groupVcDao();
    mAllGroups = mGroupVcDao.getAllGroupVc();
}
public LiveData<List<GroupVc>> getAllGroupVc() {
    return mAllGroups;
}
}

删除过滤器时数据未正确恢复的问题,因为发布过滤器结果时覆盖数据列表。

这是解决方案:

public GroupsVcAdapter(Context context, OnItemClicked onClick) {
  mInflater = LayoutInflater.from(context);
  this.onClick = onClick;
  // init the lists
  mGroupsVc = new ArrayList<>();
  filteredGroupsVc = new ArrayList<>();
}
public List<GroupVc> getFilteredGroupsVc() {
  return filteredGroupsVc;
}
@Override
public int getItemCount() {
  return filteredGroupsVc != null ? filteredGroupsVc.size() : 0;
}
@Override
public Filter getFilter() {
  return new Filter() {
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
      filteredGroupsVc = (List<GroupVc>) results.values;
      notifyDataSetChanged();
    }
    @Override
    protected FilterResults performFiltering(CharSequence constraint) {
      filteredGroupsVc.clear();
      if (constraint.length() == 0) {
        filteredGroupsVc.addAll(mGroupsVc);
      else {
        filteredGroupsVc = getFilteredResults(constraint.toString().toLowerCase());
      }
      FilterResults results = new FilterResults();
      results.values = filteredGroupsVc;
      results.count = filteredGroupsVc.size();
      return results;
    }
  };
}

如果将新的数据列表设置为适配器,则必须调用adapter.getFilter().filter(text);或将最后一个过滤器字符串保存在适配器中,然后在setGroupsVc()

中调用过滤器

注意:如果使用notifyDataSetChanged();,则没有任何动画。如果您想拥有动画,请使用其他通知方法。

希望这会有所帮助。

编辑

还更新onBindViewHolder()以从您的过滤列表中获取数据。

@Override
public void onBindViewHolder(final GroupsViewHolder holder, int position) {
  if (filteredGroupsVc != null) {
    GroupVc current = filteredGroupsVc.get(position);
    holder.getNameView().setText(current.getNameGroup());
  } else {
    holder.getNameView().setText(R.string.nogroups);
  }
}

现在,您始终从过滤列表中获取数据。最初的状态是您的列表为空。这就是您在RecyclerView中看到的。如果您致电setGroupsVc(),则将新列表设置为适配器。请记住,您总是从过滤列表中获取数据。因此,您必须使用设置为适配器(新数据(的新列表更新过滤列表(旧数据(。

您有2个选项:

  1. 呼叫setGroupsVc()后,在外部呼叫过滤器:

喜欢

...
setGroupsVc(yourNewList);
adapter.getFilter().filter(searchview.getQuery());
...
  1. 将最后一个过滤器保存在适配器中,并在setGroupsVc()中调用过滤器:

在适配器中添加新字段lastFilter

private String lastFilter = "";

performFiltering()保存过滤器字符串

lastFilter = contraint.toString();

至少在setGroupsVc()中调用过滤器

public void setGroupsVc(List<GroupVc> mGroupsVc) {
  this.mGroupsVc = mGroupsVc;
  getFilter().filter(lastFilter);
  // notifyDataSetChanged(); -> not necessary anymore because we already call this inside our filter
}

最新更新