SearchView 与 RecyclerView

SearchView with RecyclerView

一段时间以来,我一直在尝试将 SearchView 添加到我的 RecyclerView 中,并参考了这些帖子 , and 。在这一点上,我确信答案就在眼前。

但是,我不确定如何实现 SearchView,例如,使用以下内容:

private static final List<DataModel> getDummyData(){
    List<DataModel> dummyDataList = new ArrayList<>();

    dummyDataList.add(new DataModel("Alphabet", "Sub Alphabet"));
    dummyDataList.add(new DataModel("Banana", "Sub Banana"));
    dummyDataList.add(new DataModel("Captain", "Sub Captain"));
    dummyDataList.add(new DataModel("Donut", "Sub Donut"));
    dummyDataList.add(new DataModel("Elephant", "Sub Elephant"));
    dummyDataList.add(new DataModel("Fox", "Sub Fox"));
    dummyDataList.add(new DataModel("Giraffe", "Sub Giraffe"));
    dummyDataList.add(new DataModel("Hippo", "Sub Hippo"));
    dummyDataList.add(new DataModel("Iguana", "Sub Iguana"));
    dummyDataList.add(new DataModel("Jumanji", "Sub Jumanji"));

    return dummyDataList;
    }

这是我当前的设置 (following this tutorial),它使用 Locale.getISOCountries();并搜索国家名称。

MainActivityFragment:

public class MainActivityFragment extends Fragment implements    SearchView.OnQueryTextListener {

private RecyclerView mRecyclerView;
private List<DataModel> mDataModel;
private RVAdapter adapter;

public MainActivityFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.tab_one_fragment, container, false);

    mRecyclerView = (RecyclerView)view.findViewById(R.id.recyclerview);
    LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
    mRecyclerView.setLayoutManager(layoutManager);
    return view;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    setHasOptionsMenu(true);

    String[] locales = Locale.getISOCountries();

    mDataModel = new ArrayList<>();

    for (String countryCode : locales){
        Locale obj = new Locale("", countryCode);
        mDataModel.add(new DataModel(obj.getDisplayCountry(), obj.getISO3Country()));
    }
    adapter = new RVAdapter(mDataModel);
    mRecyclerView.setAdapter(adapter);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.menu_main, menu);

    final MenuItem item = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
    searchView.setOnQueryTextListener(this);

    MenuItemCompat.setOnActionExpandListener(item,
            new MenuItemCompat.OnActionExpandListener() {
                @Override
                public boolean onMenuItemActionExpand(MenuItem item) {
                    return true;
                }
                @Override
                public boolean onMenuItemActionCollapse(MenuItem item) {
                    // Do something when collapsed
                    adapter.setFilter(mDataModel);
                    return true; 
                }
            });
}

@Override
public boolean onQueryTextChange(String newText) {
    final List<DataModel> filteredModeList = filter(mDataModel, newText);
    adapter.setFilter(filteredModeList);
    return true;
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

private List<DataModel>filter(List<DataModel> models, String query){
    query = query.toLowerCase();

    final List<DataModel> filteredModeList = new ArrayList<>();
    for (DataModel model : models){
        final String text = model.getName().toLowerCase();
        if (text.contains(query)){
            filteredModeList.add(model);
        }
    }
    return filteredModeList;
    }
}

还有我的适配器-RVAdapter.java:

public class RVAdapter extends RecyclerView.Adapter<ItemViewHolder> {

private List<DataModel> mDataModel;

public RVAdapter(List<DataModel> mDataModel){
    this.mDataModel = mDataModel;
}

@Override
public void onBindViewHolder(ItemViewHolder itemViewHolder, int position) {
    final DataModel model = mDataModel.get(position);
    itemViewHolder.bind(model);
}

@Override
public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int position){
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_row, viewGroup, false);
    return new ItemViewHolder(view);
}

@Override
public int getItemCount() {
    return mDataModel.size();
}

public void setFilter(List<DataModel> dataModels){
    mDataModel = new ArrayList<>();
    mDataModel.addAll(dataModels);
    notifyDataSetChanged();
    }
}

这是ItemViewHolder.java:

public class ItemViewHolder extends RecyclerView.ViewHolder {

public TextView name_TextView;
public TextView subName_TextView;

public ItemViewHolder(View itemView){
    super(itemView);

    name_TextView = (TextView)itemView.findViewById(R.id.country_name);
    subName_TextView = (TextView)itemView.findViewById(R.id.country_iso);
}

public void bind(DataModel dataModel){
    name_TextView.setText(dataModel.getName());
    subName_TextView.setText(dataModel.getSubName());
    }
}

最后,DataModel.java:

