【问题标题】:How to highlight filtered text in RecyclerView when using SearchView widget使用 SearchView 小部件时如何在 RecyclerView 中突出显示过滤后的文本
【发布时间】:2017-03-08 11:38:13
【问题描述】:

如何在 RecyclerView 中突出显示搜索文本结果。 我发现了一些关于 Spannable TextView 的帖子,但不确定在我的情况下在哪里实施。 感谢您的关注和帮助。

MainActivity 或 Chapter1

 public class Chapter1 extends AppCompatActivity implements SearchView.OnQueryTextListener {
        MyRecAdapter myRecAdapter;
        RecyclerView recyclerView;
        List<Post> list;        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(layout.chapter1_main);
            recyclerView = (RecyclerView) findViewById(R.id.myrec);
            createdata();
            myRecAdapter = new MyRecAdapter(list, Chapter1.this);
            recyclerView.setLayoutManager(new LinearLayoutManager(Chapter1.this));
            recyclerView.setAdapter(myRecAdapter);
        }        
        void createdata() {             
            list = new ArrayList<>();               
            String topic_1_1 = getResources().getString(string.topic_1_1);
            String text_1_1 = getString(string.text_1_1);
            String topic_1_2 = getResources().getString(string.topic_1_2);
            String topic_1_3 = getResources().getString(string.topic_1_3);
            String text_1_3 = getString(string.text_1_3);         
            list.add(new Post(topic_1_1, text_1_1));
            list.add(new Post(topic_1_2, ""));
            list.add(new Post(topic_1_3, text_1_3));               

        }       

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.menu_search, menu);
            getMenuInflater().inflate(R.menu.main, menu);        
            MenuItem item = menu.findItem(R.id.menu_search);
            SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);        
            searchView.setOnQueryTextListener(this);        
            return super.onCreateOptionsMenu(menu);
        }     


        @Override
        public boolean onQueryTextChange(String newText) {        

            final List<Post> filteredModelList = filter(list, newText);        
            if (filteredModelList.size() > 0) {        
                myRecAdapter.setFilter(filteredModelList);
                return true;
            } else {
                Toast.makeText(Chapter1.this, "Not Found", Toast.LENGTH_SHORT).show();
                return false;
            }
        }        
        private List<Post> filter(List<Post> models, String query ) {
            query = query.toLowerCase();        
            final List<Post> filteredModelList = new ArrayList<>();
            for (Post model : models) {        
                final String text = model.getPostTitle().toLowerCase();
                final String text_sub = model.getPostSubTitle().toLowerCase();        
                if (text.contains(query)) {
                    filteredModelList.add(model);

                }
                else {
                    if (text_sub.contains(query)) {
                        filteredModelList.add(model);
                    }        
                }     
            }
            createdata();
            myRecAdapter = new MyRecAdapter(filteredModelList, Chapter1.this);
            recyclerView.setLayoutManager(new LinearLayoutManager(Chapter1.this));
            recyclerView.setAdapter(myRecAdapter);
            myRecAdapter.notifyDataSetChanged();
            return filteredModelList;
        }
    }

适配器

public class MyRecAdapter extends RecyclerView.Adapter<MyRecAdapter.VH> {
        public List<Post> parkingList;        
        public Context context;
        ArrayList<Post> mCountryModel;        
        public MyRecAdapter(List<Post> parkingList, Context context) {
            this.parkingList = parkingList;
            this.context = context;
        }        
        @Override
        public MyRecAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
            return new MyRecAdapter.VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.mycardview, parent, false));
        }        
        @Override
        public void onBindViewHolder(MyRecAdapter.VH holder, int position) {       
                holder.t1.setText(Html.fromHtml(parkingList.get(position).getPostTitle()));                    holder.t2.setText(Html.fromHtml(parkingList.get(position).getPostSubTitle()));        
        }        
        @Override
        public int getItemCount() {
            return parkingList.size();
        }        
        public class VH extends RecyclerView.ViewHolder {
            TextView t1, t2;        
            public VH(View view) {
                super(view);        
                t1 = (TextView) view.findViewById(R.id.list_title);
                t2 = (TextView) view.findViewById(R.id.list_desc);        
            }        
        }          
        public void setFilter(List<Post> countryModels) {
            mCountryModel = new ArrayList<>();
            mCountryModel.addAll(countryModels);        
            notifyDataSetChanged();        
        }

    }

