как выделить искомый текст в фильтре поиска?

Я пытаюсь выполнить поиск таким образом, чтобы все «видимые» поисковые буквы были выделены. Я пытался использовать 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! 

вот мой гетвью

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

person Community    schedule 29.10.2015    source источник


Ответы (6)


Предположим, вы создали собственный адаптер, тогда вы можете обратиться к следующему коду:

    @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 будет обновлен в следующем внутри performFiltering класса ArrayFilter.

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

Дополнительную информацию можно найти в моем примере кода здесь или мой GitHub (с последним обновлением).

Вот скриншот

введите описание изображения здесь

person BNK    schedule 07.11.2015
comment
спасибо, это один из примеров кодов, похожих на мой код, можете ли вы объяснить это, когда я использовал ваш код, весь мой текст стал синим, а не только отфильтрованные символы. androidhive.info/2012/09/ - person ; 07.11.2015
comment
спасибо за подробный ответ. Я попробую выделить логику и дам вам знать. А пока спасибо за дополнительный шаг, просто за это награда за вас. Если это сработает для меня, я отмечу этот ответ как правильный. - person ; 08.11.2015
comment
Добро пожаловать, если это не работает для вашего приложения, пожалуйста, пришлите мне свой код, чтобы я мог проверить :) - person BNK; 08.11.2015
comment
Я не могу заставить его работать с моим кодом. Я добавил getview и внес изменения в соответствии с вашим кодом, но он все еще не работает, можете ли вы проверить мой getview, который я обновил в своем вопросе? что мне не хватает? - person ; 09.11.2015
comment
Где вы обновили значение mSearchText ? Попробуйте выполнить отладку, чтобы увидеть значения переменных, например, startPos, endPos, spannable - person BNK; 09.11.2015
comment
Пожалуйста, добавьте mSearchText = constraint.toString().toLowerCase() внутри if (constraint != null && constraint.length() > 0) { Я думаю, что ваше приложение входит в этот IF :) - person BNK; 09.11.2015
comment
это работает! спасибо, но в настоящее время даже в вашем тестовом приложении я вижу, что когда я очищаю последовательность символов, которую ищу, совпадающие символы остаются выделенными. скажем, я ищу буквы ab, и некоторые строки в списке соответствуют этому, тогда, когда я очищаю поиск, буквы остаются цветными. есть ли способ убрать цвета, когда окно поиска пусто? - person ; 09.11.2015
comment
Да, я забыл сбросить переменную mSearchText, в моем коде mSearchText = ""; должно быть внутри if (prefix == null || prefix.length() == 0) {. Я только что разместил свой пример проекта на github.com/ngocchung/filteredlistview. - person BNK; 09.11.2015
comment
Рад, что смог помочь, удачного кодирования :) - person BNK; 09.11.2015
comment
Вы случайно не знаете, как я могу также применить мою функцию getRegEx (CharSequence elements) к представлению выделения, а не только к нижнему регистру ()? есть ли способ заставить его выделять буквы, которые ищутся, даже после пробела между ними или около того на основе функции регулярного выражения - person ; 11.11.2015
comment
если вы имеете в виду sp в окне поиска, например, отображение списка Dell Inspiron, то внутри performFiltering класса ArrayFilter: используйте if (valueText.startsWith(prefixString) || valueText.contains(prefixString)) и if (word.startsWith(prefixString) || word.contains(prefixString)) - person BNK; 11.11.2015
comment
нет, я имел в виду String prefixString = ограничение.toString().toLowerCase(); , это просто выделяет строчные буквы и умирает после пробела, есть ли способ использовать мою текущую функцию регулярного выражения, чтобы выделить совпадающие символы вместе с ограничением.toString().toLowerCase() - person ; 11.11.2015
comment
Вы имеете в виду следующий снимок экрана drive.google.com/file/d /0B2HGUM4c0YwpRjJhX1hfMkNnelk/? - person BNK; 11.11.2015
comment
.toLowerCase() только для правильного сравнения, прописные буквы тоже выделены - person BNK; 11.11.2015
comment
что-то вроде поиска k 8, и он будет выделять k и 8 только на этом снимке экрана для nokia 8800, сейчас он выделяет, только если слова ищутся непрерывно, например, для nokia 8800 он выделяет kia 8 по мере их поиска для непрерывности. даже если я ищу любую букву из первого слова и любую букву из второго и т. д., она должна выделить их, для чего я использую свою функцию регулярного выражения (для поиска), просто хочу знать, как я могу применить это для выделения также - person ; 11.11.2015
comment
А, я еще не пробовал, ок, попробую сегодня или завтра, когда будет больше свободного времени :) - person BNK; 11.11.2015
comment
нет проблем, всякий раз, когда вы можете, если вы можете использовать мою функцию регулярных выражений и подобную логику, это будет потрясающе, спасибо! - person ; 11.11.2015
comment
Извините, я не нашел решения с вашей функцией getRegex, моя последняя попытка с новым адаптером, вы можете увидеть github.com/ngocchung/FilteredListView/blob/master/app/src/main/, то если вы наберете k 8 4, список будет показать и выделить все элементы, имеющие хотя бы одну букву. Надеюсь это поможет! - person BNK; 11.11.2015
comment
спасибо, я могу видеть проблемы, постараюсь изменить дальше по мере продвижения, спасибо за помощь, спасибо за это! - person ; 11.11.2015
comment
@BNK отличный пример. - person Kuldeep Singh; 15.02.2019

В методе 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);
    }
    ...
}

Надеюсь, поможет.

person Vishavjeet Singh    schedule 06.11.2015

Привет, ваш класс адаптера, сделайте составной текст и установите его в свой текстовый вид, приведенный ниже код вы можете использовать для справки.

 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);
    }
person Arun Antoney    schedule 06.11.2015
comment
Вы используете ArrayAdapter? вам нужно настроить адаптер для этого. - person Prashant Maheshwari Andro; 09.11.2015

Это только демо для выделения текста, вы можете реализовать себя, вызвав 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;
    }
 }
}
person Rajesh    schedule 02.11.2015
comment
зависает клавиатура, есть ли другой способ сделать это - person ; 03.11.2015

Поместите этот код перед установкой текста в 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);
person Prashant Maheshwari Andro    schedule 05.11.2015
comment
Вы используете ArrayAdapter? для приведенного выше кода вам необходимо настроить адаптер. - person Prashant Maheshwari Andro; 09.11.2015

Это можно сделать немного проще:

  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?) {}
        })
person yshahak    schedule 30.05.2021