Выделить текст внутри textview

У меня есть TextView со случайным цветом фона (на самом деле это может быть любой цвет). У меня также есть текст на этом Textview, который должен быть читаемым. Я полагаю, что лучшее решение - выделить указанный текст белым и установить цвет текста на черный.

Мой вопрос: можно ли выделить текст внутри texview из XML?

У меня есть следующее в моем макете:

  <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/colorButton4"
        android:layout_gravity="right|bottom"
        android:background="@drawable/layout_border"
        android:layout_marginRight="30dp"
        android:layout_marginBottom ="30dp"
        android:clickable="true"
        android:onClick="onClick"
        android:gravity="center"
        android:textColorHighlight="@color/bgWhite"
        android:textColor="@color/Black"
        android:text="5431354" />

Но это не выделяет текст.

8 ответов

Решение

https://github.com/datanapps/HighlightedTextView

      <datanapps.highlightedtextview.HighLightTextView
    android:id="@+id/tv2"
    android:layout_below="@+id/tv1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android is an open source and Linux-based operating system for mobile devices such as smartphones and tablet computers."
    android:textColor="@color/white"
    app:fontFamily="serif"
    android:lineSpacingExtra="50sp"
    android:layout_marginTop="20dp"
    android:textSize="20sp"
    android:textAlignment="viewEnd"
    app:highLightColor="@color/blue"
    />

Возможно, вы захотите использовать для этого SpannableString, которая позволяет по-разному отображать отдельные части строки в TextView.

Вот так:

    SpannableString str = new SpannableString("Highlighted. Not highlighted.");
    str.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, 11, 0);
    textView.setText(str);

Простой способ

Вы можете использовать Spannable класс для форматирования текста.

textView.setText("Hello, I am Awesome, Most Awesome"); // set text first
setHighLightedText(textView, "a"); // highlight all `a` in TextView

На выходе будет что-то вроде изображения ниже.

Выход

Вот метод.

 /**
     * use this method to highlight a text in TextView
     *
     * @param tv              TextView or Edittext or Button (or derived from TextView)
     * @param textToHighlight Text to highlight
     */
    public void setHighLightedText(TextView tv, String textToHighlight) {
        String tvt = tv.getText().toString();
        int ofe = tvt.indexOf(textToHighlight, 0);
        Spannable wordToSpan = new SpannableString(tv.getText());
        for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) {
            ofe = tvt.indexOf(textToHighlight, ofs);
            if (ofe == -1)
                break;
            else {
                // set color here
                wordToSpan.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + textToHighlight.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                tv.setText(wordToSpan, TextView.BufferType.SPANNABLE);
            }
        }
    }

Вы можете проверить этот ответ на кликабельный выделенный текст.

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