【问题讨论】:

  • 您要突出显示哪个字段的标题或描述?
  • @HRaval 我想突出显示标题和描述
  • 据了解您的问题...您已根据查询文本归档列表,现在您只显示过滤结果对吗?那你为什么要突出显示?
  • @HRaval 是的,正确,根据查询文本进行过滤,但在许多情况下,TextView 中有超过 500 个世界(主要在 Desc 中),并且列表非常宽,即使在过滤后也是如此。突出显示将大大改善设计视图...

标签: android android-recyclerview android-adapter searchview


【解决方案1】:

将您的适配器更改为

public class MyRecAdapter extends RecyclerView.Adapter<MyRecAdapter.VH> {
        public List<Post> parkingList;        
        public Context context;
        ArrayList<Post> mCountryModel;  
        String searchText;
        
        public MyRecAdapter(List<Post> parkingList, Context context) {
            this.parkingList = parkingList;
            this.context = context;
        }        
        @Override
        public MyRecAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {
            return new MyRecAdapter.VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.mycardview, parent, false));
        }        
        @Override
        public void onBindViewHolder(MyRecAdapter.VH holder, int position) {       
                String title = parkingList.get(position).getPostTitle();
                String desc = parkingList.get(position).getPostSubTitle();
                
                holder.t1.setText(Html.fromHtml(title));                    
                if(searchText.length()>0){
                    //color your text here
                    int index = desc.indexOf(searchText);
                    while(index>0){
                       SpannableStringBuilder sb = new SpannableStringBuilder(desc);
                       ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); //specify color here
                       sb.setSpan(fcs, index, index+searchText.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); 
                        index = desc.indexOf(searchText,index+1);
                       
                    }
                    holder.t2.setText(sb);  
                    
                }else{
                holder.t2.setText(Html.fromHtml(desc));  
                }
                      
        }        
        @Override
        public int getItemCount() {
            return parkingList.size();
        }        
        public class VH extends RecyclerView.ViewHolder {
            TextView t1, t2;        
            public VH(View view) {
                super(view);        
                t1 = (TextView) view.findViewById(R.id.list_title);
                t2 = (TextView) view.findViewById(R.id.list_desc);        
            }        
        }          
        public void setFilter(List<Post> countryModels,String searchText) {
            mCountryModel = new ArrayList<>();
            mCountryModel.addAll(countryModels);        
            this.searchText = searchText;
            notifyDataSetChanged();        
        }

    }

并将 onQueryTextChange 设置为

 @Override
        public boolean onQueryTextChange(String newText) {        

            final List<Post> filteredModelList = filter(list, newText);        
            if (filteredModelList.size() > 0) {        
                myRecAdapter.setFilter(filteredModelList,newText);
                return true;
            } else {
                Toast.makeText(Chapter1.this, "Not Found", Toast.LENGTH_SHORT).show();
                return false;
            }
        }     

【讨论】:

  • @H Raval 为什么在 setSpan() 中使用“searchText.length()”?为什么在 index = desc.indexOf... 中使用“index+1”?
  • length 用于从索引到长度和 inxex+1 为下一次匹配的单词着色
【解决方案2】:
/**
 * Kotlin Code
 * highlight the backgroundText
 * searchText -> pass the hightlight that appear in background
 * name -> actual string name
 * textView -> append the data to widget.
 */
