How formatted string and then change style by annotations
Asked Answered
S

3

7

i have 3 strings localizations

<string name="tests" formatted="true">Test<annotation font="bold"> testBold %1$s</annotation> end</string>
<string name="tests" formatted="true">Тест<annotation font="bold"> тестБолд %1$s</annotation> конец</string>
<string name="tests" formatted="true">Тест<annotation font="bold"> тестБолд %1$s</annotation> кінець</string>

How i can add some argument and modified text by annotation then. The maximum that I get is to do this one thing

CharSequence t = getResources().getString(R.string.tests, "myValue");//in this case i lose my annotation, but set my argument
//OR
CharSequence t = getText(R.string.tests);//in this case i lose my argument but get style BOLD

public SpannableString textFormattingByTags(CharSequence t) {
        SpannedString titleText = new SpannedString(t);
        SpannedString titleText = (SpannedString) getText(R.string.tests);
        Annotation[] annotations = titleText.getSpans(0, titleText.length(), Annotation.class);
        SpannableString spannableString = new SpannableString(titleText);
        for (Annotation annotation : annotations) {
            if (annotation.getKey().equals("font")) {
                String fontName = annotation.getValue();
                if (fontName.equals("bold")) {
                    spannableString.setSpan(new CustomTypefaceSpan("",fontBold),
                            titleText.getSpanStart(annotation),
                            titleText.getSpanEnd(annotation),
                            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                }
            }
        }
        return spannableString;
    }

result in first case i get "Test testBold MyValue end" in second "Test testBold %1$s end". Who had some ideas?

Stephanystephen answered 9/8, 2018 at 8:21 Comment(5)
It's a bit difficult to understand what your question is. Can you repeat it but in other words (and avoid using computer terms, just describe your issue)Sheya
I need to add my variables to a string, and then apply a non-standard font to a specific section of textStephanystephen
I went looking for the same functionality you mention - I need string resource annotations and replacement params (getText(int resId, Object... formatArgs))- the annotations are great but useless to me w/o replacement params.Intonation
@Stephanystephen have you found any solution?Andi
@Andi added an answer, if something is not clear askStephanystephen
S
1
Typeface fontBold = Typeface.createFromAsset(getAssets(), "fonts/Trebuchet_MS_Bold.ttf");
    String s = getResources().getString(R.string.error_date, "20.02.2019", "25.02.2019");
    SpannableStringBuilder spannableString = new SpannableStringBuilder(s);

    Integer first1 = null;
    Integer first2 = null;
    Integer last1 = null;
    Integer last2 = null;
    int digits = 0;
    char[] crs = s.toCharArray();
    for (int i = 0; i < crs.length; i++) {
        if (Character.isDigit(crs[i]) && digits != 8) {
            if (first1 == null) {
                first1 = i;
            }
            last1 = i;
            digits++;
            continue;
        }
        if (Character.isDigit(crs[i])) {
            if (first2 == null) {
                first2 = i;
            }
            last2 = i;
        }

    }
    spannableString.setSpan(new CustomTypefaceSpan("", fontBold), first1, last1 + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    spannableString.setSpan(new CustomTypefaceSpan("", fontBold), first2, last2 + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getInstance());
    builder.setTitle("test");
    builder.setMessage(spannableString);
    builder.setCancelable(false);
    builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            getLoaderManager().destroyLoader(LOADER_SOE_BILLING_ID);
        }
    });
    android.support.v7.app.AlertDialog alert = builder.create();
    alert.show();

example

Stephanystephen answered 20/2, 2019 at 7:9 Comment(0)
T
6

1. Convert arguments to annotations

Your string:

<string name="tests" formatted="true">Test<annotation font="bold"> testBold %1$s</annotation> end</string>

Becomes:

<string name="tests">Test<annotation font="bold"> testBold <annotation arg="0">%1$s</annotation></annotation> end</string>

2. Create SpannableStringBuilder from resource

val text = context.getText(R.string.tests) as SpannedString
val spannableText = SpannableStringBuilder(text)

