如何在搜索过滤器中突出显示搜索文本?

17
我试图进行搜索,以便突出显示所有“可见”的搜索字母。我尝试使用 spannable ,但那并没有起作用,也许我做错了?基于此:在 ListView 项目中突出显示搜索文本,我该如何突出显示可见文本?这是我的筛选器:
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;
        }
6个回答

24

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

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

performFiltering 方法位于 ArrayFilter 类中,在此方法内,mSearchText 将被更新。

String prefixString = prefix.toString().toLowerCase();
mSearchText = prefixString;
你可以在这里找到我的示例代码,或者在我的 GitHub(含最新更新)中查看更多细节。

以下是屏幕截图:

输入图像说明


1
谢谢你详细的回答。我将尝试突出逻辑并让你知道。就现在而言,感谢您额外的付出,仅为此,赏金归您所有。如果它对我起作用,我将把这个答案打勾为正确答案。 - user2386226
1
是的,在我的代码中,我忘记重置mSearchText变量了,mSearchText = "";应该在 if (prefix == null || prefix.length() == 0) {内部。我刚刚将我的示例项目发布到https://github.com/ngocchung/filteredlistview - BNK
1
很高兴能帮到你,愉快编程 :) - BNK
1
抱歉,我在使用你的getRegex函数时没有找到解决方案。我最近尝试了新的适配器,你可以在https://github.com/ngocchung/FilteredListView/blob/master/app/src/main/java/com/example/filteredlistview/HighlightArrayAdapter2.java中查看。如果你输入“k 8 4”,列表视图将显示并突出显示所有至少有一个字母的项目。希望这能帮到你! - BNK
1
谢谢,我能看到问题,会在继续修改的过程中尝试改进,虽然感谢你的帮助,非常感激! - user2386226
显示剩余15条评论

6

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

// 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);
    }
    ...
}

希望这能有所帮助。

4

您好,在适配器类中,创建一个Spannable文本并将其设置到TextView上。以下代码供参考:

 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吗?你需要为此自定义你的适配器。 - Prashant Maheshwari Andro

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

有没有其他的方法可以做到这一点,而不会冻结键盘? - user2386226

0

在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?对于上述代码,你需要自定义适配器。 - Prashant Maheshwari Andro

0

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

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

2. 创建适配器并监听文本更改以保持适配器更新:
        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?) {}
        })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接