private fun highlightText(
    searchText: String,
    name: String,
    textView: TextView
) {
    if (searchText.isNotEmpty() && name.contains(searchText)) {
        val sb = SpannableStringBuilder(name)
        var index: Int = name.lowercase().indexOf(searchText.lowercase())
        while (index >= 0 && index < name.length) {
            val fcs = BackgroundColorSpan(Color.rgb(0, 200, 200)) //specify color here
            sb.setSpan(
                fcs,
                index,
                index + searchText.length,
                Spannable.SPAN_INCLUSIVE_INCLUSIVE
            )
            index = name.indexOf(searchText, index + 1)
        }
        textView.text = sb
    } else {
        textView.text = name
    }
}

【讨论】:

  • 请不要只发布代码作为答案,还要解释您的代码的作用以及它如何解决问题的问题。带有解释的答案通常更有帮助、质量更好,并且更有可能吸引投票。
【解决方案3】:

在Adapter类的Bind view holder里面使用

String title1 = Itemlist.get(position).getN_stall_name().toLowerCase(Locale.getDefault());
            holder.title.setText(Itemlist.get(position).getN_stall_name());

if (title1.contains(searchText)) {
                int startPos = title1.indexOf(searchText);
                int endPos = startPos + searchText.length();
                Spannable spanString = Spannable.Factory.getInstance().newSpannable(holder.title.getText());
                spanString.setSpan(new ForegroundColorSpan(Color.RED), startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                holder.title.setText(spanString);
            }

现在在 Adapter 类中创建公共方法

 public void updateList(List<GetShop> list, String searchText) {
            Itemlist = new ArrayList<>();
            Itemlist.addAll(list);
            this.searchText = searchText;
            notifyDataSetChanged();
        }

在您使用搜索视图的 Activity 中使用搜索视图侦听器

SearchView searchview=findviewbyid(R.id.searchview);
    searchview.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    return false;
                }
                @Override
                public boolean onQueryTextChange(String newText) {
                    newText = newText.toLowerCase();
                    ArrayList<GetShop> newList = new ArrayList<>();
                    for (GetShop userInfo : items) {
                        String type = userInfo.getN_stall_name().toLowerCase();
                        if (type.contains(newText)) {
                            newList.add(userInfo);
                        }
                    }
                    disp_adapter.updateList(newList, newText);
                    return true;
                }
            });

【讨论】:

    【解决方案4】:

    根据接受的答案 indexOf() 在字符串值为“Mumbai”的情况下不起作用,在字符串中出现相同的情况。所以在这里我使用了“Pattern”和“Matcher”类来使其工作。 此外,我还添加了“.toLowerCase()”以使字符串具有相同的大小写,并将字符串作为忽略大小写进行查询。 如果您不需要忽略查询字符串的大小写,您可以从此代码 sn-p 中删除 .toLowerCase()

      SpannableStringBuilder sb = new SpannableStringBuilder(desc);
            Pattern word = Pattern.compile(query.toLowerCase());
            Matcher match = word.matcher(desc.toLowerCase());
    
            while (match.find()) {
                ForegroundColorSpan fcs = new ForegroundColorSpan(
                        ContextCompat.getColor(context, R.color.colorPrimary)); //specify color here
                    sb.setSpan(fcs, match.start(), match.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
            }
            viewHolders.text_view_title.setText(sb);
    

    【讨论】:

      【解决方案5】:
              while(index>**-1**){
                         SpannableStringBuilder sb = new SpannableStringBuilder(desc);
                         ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); //specify color here
                         sb.setSpan(fcs, index, **index** + searchText.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); 
                          index = desc.indexOf(searchText,index+1);
      
                      }
      

      对上一个答案的一点补充,你应该检查 index >-1 ,否则第一个文本不会被突出显示。 对于 setSpan ,int end 应该是您的 index + searchText.length()。

      【讨论】:

      • “index + searchText.length()”对“int end”有什么作用?
      • int end 是高亮文本停止的索引(位置)。您需要获取要突出显示的文本的起始索引并添加文本的长度。
      • 啊,现在我明白了。干杯!
      猜你喜欢
      • 1970-01-01
      • 2021-10-21
      • 1970-01-01
      • 2021-01-29
      • 1970-01-01
      • 2015-08-04
      • 2022-08-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多