使用SearchView使用LiveData内容过滤RecyclerView的列表 [英] Filtering RecyclerView's list with LiveData content using SearchView

查看:92
本文介绍了使用SearchView使用LiveData内容过滤RecyclerView的列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了RecyclerView,其中包含简单的单词列表(GroupVc对象的字符串名称).由于列表可能很长,我想使用工具栏中的SearchView使其可过滤. 我的应用程序的体系结构基于Android体系结构组件,其中所有GroupVc对象都存在于Room数据库中,并且可以通过ViewModel和Repository对象提供从UI访问数据库的功能.我正在将包含在LiveData中的所有GroupsVc的列表归档到RecyclerView中,以使其保持更新. 我的问题是我不知道如何使RecyclerView可过滤.我试图通过在适配器中实现Filterable Interface来做到这一点:

I've created RecyclerView which contains simple list of words (String names of GroupVc objects). As list can be very long I want to make it filterable with SearchView in Toolbar. Architecture of my application is based on Android Architecture Components, where all GroupVc objects exist in Room database and access to DataBase from UI is provided through ViewModel and Repository objects. I'm filing my RecyclerView with list of all GroupsVc wrapped in LiveData to keep it updated. My problem is that I don't know how to make RecyclerView filterable. I tried to do it with implementing of Filterable Interface in adapter:

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

然后在活动"中我编写了一个方法:

And then in Activity I've written a method:

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

推荐答案

删除过滤器时无法正确还原数据的问题是因为发布过滤器结果时覆盖了数据列表.

The problem that your data is not restored correctly when you delete the filter is, because you overwrite your data list when you publish the filter result.

这是解决方案:

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

If you set a new list of data to the adapter, you have to call adapter.getFilter().filter(text); or you save the last filter string inside your adapter and call your filter inside setGroupsVc()

注意: 如果使用notifyDataSetChanged();,则没有任何动画.如果要制作动画,请使用其他通知方法.

Note: You don't have any animations if you use notifyDataSetChanged();. If you want to have animations, use the other notify methods.

希望这会有所帮助.

编辑

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

Also update onBindViewHolder() to get the data from your filtered list.

@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(),则为适配器设置一个新列表.请记住,您总是从过滤列表中获取数据.因此,您必须使用设置为适配器(新数据)的新列表来更新过滤后的列表(旧数据).

Now you always get the data from the filtered list. The initial state is that your lists is empty. That's what you saw in your RecyclerView. If you call setGroupsVc() you set a new list to the adapter. Remember you always get the data from the filtered list. So you have to update the filtered list (old data) with the new list which you set to the adapter (new data).

您有2个选择:

  1. 致电setGroupsVc()后,在外部致电过滤器:
  1. Call filter on the outside after you call setGroupsVc():

喜欢

...
setGroupsVc(yourNewList);
adapter.getFilter().filter(searchview.getQuery());
...

  1. 将最后一个过滤器保存在适配器中,然后在setGroupsVc()内调用过滤器:
  1. Save the last filter in your adapter and call filter inside your setGroupsVc():

在适配器中添加新字段lastFilter

Add new field lastFilter in your adapter

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
}

这篇关于使用SearchView使用LiveData内容过滤RecyclerView的列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