【问题标题】:how do I highlight the searched text in my search filter?如何在搜索过滤器中突出显示搜索到的文本?
【发布时间】:2016-01-29 19:27:12
【问题描述】:

我正在尝试进行搜索,以便突出显示所有“可见”的搜索字母。我尝试使用 spannable 但这并没有成功,也许我做得不对?基于此:Highlight searched text in ListView items 如何突出显示可见文本?这是我的过滤器:

private LayoutInflater mInflater;

        private ValueFilter valueFilter;

        public MySimpleArrayAdapter(Activity context) {

            this.context = context;
            mInflater = LayoutInflater.from(context);

        }
        private class ValueFilter extends Filter {


            //Invoked in a worker thread to filter the data according to the constraint.
            @Override
            protected synchronized FilterResults performFiltering(CharSequence constraint) {

                FilterResults results = new FilterResults();

                if (constraint != null && constraint.length() > 0) {

                    ArrayList<Integer> filterList = new ArrayList<>();

                    int iCnt = listItemsHolder.Names.size();
                    for (int i = 0; i < iCnt; i++) {
                        if(listItemsHolder.Types.get(i).toString().indexOf("HEADER_")>-1){
                            continue;
                        }
                        if (listItemsHolder.Names.get(i).matches(getRegEx(constraint))||(listItemsHolder.Names.get(i).toLowerCase().contains(constraint.toString().toLowerCase()))) {
                            if(filterList.contains(i))
                                continue;

                            filterList.add(i);

                        }
                        }

                    results.count = filterList.size();

                    results.values = filterList;
                }else {
                String prefixString = getRegEx(constraint);
                mSearchText = prefixString;
                    results.count = listItemsHolder.Names.size();

                    ArrayList<Integer> tList = new ArrayList<>();
                    for(int i=0;i<results.count;i++){
                        tList.add(i);
                    }

                    results.values = tList;

                }

                return results;


}


                //Invoked in the UI thread to publish the filtering results in the user interface.
                @SuppressWarnings("unchecked")
                @Override
                protected void publishResults(CharSequence constraint, FilterResults results) {
                    ArrayList<Integer> resultsList = (ArrayList<Integer>)results.values;
                    if(resultsList != null) {
                        m_filterList = resultsList;
                    }
                    notifyDataSetChanged();
                }

            }

            public String getRegEx(CharSequence elements){
                String result = "(?i).*";
                for(String element : elements.toString().split("\\s")){
                    result += element + ".*";
                }
                result += ".*";
                return result;
            }

Thanks in advance! 

这是我的getview

@Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View rowView = convertView;
            ViewHolder holder;
            if(filtering && m_filterList != null && m_filterList.size() > position)
                position = m_filterList.get(position);

            if (rowView == null) {
                holder = new ViewHolder();

                mInflater = context.getLayoutInflater();
                rowView = mInflater.inflate(R.layout.rowlayout, null);
                // configure view holder
                holder.text = (TextView) rowView.findViewById(R.id.label);
                holder.text.setTextColor(Color.WHITE);
                holder.text.setSingleLine();
                holder.text.setTextSize(15);
                holder.text.setEllipsize(TextUtils.TruncateAt.END);
                holder.text.setPadding(2, 2, 6, 2);
                Typeface label = Typeface.createFromAsset(holder.text.getContext().getAssets(),
                        "fonts/arial-bold.ttf");
                holder.text.setTypeface(label);
                holder.image = (ImageView) rowView.findViewById(R.id.icon);
                holder.image.setPadding(6, 4, 0, 4);
                holder.image.getLayoutParams().height = (int) getResources().getDimension(R.dimen.icon_width_height);
                holder.image.getLayoutParams().width = (int) getResources().getDimension(R.dimen.icon_width_height);
                rowView.setBackgroundResource(R.drawable.row_border);
                rowView.setPadding(2, 2, 6, 2);
                rowView.setTag(holder);
            }else {

                // fill data
                holder = (ViewHolder) rowView.getTag();
            }

            String id  = listItemsHolder.getid(position);
            String name = listItemsHolder.getName(position);
            holder.image.setVisibility(View.VISIBLE);


            if (name != null) {
                holder.text.setText(listItemsHolder.getName(position));
                ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) holder.text.getLayoutParams();
                params.leftMargin = 20;
            }else{
                holder.text.setText(id);
            }
            String fullText = listItemsHolder.getName(position);
            // highlight search text
            if (mSearchText != null && !mSearchText.isEmpty()) {
                int startPos = fullText.toLowerCase(Locale.US).indexOf(mSearchText.toLowerCase(Locale.US));
                int endPos = startPos + mSearchText.length();
                if (startPos != -1) {
                    Spannable spannable = new SpannableString(fullText);
                    ColorStateList blueColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{Color.BLUE});
                    TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, blueColor, null);
                    spannable.setSpan(highlightSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    holder.text.setText(spannable);
                } else {
                    holder.text.setText(fullText);
                }
            } else {
                holder.text.setText(fullText);
            }
            return rowView;
        }