private void highlightString(String input) {
//Get the text from text view and create a spannable string
SpannableString spannableString = new SpannableString(mTextView.getText());
//Get the previous spans and remove them
BackgroundColorSpan[] backgroundSpans = spannableString.getSpans(0, spannableString.length(), BackgroundColorSpan.class);

for (BackgroundColorSpan span: backgroundSpans) {
    spannableString.removeSpan(span);
}

//Search for all occurrences of the keyword in the string
int indexOfKeyword = spannableString.toString().indexOf(input);

while (indexOfKeyword > 0) {
    //Create a background color span on the keyword
    spannableString.setSpan(new BackgroundColorSpan(Color.YELLOW), indexOfKeyword, indexOfKeyword + input.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

    //Get the next index of the keyword
    indexOfKeyword = spannableString.toString().indexOf(input, indexOfKeyword + input.length());
}

//Set the final text on TextView
mTextView.setText(spannableString);}

Примечание. MTextView - это объект TextView, в котором вы хотите выделить свой текст.

Я написал метод Kotlin, который выделит все ключевые слова во всех вхождениях в String и вернется SpannableString.

fun main() {
    textView.text = highlightKeywords(
        highlightColor = ContextCompat.getColor(context, R.color.colorAccent),
        message = "Hello World, and Hello to all my Hello Friends.",
        keywords = listOf("Hello")
    )
}


fun highlightKeywords(
    highlightColor: Int,
    message: String,
    keywords: List<String>,
): SpannableString {
    val spannableString = SpannableString(message)
    keywords.forEach { keyword ->
        if (!keyword.isBlank()) {
            var startIndex = message.indexOf(keyword)

            while (startIndex >= 0) {
                spannableString.setSpan(
                    ForegroundColorSpan(highlightColor),
                    startIndex,
                    startIndex + keyword.length,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                )

                startIndex = message.indexOf(keyword, startIndex + keyword.length)
            }
        }
    }
    return spannableString
}

После 4 лет работы над Android я могу с уверенностью сказать, что без кода вне XML невозможно. Решения, которыми поделились другие, помогут вам, если вы сможете использовать некоторый код, так что прочитайте!

Ответ Сураджа отличный и работает, но в нем отсутствуют 2 компонента. Во-первых, он не выделяет первое слово (как прокомментировал Rany Albeg Wein ), а во-вторых, он не игнорирует регистр, поэтому поиск в этой строке: ничего не найду.

Это мой обновленный ответ, который разрешает оба из них с помощью переданного аргумента, а также добавляет альфа-канал, если вы хотите использовать собственный цвет для выделения. Обратите внимание, что перегруженный первый метод является примером того, как вернуть то, что делал предыдущий метод, кроме первого выбранного элемента.

          
    /**
     * Use this method to get the same return as the previous method
     */
    public static SpannableString buildHighlightString(String originalText, String textToHighlight){
        return buildHighlightString(originalText, textToHighlight, false, Color.YELLOW, 1.0F);
    }
    
    /**
     * Build a spannable String for use in highlighting text colors
     * 
     * @param originalText The original text that is being highlighted
     * @param textToHighlight The text / query that determines what to highlight
     * @param ignoreCase Whether or not to ignore case. If true, will ignore and "test" will have
     *                   the same return as "TEST". If false, will return an item as highlighted
     *                   only if it matches it case specficic.
     * @param highlightColor The highlight color to use. IE {@link Color#YELLOW} || {@link Color#BLUE}
     * @param colorAlpha Alpha to adjust how transparent the color is. 1.0 means it looks exactly
     *                   as it should normally where as 0.0 means it is completely transparent and
     *                   see-through. 0.5 means it is 50% transparent. Useful for darker colors
     */
    public static SpannableString buildHighlightString(String originalText, String textToHighlight,
                                                       boolean ignoreCase, @ColorInt int highlightColor,
                                                       @FloatRange(from = 0.0, to = 1.0) float colorAlpha){
        SpannableString spannableString = new SpannableString(originalText);
        if (TextUtils.isEmpty(originalText) || TextUtils.isEmpty(textToHighlight)) {
            return spannableString;
        }
        String lowercaseOriginalString = originalText.toLowerCase();
        String lowercaseTextToHighlight = textToHighlight.toLowerCase();
        if(colorAlpha < 1){
            highlightColor = ColorUtils.setAlphaComponent(highlightColor, ((int)(255*colorAlpha)));
        }
        //Get the previous spans and remove them
        BackgroundColorSpan[] backgroundSpans = spannableString.getSpans(0, spannableString.length(), BackgroundColorSpan.class);
        for (BackgroundColorSpan span: backgroundSpans) {
            spannableString.removeSpan(span);
        }
        //Search for all occurrences of the keyword in the string
        int indexOfKeyword = (ignoreCase)
                ? lowercaseOriginalString.indexOf(lowercaseTextToHighlight)
                : originalText.indexOf(textToHighlight);
        while (indexOfKeyword != -1) {
            //Create a background color span on the keyword
            spannableString.setSpan(new BackgroundColorSpan(highlightColor), indexOfKeyword,
                    indexOfKeyword + (textToHighlight.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            
            //Get the next index of the keyword
            indexOfKeyword = (ignoreCase)
                    ? lowercaseOriginalString.indexOf(lowercaseTextToHighlight, (indexOfKeyword) + textToHighlight.length())
                    : originalText.indexOf(textToHighlight, (indexOfKeyword) + textToHighlight.length());
        }
        return spannableString;
    }
    

Если бы вы проверили документы для TextView, вы бы узнали, что android:textColorHighlight не делает то, что вы хотите: https://developer.android.com/reference/android/widget/TextView.html

Он используется только при выборе текста, например, в EditText. Вам нужно установить фон TextView, чтобы "выделить его".

Другие вопросы по тегам