public class DataModel {

String name;
String subName;

DataModel(String name, String subName){
    this.name = name;
    this.subName = subName;
}

public String getName(){
    return name;
}

public String getSubName(){
    return subName;
    }
}

任何帮助、建议或指向正确方向的观点都会很棒!

这受到以下启发post:

我需要一个更复杂的解决方案,因为 recyclerview 托管在片段中,所以我使用 EventBus 将搜索查询从托管搜索视图的 activity 传递到片段。

来自有权访问 SearchView 的 Activity

@Override
public boolean onQueryTextChange(String query) {
    EventBus.getDefault().post(new SearchEvent(query));
    return true;
}

在托管 recyclerview 的片段上:

public void onEvent(SearchEvent e) {
    final List<? extends GenericCompany> filteredModelList = filter(mCompanies, e.getSearchString());
    mListAdapter.replaceData(filteredModelList);
    mRecyclerView.scrollToPosition(0);
}


private List<? extends GenericCompany> filter(List<SCSponsors> models, String query) {
    query = query.toLowerCase();

    final List<SCSponsors> filteredModelList = new ArrayList<>();
    for (SCSponsors model : models) {
        final String text = model.getDisplayName().toLowerCase();
        if (text.contains(query)) {
            filteredModelList.add(model);
        }
    }
    return filteredModelList;
}

适配器内部:

    public void replaceData(List<? extends GenericCompany> companies) {
        setList(companies);
        notifyDataSetChanged();
    }

好吧,所以在仔细查看 post 之后(这个答案在很大程度上基于并且离不开它),我最终得出以下结论:

注意:我最初在一个有 TabLayout 的项目上对此进行了测试,因此有一些与选项卡相关的变量和 class 名称。此外,我所有的布局都与上面链接的 post 中的布局相同,但是将它与 TabLayout、CoordinatorLayout/AppBarLayout 等一起使用应该不难。

这里是MainFragment.java

public class MainFragment extends Fragment implements SearchView.OnQueryTextListener {

public static MainFragment newInstance() {
    return new MainFragment();
}

RecyclerView mRecyclerView;
private List<MemberData> tabListItem;
private RecyclerViewTabsAdapter mAdapter;


@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    final View view = inflater.inflate(R.layout.recyclerview, container, false);
    mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);

    return view;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    setHasOptionsMenu(true);

    mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
    mRecyclerView.setHasFixedSize(true);

    tabListItem = new ArrayList<>();

    tabListItem = getTabRowList();

    mAdapter = new RecyclerViewTabsAdapter(getActivity(), tabListItem);
    mRecyclerView.setAdapter(mAdapter);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.menu_main, menu);
    final MenuItem item = menu.findItem(R.id.action_search);
    final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
    searchView.setOnQueryTextListener(this);
}

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

@Override
public boolean onQueryTextChange(String newText) {
    final List<MemberData> filteredModelList = filter(tabListItem, newText);
    mAdapter.animateTo(filteredModelList);
    mRecyclerView.scrollToPosition(0);

    return true;
}

private List<MemberData> filter(List<MemberData> datas, String newText) {
    newText = newText.toLowerCase();

    final List<MemberData> filteredModelList = new ArrayList<>();
    for (MemberData data : datas) {
        final String text = data.getName().toLowerCase();
        if (text.contains(newText)) {
            filteredModelList.add(data);
        }
    }
    return filteredModelList;
}

private List<MemberData> getTabRowList() {
    List<MemberData> currentItem = new ArrayList<>();
    currentItem.add(new MemberData("Albert", "Albert Sub", R.drawable.your_drawable));
    currentItem.add(new MemberData("Abby", "Abby Text", R.drawable.your_drawable));
    currentItem.add(new MemberData("Brian", "Brian Text", R.drawable.your_drawable));
    currentItem.add(new MemberData("Chris", "Chris Text", R.drawable.your_drawable));
    currentItem.add(new MemberData("Dante", "Dante Text", R.drawable.your_drawable));

//Add more of your dummy data here


        return currentItem;
    }
}

适配器

public class RecyclerViewTabsAdapter extends RecyclerView.Adapter<RecyclerViewTabsViewHolder> {

private final LayoutInflater mInflater;
private final List<MemberData>mItemList;

public RecyclerViewTabsAdapter(Context context, List<MemberData> itemList) {
    mInflater = LayoutInflater.from(context);
    mItemList = new ArrayList<>(itemList);

}

@Override
public RecyclerViewTabsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    final View view = mInflater.inflate(R.layout.cardview_detail, parent, false);
    return new RecyclerViewTabsViewHolder(view);
}