【问题讨论】:

    标签: android android-layout android-fragments textview android-search


    【解决方案1】:

    这只是高亮文本的演示,你可以通过调用实现你自己 highlight(searchText, originalText) 在过滤器中,

    public class MainActivity extends AppCompatActivity {
    EditText editText;
    TextView text;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.editText);
        text = (TextView) findViewById(R.id.textView1);
    
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
            }
    
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                    text.setText(highlight(editText.getText().toString(), text.getText().toString()));
            }
    
            @Override
            public void afterTextChanged(Editable s) {
    
            }
        });
    
    }
    
    public static CharSequence highlight(String search, String originalText) {
        String normalizedText = Normalizer.normalize(originalText, Normalizer.Form.NFD).replaceAll("\\p{InCombiningDiacriticalMarks}+", "").toLowerCase();
        int start = normalizedText.indexOf(search);
        if (start <= 0) {
            return originalText;
        } else {
            Spannable highlighted = new SpannableString(originalText);
            while (start > 0) {
                int spanStart = Math.min(start, originalText.length());
                int spanEnd = Math.min(start + search.length(), originalText.length());
                highlighted.setSpan(new BackgroundColorSpan(Color.YELLOW), spanStart, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                start = normalizedText.indexOf(search, spanEnd);
            }
            return highlighted;
        }
     }
    }
    

    【讨论】:

    • 冻结键盘,还有其他方法可以做到这一点
    【解决方案2】:

    在 getview 中设置文本之前添加此代码

    Spannable wordtoSpan = new SpannableString("Your_text_in_getviews");
    
            wordtoSpan.setSpan(new ForegroundColorSpan(Color.RED), 0, edtFilter
                    .getText().toString().length(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    txt_contact.setText(wordtoSpan);
    

    【讨论】:

    • 你在使用 ArrayAdapter 吗?对于上述代码,您需要自定义您的适配器。
    【解决方案3】:

    您好,在您的适配器类上,制作一个可扩展的文本并将其设置为您的文本视图,您可以使用以下代码作为参考。

     if ("text contains filter value".toLowerCase().contains("filter".toLowerCase())) {
            Spannable spanText = Spannable.Factory.getInstance().newSpannable("text contains filter value".toLowerCase());
    
            Matcher matcher = Pattern.compile("filter".toLowerCase())
                    .matcher("text contains filter value".toLowerCase());
            while (matcher.find()) {
                spanText.setSpan(new ForegroundColorSpan(Color.RED), matcher.start(),
                        matcher.start() + "filter".length(),
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
           yourTextView.setText(spanText);
        }
    

    【讨论】:

    • 你在使用 ArrayAdapter 吗?您需要为此自定义适配器。
    【解决方案4】:

    在您的 filter 方法中,存储用于执行过滤器的字符串:

    // Filter Class
    public void filter(String searchString) {
        this.searchString = searchString;
        ...
        // Filtering stuff as normal.
    }
    

    你必须声明一个成员字符串来存储它:

    public class ListViewAdapter extends BaseAdapter {
        ...    
        String searchString = "";
        ...
    

    并且,在 getView 中突出显示搜索词:

    public View getView(final int position, View view, ViewGroup parent) {
        ...
        // Set the results into TextViews
        WorldPopulation item = worldpopulationlist.get(position);
        holder.rank.setText(item.getRank());
        holder.country.setText(item.getCountry());
        holder.population.setText(item.getPopulation());
    
        // Find charText in wp
        String country = item.getCountry().toLowerCase(Locale.getDefault());
        if (country.contains(searchString)) {
            Log.e("test", country + " contains: " + searchString);
            int startPos = country.indexOf(searchString);
            int endPos = startPos + searchString.length();
    
            Spannable spanText = Spannable.Factory.getInstance().newSpannable(holder.country.getText()); // <- EDITED: Use the original string, as `country` has been converted to lowercase.
            spanText.setSpan(new ForegroundColorSpan(Color.RED), startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    
            holder.country.setText(spanText, TextView.BufferType.SPANNABLE);
        }
        ...
    }
    

    希望对你有帮助。

    【讨论】:

      【解决方案5】:

      假设您已经创建了一个自定义适配器,那么您可以参考以下代码:

          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              View view;
              TextView text;
      
              if (convertView == null) {
                  view = mInflater.inflate(mResource, parent, false);
              } else {
                  view = convertView;
              }
      
              try {
                  if (mFieldId == 0) {
                      //  If no custom field is assigned, assume the whole resource is a TextView
                      text = (TextView) view;
                  } else {
                      //  Otherwise, find the TextView field within the layout
                      text = (TextView) view.findViewById(mFieldId);
                  }
              } catch (ClassCastException e) {
                  Log.e("ArrayAdapter", "You must supply a resource ID for a TextView");
                  throw new IllegalStateException(
                          "ArrayAdapter requires the resource ID to be a TextView", e);
              }
              String item = getItem(position);
              text.setText(item);
      
              String fullText = getItem(position);
              // highlight search text
              if (mSearchText != null && !mSearchText.isEmpty()) {
                  int startPos = fullText.toLowerCase(Locale.US).indexOf(mSearchText.toLowerCase(Locale.US));
                  int endPos = startPos + mSearchText.length();
      
                  if (startPos != -1) {
                      Spannable spannable = new SpannableString(fullText);
                      ColorStateList blueColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{Color.BLUE});
                      TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, blueColor, null);
                      spannable.setSpan(highlightSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                      text.setText(spannable);
                  } else {
                      text.setText(fullText);
                  }
              } else {
                  text.setText(fullText);
              }
      
              return view;
          }
      

      mSearchText 将在 ArrayFilter 类的 performFiltering 内部更新。

      String prefixString = prefix.toString().toLowerCase();
      mSearchText = prefixString;
      

      您可以在my sample code heremy GitHub (with lastest update) 找到更多详细信息。

      这是截图

      【讨论】:

      • 感谢您的详细回答。我将尝试高亮逻辑并让您知道。现在,感谢您采取额外的步骤,仅此而已,赏金是您的。如果它对我有用,我会将这个答案标记为正确的答案。
      • 是的,我忘了重置mSearchText 变量,在我的代码中,mSearchText = ""; 应该在if (prefix == null || prefix.length() == 0) { 内。我刚刚将我的示例项目发布到github.com/ngocchung/filteredlistview
      • 很高兴能帮上忙,编码愉快 :)
      • 对不起,我没有找到你的 getRegex 函数的解决方案,我最近尝试了新的适配器,你可以看到github.com/ngocchung/FilteredListView/blob/master/app/src/main/…,然后如果你输入k 8 4,列表视图将显示并突出显示所有项目至少有一个字母。希望这会有所帮助!
      • 谢谢,我能看到问题,我会尝试进一步修改,不过感谢您的帮助,不胜感激!
      【解决方案6】:

      可以用更简单的方式完成:

      1. 定义自定义适配器:
      class HighlightAutoCompleteAdapter(context: Context, resource: Int, private val textResId: Int, items: List<String>) :
          ArrayAdapter<String>(context, resource, textResId, items) {
      
          private val inflater = LayoutInflater.from(context)
          var queryText = ""
      
          override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
              val view = convertView ?: inflater.inflate(textResId, parent, false)
              val textView: TextView = view.findViewById(android.R.id.text1) as TextView
              val fullText = getItem(position) as String
              // highlight search text
              val highlight: Spannable = SpannableString(fullText)
              if (queryText.isNotEmpty()) {
                  val startPos: Int = fullText.toLowerCase(Locale.US).indexOf(queryText.toLowerCase(Locale.US))
                  val endPos: Int = startPos + queryText.length
                  if (startPos != -1) {
                      highlight.setSpan(StyleSpan(BOLD),
                                          startPos,
                                          endPos,
                                          Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
                  }
              }
              textView.text = highlight
              return view
          }
      }
      
      1. 创建适配器并监听文本更改以保持适配器更新:
              val searchEditText: AutoCompleteTextView = view.findViewById(R.id.search_edit_text)
              val arrayAdapter = HighlightAutoCompleteAdapter(requireContext(), 0, R.layout.search_complete_item, autoCompletionList)
              searchEditText.setAdapter(arrayAdapter)
              searchEditText.addTextChangedListener(object : TextWatcher {
                  override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
                  override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                      arrayAdapter.queryText = s?.toString() ?: ""
                  }
                  override fun afterTextChanged(s: Editable?) {}
              })
      

      【讨论】:

        猜你喜欢
        • 2018-03-25
        • 2016-03-21
        • 2012-06-25
        • 2012-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-11
        相关资源
        最近更新 更多