3. Apply ALL arg annotations FIRST

An example implementation:

fun SpannableStringBuilder.applyArgAnnotations(vararg args: Any) {
    val annotations = this.getSpans(0, this.length, Annotation::class.java)
    annotations.forEach { annotation ->
        when (annotation.key) {
            "arg" -> {
                val argIndex = Integer.parseInt(annotation.value)
                when (val arg = args[argIndex]) {
                    is String -> {
                        this.replace(
                            this.getSpanStart(annotation),
                            this.getSpanEnd(annotation),
                            arg
                        )
                    }
                }
            }
        }
    }
}

Pass in arguments:

spannableText.applyArgAnnotations("myValue")

4. Apply remaining annotations

spannableText.applyAnnotations()
textView.text = spannableText

5. Result

Test testBold myValue end

Edit

Created a small lib to abstract this behaviour - https://github.com/veritas1/tinytextstyler

Tieshatieup answered 6/4, 2020 at 15:32 Comment(0)
S
1
Typeface fontBold = Typeface.createFromAsset(getAssets(), "fonts/Trebuchet_MS_Bold.ttf");
    String s = getResources().getString(R.string.error_date, "20.02.2019", "25.02.2019");
    SpannableStringBuilder spannableString = new SpannableStringBuilder(s);

    Integer first1 = null;
    Integer first2 = null;
    Integer last1 = null;
    Integer last2 = null;
    int digits = 0;
    char[] crs = s.toCharArray();
    for (int i = 0; i < crs.length; i++) {
        if (Character.isDigit(crs[i]) && digits != 8) {
            if (first1 == null) {
                first1 = i;
            }
            last1 = i;
            digits++;
            continue;
        }
        if (Character.isDigit(crs[i])) {
            if (first2 == null) {
                first2 = i;
            }
            last2 = i;
        }

    }
    spannableString.setSpan(new CustomTypefaceSpan("", fontBold), first1, last1 + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    spannableString.setSpan(new CustomTypefaceSpan("", fontBold), first2, last2 + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getInstance());
    builder.setTitle("test");
    builder.setMessage(spannableString);
    builder.setCancelable(false);
    builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            getLoaderManager().destroyLoader(LOADER_SOE_BILLING_ID);
        }
    });
    android.support.v7.app.AlertDialog alert = builder.create();
    alert.show();

example

Stephanystephen answered 20/2, 2019 at 7:9 Comment(0)
L
0

How about change this code

<string name="tests" formatted="true">Test<annotation font="bold"> testBold %1$s</annotation> end</string>
<string name="tests" formatted="true">Тест<annotation font="bold"> тестБолд %1$s</annotation> конец</string>
<string name="tests" formatted="true">Тест<annotation font="bold"> тестБолд %1$s</annotation> кінець</string>

into

<string name="tests" formatted="true">Test<b> testBold %1$s</b> end</string>
<string name="tests" formatted="true">Тест<b> тестБолд %1$s</b> конец</string>
<string name="tests" formatted="true">Тест<b> тестБолд %1$s</b> кінець</string>

and you can use it like this

Spanned result = Html.fromHtml(getString(R.string.tests, "testing"));
textView.setText(result);

other styles include:

Tags                Format
--------------------------
b, strong           Bold
i, em, cite, dfn    Italics
u                   Underline
sub                 Subtext
sup                 Supertext
big                 Big
small               Small
tt                  Monospace
h1 ... h6           Headlines
img                 Image
font                Font face and color
blockquote          For longer quotes
a                   Link
div, p              Paragraph
br                  Linefeed

Turns out you cannot use getText(), you can find about this in the documentation.

Lardner answered 9/8, 2018 at 10:28 Comment(4)
to the arguments i need apply bold style tooStephanystephen
I see, I have improve my answer to fit your requirementLardner
This method does not allow me to add my custom typeface and when i use getText() i can't put my arguments. One more thing getText() return CharSequence, but the method fromHtml required StringStephanystephen
how about this?Lardner

© 2022 - 2024 — McMap. All rights reserved.