@Override
public void onBindViewHolder(final RecyclerViewTabsViewHolder holder, final int position) {
    final MemberData data = mItemList.get(position);
    holder.bind(data);
    //TODO: Set onClick here

}

@Override
public int getItemCount() {
    return this.mItemList.size();

}

public void animateTo(List<MemberData> memberDatas) {
    applyAndAnimateRemovals(memberDatas);
    applyAndAnimateAdditions(memberDatas);
    applyAndAnimateMovedItems(memberDatas);
}

private void applyAndAnimateRemovals(List<MemberData> newDatas) {
    for (int i = mItemList.size() - 1; i >= 0; i--) {
        final MemberData data = mItemList.get(i);
        if (!newDatas.contains(data)) {
            removeItem(i);
        }
    }
}

private void applyAndAnimateAdditions(List<MemberData> newDatas) {
    for (int i = 0, count = newDatas.size(); i < count; i++) {
        final MemberData data = newDatas.get(i);
        if (!mItemList.contains(data)) {
            addItem(i, data);
        }
    }
}

private void applyAndAnimateMovedItems(List<MemberData> newDatas) {
    for (int toPosition = newDatas.size() - 1; toPosition >= 0; toPosition--) {
        final MemberData data = newDatas.get(toPosition);
        final int fromPosition = mItemList.indexOf(data);
        if (fromPosition >= 0 && fromPosition != toPosition) {
            moveItem(fromPosition, toPosition);
        }
    }
}

public MemberData removeItem(int position) {
    final MemberData data = mItemList.remove(position);
    notifyItemRemoved(position);
    return data;
}

public void addItem(int position, MemberData data) {
    mItemList.add(position, data);
    notifyItemInserted(position);
}

public void moveItem(int fromPosition, int toPosition) {
    final MemberData data = mItemList.remove(fromPosition);
    mItemList.add(toPosition, data);
    notifyItemMoved(fromPosition, toPosition);
    }
}

ViewHolder

public class RecyclerViewTabsViewHolder extends RecyclerView.ViewHolder {

CardView cv;
TextView name;
TextView subText;
ImageView memberPhoto;

public RecyclerViewTabsViewHolder(View itemView){
    super(itemView);
    cv = (CardView)itemView.findViewById(R.id.cv);
    name = (TextView)itemView.findViewById(R.id.person_name);
    subText = (TextView)itemView.findViewById(R.id.person_subtext);
    memberPhoto = (ImageView)itemView.findViewById(R.id.person_photo);
}

public void bind(MemberData data){
    name.setText(data.getName());
    subText.setText(data.getSubText());
    memberPhoto.setImageResource(data.getPhotoId());
    }
}

最后,MemberData.java

public class MemberData {

String name;
String subText;
int photoId;

public MemberData(String name, String subText, int photoId){
    this.name = name;
    this.subText = subText;
    this.photoId = photoId;
}

public String getName(){
    return name;
}

public String getSubText() {
    return subText;
}

public int getPhotoId() {
    return photoId;
    }
}

唯一剩下的问题(我会把它留到另一个 post),是 onClick 方法和在过滤列表后获得正确的位置。

希望对您有所帮助!

在您的适配器中 class 实现 filterable 并覆盖它的方法。

    @Override
public Filter getFilter() {

    Filter filter = new Filter() {
        @Override
        protected FilterResults performFiltering(CharSequence charSequence) {
            FilterResults filterResults = new FilterResults();

            if(charSequence == null | charSequence.length() == 0){
                filterResults.count = getUserModelListFiltered.size();
                filterResults.values = getUserModelListFiltered;

            }else{
                String searchChr = charSequence.toString().toLowerCase();

                List<UserModel> resultData = new ArrayList<>();

                for(UserModel userModel: getUserModelListFiltered){
                    if(userModel.getUserName().toLowerCase().contains(searchChr)){
                        resultData.add(userModel);
                    }
                }
                filterResults.count = resultData.size();
                filterResults.values = resultData;

            }

            return filterResults;
        }

        @Override
        protected void publishResults(CharSequence charSequence, FilterResults filterResults) {

            userModelList = (List<UserModel>) filterResults.values;
            notifyDataSetChanged();

        }
    };
    return filter;
}

在您的 activity 中创建搜索视图并监听 setOnQueryTextListener。

 @Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);

    MenuItem menuItem = menu.findItem(R.id.search_view);

    SearchView searchView = (SearchView) menuItem.getActionView();

    searchView.setMaxWidth(Integer.MAX_VALUE);

    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {

            usersAdapter.getFilter().filter(newText);
            return true;
        }
    });



    return  true;
}

完整教程和源代码: Recyclerview with search view