0

I'm trying to replace a certain string with a span.

For example I have this String:

String s = "redHello greenWorld";

I wanna replace "red" with:

modifiedText.setSpan(new ForegroundColorSpan(Color.parseColor("#FF0000")), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

and "green" with:

modifiedText.setSpan(new ForegroundColorSpan(Color.parseColor("#00FF00")), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

So I create modifiedText this way:

Spannable modifiedText = new SpannableString(s);

How can I replace a certain String with a Span without HTML?

Qayin
  • 3
  • 2
  • You seem to have the code to do that in your question. To apply a span, call `setSpan()`. If your question is "how do I find the values of `start` and `end`?", you could use methods like [`indexOf()`](https://developer.android.com/reference/java/lang/String#indexOf(java.lang.String)) to find `start`, and you calculate `end` based on `start` and the length of the substring that you are looking for. – CommonsWare Feb 21 '21 at 00:53
  • @CommonsWare This would never work, bacause I wanna replace and not only find, so the words "red" and "green" would not be deleted... – Qayin Feb 21 '21 at 01:01
  • since the SpannableString is immutable, you could write a function that returns a new string (Iterate through the string, and if you find "red" or "green" set the span else to the output) (I hope I understood your question right) – KH241 Feb 21 '21 at 01:14
  • @KH241 That's the problem: how can I append to a String something that isn't a String? – Qayin Feb 21 '21 at 01:19
  • but cant you just calculate the positions, cut the "red" and "green" out and then you set the span? – KH241 Feb 21 '21 at 01:22
  • @KH241 Once the String are passed to the constructor it can't be cutted out any more :( ... – Qayin Feb 21 '21 at 01:27
  • I meant before you do the `SpannableString(s)`. save the positions/cut the string -> call the constructor – KH241 Feb 21 '21 at 01:28
  • @KH241 I see no way to save the positions. What would be your way? – Qayin Feb 21 '21 at 23:47

2 Answers2

0

SpannableString doesn't split (substring) the given String. It just changes attributes for given range with start and end index. If you want to change color of some part of a String you don't need substring (or cut). Just use start and end index for which part you want to change and the rest will remain same (original). In your case according to your question you want to change color of red and green words so just use 2 setSpan methods with start and end index of for those words.

String s = "redHello greenWorld";
SpannableString modifiedText = new SpannableString(s);

//Change the color for 'red' word
modifiedText.setSpan(new ForegroundColorSpan(Color.parseColor("#FF0000")), 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

//Change the color for 'green' word
modifiedText.setSpan(new ForegroundColorSpan(Color.parseColor("#00FF00")), 9, 14, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Eventhough it's SpannableString it's still String so you can use it like a String if you want to set as a Text for your View. For example if you want to set that String to your TextView use setText() method with your SpannableString.

textView.setText(modifiedText);
Toygur Ziya Kutlu
  • 269
  • 1
  • 3
  • 15
  • The strings "red" and "green" would not be cutted out. I wanna replace them with an empty string. – Qayin Feb 21 '21 at 23:52
  • I really don't understand what you want. Is your string "Hello Word" and you want Hello with red color and Word with green color? – Toygur Ziya Kutlu Feb 22 '21 at 01:43
  • I wanna replace the "green" String with green color and the "red" String with red color. That means: cut out the strings "green" and "red" and instead set the entire string in the appropriate color from the position in which "green" or "red" was previously written. – Qayin Feb 22 '21 at 22:12
0

Since you asked about cutting out - I hope this is what you meant (new code)

//setup
String text = "redHello greenWorld redTest";
List<String> colors = new ArrayList<>();
colors.add("red");
colors.add("green");

//finding the positions
List<Integer> pos = new ArrayList<>();
List<String> colorPositions = new ArrayList<>();
for (String toFind: colors) {
    Pattern word = Pattern.compile(toFind);
    Matcher match = word.matcher(text);
    while (match.find()) {
       pos.add(match.start());
       colorPositions.add(toFind);
    }
}

//replacing
for (String element : colors) {
    text = text.replace(element, "");
}

Now you need to sort the list

//really inefficient sorting
boolean sorted = false;
Integer temp_pos;
String temp_color;
Integer[] sorted_pos = pos.toArray(Integer[]::new);
String[] sorted_color = colorPositions.toArray(String[]::new);
while(!sorted) {
    sorted = true;
    for (int i = 0; i < sorted_pos.length - 1; i++) {
        if (sorted_pos[i] > sorted_pos[i + 1]) {
            temp_pos = sorted_pos[i];
            temp_color = sorted_color[i];
            sorted_pos[i] = sorted_pos[i + 1];
            sorted_color[i] = sorted_color[i + 1];
            sorted_pos[i + 1] = temp_pos;
            sorted_color[i + 1] = temp_color;
            sorted = false;
        }
    }
}

And subtract the "red" and "green"

//subtracting
for (int i = 1; i < sorted_pos.length; i++) {
    for (int j = i; j < sorted_pos.length; j++) {
        sorted_pos[j] -= sorted_color[i - 1].length();
    }
}

The end result now contains [0, 6, 12], the starting indexes of the spans - now you only need to iterate through the output and set the spans (the colors to the spans are in sorted_color)

KH241
  • 166
  • 5
  • Thank you for your code, @KH241. There is only the problem, that it works only if there are only one "green" and "red" to cut out. But if there are multiple of them, than there are problems... You can see here my complete try with multiple colors from bytes: https://pastebin.com/Wvkm9nhQ – Qayin Feb 22 '21 at 22:05
  • I updated the code - you will need to figure out one or two things but the basics are in my Answer. – KH241 Feb 23 '21 at 01:02
  • Thank you for your code. It's really easier to understand and it's very clean. But there is still a problem: You pass to the StringBuilder constructor the clean String. According to List pos, the green color should be set from position 9 onwards. But since the string is now clean and the strings "red" and "green" have been cut out, the green color no longer has to be set from position 9, but from position 6. And I don't know how to calculate that additionally... – Qayin Feb 23 '21 at 23:41
  • Mate, I told you that you will need to sort and subtract. If the sorting part was too hard you should really work on your basics and/or google skills. Anyway, I added both parts. Everything should work now. – KH241 Feb 25 '21 at 05:46
  • No, pos should be [0, 12, 6] but with your code pos is still [0, 20, 9]... However, thank you for your help. Accepted answer! – Qayin Feb 26 '21 at 22:09
  • I still get [0, 20, 9]. – Qayin Mar 08 '21 at 11:40
  • I know, it's not a little problem. Maybe I'll find another way to solve this... – Qayin Mar 08 '21 at 11:40
  • How do you get [0,20,9]? Did you copy everything or just the first block of code? – KH241 Mar 08 '21 at 23:27