Как использовать функцию поддержки шрифтов библиотеки как часть содержимого TextView (используя spannable)? - PullRequest
0 голосов
/ 26 апреля 2018

Фон

Библиотека поддержки (документы здесь ) позволяет вам использовать файлы шрифтов TTF в папке "res / font", либо в XML:

app:fontFamily="@font/lato_black"

или по коду:

val typeface = ResourcesCompat.getFont(context, R.font.lato_black)

Проблема

Хотя я знаю, что можно использовать сменную технику для установки различных стилей в частях содержимого TextView (таких как полужирный, курсив, цвета и т. Д.), Единственное, что я нашел для установки другого шрифта, это использование встроенных шрифтов ОС, как показано здесь , но я не вижу способа сделать это для нового способа загрузки шрифтов.

Что я пробовал

Я пытался найти способ конвертировать между ними, но безуспешно. Конечно, я также пытался найти в документации о возможных функциях, и я попытался найти об этом через Интернет.

Вопрос

Как установить разные шрифты для разных частей TextView?

Например, в тексте «Hello world» установите для «Hello» шрифт «lato_black» и значение по умолчанию для остальных.


РЕДАКТИРОВАТЬ: Поскольку я получил примерно один и тот же ответ от 2 разных людей, я не могу принять один за другим. Дали им оба +1 за усилие, и я немного изменил вопрос:

Как бы легко установить стиль шрифта для части текста, в то время как файл strings.xml определит его с помощью настраиваемого тега шрифта.

Например, это может быть в файле strings.xml, чтобы установить его, как я просил выше:

<string name="something" ><customFont fontResource="lato_black">Hello</customFont> world</string>

Затем в коде все, что вам нужно сделать, это использовать что-то вроде:

textView.setText (Html.fromHtml(text, null, CustomFontTagHandler()))

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

Ответы [ 4 ]

0 голосов
/ 14 сентября 2018

Чтобы расширить ответ разработчика Android , можно сделать .font(){...} функцию расширения, например .bold{}, .backgroundColor{} из android ktx :

inline fun SpannableStringBuilder.font(typeface: Typeface, builderAction: SpannableStringBuilder.() -> Unit): SpannableStringBuilder {
    return inSpans(TypefaceSpan(typeface), builderAction = builderAction)
}

Тогда вы можете использовать:

val spannable = SpannableStringBuilder()
                .append(getString(...))
                .font(ResourcesCompat.getFont(context!!, R.font.myFont)!!) {
                    append(getString(...))
                }
                .bold{append(getString(...))}
textView.text = spannable

Не использовать spannable.toString().

Бонус: fontSize за Spannable:

inline fun SpannableStringBuilder.fontSize(fontSize: Int, builderAction: SpannableStringBuilder.() -> Unit): SpannableStringBuilder {
    return inSpans(AbsoluteSizeSpan(fontSize), builderAction = builderAction)
}
0 голосов
/ 03 мая 2018

Попробуйте ... в моем случае это работает отлично, вы можете изменить в соответствии с вашими требованиями. В настоящее время он работает как, например: - Hello World, Hello в font lato_light и remaining в font lato_bold.

protected final SpannableStringBuilder decorateTitle(String text, @IdRes int view) {
                            List<TextUtils.Option> options = new ArrayList<>();
                            int index = text.indexOf(' ');
                            if (index >= 0) {
                                options.add(new TextUtils.Option(ResourcesCompat.getFont(this, R.font.lato_light),
                                        ContextCompat.getColor(this, R.color.toolbar_title_text),
                                        0, index));
                                options.add(new TextUtils.Option(ResourcesCompat.getFont(this, R.font.lato_bold),
                                        ContextCompat.getColor(this, R.color.primary_text),
                                        index, text.length()));
                            } else options.add(new TextUtils.Option(ResourcesCompat.getFont(this, R.font.lato_bold),
                                    ContextCompat.getColor(this, R.color.primary_text),
                                    0, text.length()));

                            SpannableStringBuilder stringBuilder = TextUtils.stringSpanning(options, text);
                            if (view != 0) {
                                ((TextView) findViewById(view)).setText(stringBuilder);
                            }
                            return stringBuilder;
                        }

В классе Java добавьте этот метод pass String u want to decorate & view in xml

        public void onSuccess(@NonNull String title) {
                                            decorateTitle(title, R.id.listing_toolbar_title);
                                    }

TextUtils.java

public final class TextUtils {

    public static String trim(String text) {
        text = text.trim();
        return text.replaceAll("\\s+", " ");
    }

    public static String sanitize(String text) {
        if (text == null || text.isEmpty()) return text;

        if (text.contains("\ufffd")) {
            text = text.replaceAll("\ufffd", "");
        }

        if (text.contains(" ")) {
            return sanitize(text.split("\\s"));
        } else if (text.contains("_")) {
            return sanitize(text.split("_"));
        } else if (text.contains("-")) {
            return sanitize(text.split("-"));
        }
        if (!Character.isUpperCase(text.charAt(0))) {
            return text.substring(0, 1).toUpperCase() + text.substring(1);
        } else {
            return text;
        }
    }

    private static String sanitize(String[] strings) {
        StringBuilder sb = new StringBuilder();
        int lastIndex = strings.length - 1;
        for (int i = 0; i < strings.length; i++) {
            String str = strings[i];
            if (str.length() > 0) {
                if (Character.isLetter(str.charAt(0))
                        && !Character.isUpperCase(str.charAt(0))) {
                    sb.append(str.substring(0, 1).toUpperCase()).append(str.substring(1));
                } else {
                    sb.append(str);
                }

                if (i != lastIndex) sb.append(" ");
            }
        }
        return sb.toString();
    }


    public static String fillWithUnderscore(String text) {
        if (text.contains(" ")) {
            String[] splitText = text.split(" ");
            StringBuilder sb = new StringBuilder();
            int lastIndex = splitText.length - 1;
            for (int i = 0; i < splitText.length; i++) {
                sb.append(splitText[i]);
                if (i != lastIndex) sb.append("_");
            }
            return sb.toString();
        } else return text;
    }


    public static String sanitizePrice(Double price) {
        if (Objects.isNull(price) || price == 0) return "";

        String pricing = String.format(Locale.getDefault(), "₹ %.0f", price);
        StringBuilder input = new StringBuilder(pricing).reverse();
        StringBuilder output = new StringBuilder("");
        char[] digits = input.toString().toCharArray();
        for (int i = 0; i < digits.length; i++) {
            if (i < 3 || i % 2 == 0) {
                output.append(digits[i]);
            } else if (i % 2 != 0) {
                output.append(" ").append(digits[i]);
            }
        }
        return output.reverse().toString();
    }

    public static String sanitizeProductName(String productName) {
        if (productName.contains("\ufffd")) {
            return productName.replaceAll("\ufffd", "");
        } else return productName;
    }

    ///////////////////////////////////////////////////////////////////////////
    // String Spanning
    ///////////////////////////////////////////////////////////////////////////

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        paint.setTypeface(tf);
    }

    public static SpannableStringBuilder stringSpanning(List<Option> options, StringBuilder builder) {
        return stringSpanning(options, builder.toString());
    }

    public static SpannableStringBuilder stringSpanning(List<Option> options, String text) {
        SpannableStringBuilder spannable = new SpannableStringBuilder(text);
        for (Option option : options) {
            spannable.setSpan(new CustomTypefaceSpan(option.getFont()),
                    option.getFromIndex(), option.getToIndex(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
            spannable.setSpan(new ForegroundColorSpan(option.getColor()),
                    option.getFromIndex(), option.getToIndex(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
        }
        return spannable;
    }

    static class CustomTypefaceSpan extends MetricAffectingSpan {

        private final Typeface typeface;

        CustomTypefaceSpan(Typeface typeface) {
            this.typeface = typeface;
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            applyCustomTypeFace(ds, typeface);
        }

        @Override
        public void updateMeasureState(TextPaint paint) {
            applyCustomTypeFace(paint, typeface);
        }
    }

    public static class Option {
        private Typeface font;
        private int color;
        private int fromIndex;
        private int toIndex;

        public Option(Typeface font, int color, int fromIndex, int toIndex) {
            this.font = font;
            this.color = color;
            this.fromIndex = fromIndex;
            this.toIndex = toIndex;
        }

        public Option(Context context, @FontRes int font, @ColorRes int color, int fromIndex, int toIndex) {
            this.font = ResourcesCompat.getFont(context, font);
            this.color = ContextCompat.getColor(context, color);
            this.fromIndex = fromIndex;
            this.toIndex = toIndex;
        }

        public Typeface getFont() {
            return font;
        }

        public void setFont(Typeface font) {
            this.font = font;
        }

        public int getColor() {
            return color;
        }

        public void setColor(int color) {
            this.color = color;
        }

        public int getFromIndex() {
            return fromIndex;
        }

        public void setFromIndex(int fromIndex) {
            this.fromIndex = fromIndex;
        }

        public int getToIndex() {
            return toIndex;
        }

        public void setToIndex(int toIndex) {
            this.toIndex = toIndex;
        }
    }

    public static Double toDouble(String text) {
        StringBuilder collect = new StringBuilder();
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            if (Character.isDigit(c))
                collect.append(c);
        }
        return Double.parseDouble(collect.toString());
    }
}
0 голосов
/ 10 мая 2018

Поскольку оба ответа MJM и TheMatrix практически одинаковы (но слишком сложны для меня), и оба ответа были даны примерно в одно и то же время, я не мог просто выбрать один из них, поэтому я предоставил +1 для каждого, попросив их сделать его короче, но с поддержкой XML-тега для облегчения работы со строкой.

А пока вот гораздо более короткая версия того, как установить собственный шрифт для части текста в TextView:

class CustomTypefaceSpan(private val typeface: Typeface?) : MetricAffectingSpan() {
    override fun updateDrawState(paint: TextPaint) {
        paint.typeface=typeface
    }

    override fun updateMeasureState(paint: TextPaint) {
        paint.typeface=typeface
    }
}

Пример использования:

    val text = "Hello world"
    val index = text.indexOf(' ')
    val spannable = SpannableStringBuilder(text)
    spannable.setSpan(CustomTypefaceSpan(ResourcesCompat.getFont(this, R.font.lato_light)), 0, index, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
    spannable.setSpan(CustomTypefaceSpan(ResourcesCompat.getFont(this, R.font.lato_bold)), index, text.length, Spanned.SPAN_EXCLUSIVE_INCLUSIVE)
    textView.text = spannable

РЕДАКТИРОВАТЬ: кажется, Google предоставил видео об этом, здесь :

class CustomTypefaceSpan(val font: Typeface?) : MetricAffectingSpan() {
    override fun updateMeasureState(textPaint: TextPaint) = update(textPaint)
    override fun updateDrawState(textPaint: TextPaint?) = update(textPaint)

    private fun update(tp: TextPaint?) {
        tp.apply {
            val old = this!!.typeface
            val oldStyle = old?.style ?: 0
            val font = Typeface.create(font, oldStyle)
            typeface = font
        }
    }
}

И решение обработки этого в strings.xml также обсуждается на видео, здесь , но с использованием аннотаций вместо новых тегов HTML. Пример: * 1 023 *

strings.xml

<string name="title"><annotation font="lato_light">Hello</annotation> <annotation font="lato_bold">world</annotation></string>

MainActivity.kt

    val titleText = getText(R.string.title) as SpannedString
    val spannable = SpannableStringBuilder(titleText)
    val annotations = titleText.getSpans(0, titleText.length, android.text.Annotation::class.java)
    for (annotation in annotations) {
        if(annotation.key=="font"){
            val fontName=annotation.value
            val typeface= ResourcesCompat.getFont(this@MainActivity,resources.getIdentifier(fontName,"font",packageName))
            spannable.setSpan(CustomTypefaceSpan(typeface),spannable.getSpanStart(annotation),spannable.getSpanEnd(annotation), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    }
    textView.text = spannable

И результат:

enter image description here

Тем не менее я почти уверен, что можно использовать из HTML, но, вероятно, оно того не стоит.

Мне также интересно, что нужно делать, если мы хотим использовать как основные теги HTML, так и теги cusomzied, которые мы установили для шрифта, если мы действительно используем annotation там.

0 голосов
/ 03 мая 2018

Пользовательский класс для применения fonrFamilySpan

 public class MultipleFamilyTypeface extends TypefaceSpan {
        private final Typeface typeFace;

        public MultipleFamilyTypeface(String family, Typeface type) {
            super(family);
            typeFace = type;
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            applyTypeFace(ds, typeFace);
        }

        @Override
        public void updateMeasureState(TextPaint paint) {
            applyTypeFace(paint, typeFace);
        }

        private static void applyTypeFace(Paint paint, Typeface tf) {
            int oldStyle;
            Typeface old = paint.getTypeface();
            if (old == null) {
                oldStyle = 0;
            } else {
                oldStyle = old.getStyle();
            }

            int fake = oldStyle & ~tf.getStyle();
            if ((fake & Typeface.BOLD) != 0) {
                paint.setFakeBoldText(true);
            }

            if ((fake & Typeface.ITALIC) != 0) {
                paint.setTextSkewX(-0.25f);
            }

            paint.setTypeface(tf);
        }
    }

Применить шрифт

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String firstWord = "Hello ";
        String secondWord = "Word ";
        String thirdWord = "Normal ";

        TextView textViewTest = findViewById(R.id.textViewTest);

        Spannable spannable = new SpannableString(firstWord + secondWord + thirdWord);

        Typeface CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.akronim);
        Typeface SECOND_CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.baloo_thambi);

        spannable.setSpan(new MultipleFamilyTypeface("akronim", CUSTOM_TYPEFACE), 0, firstWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        spannable.setSpan(new MultipleFamilyTypeface("baloo_thambi", SECOND_CUSTOM_TYPEFACE), firstWord.length(), firstWord.length() + secondWord.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);


        textViewTest.setText(spannable);
    }
}

1010 Мощн * *

enter image description here

Способ редактирования два для пользовательских тегов

Добавить implementation 'org.jsoup:jsoup:1.11.3' в Gradle

 List<String> myCustomTag = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        TextView textViewTest = findViewById(R.id.textViewTest);


        // mention list custom tag that you used 
        myCustomTag.add("akronim");
        myCustomTag.add("baloo_thambi");
        myCustomTag.add("xyz");

        String html = "<akronim>Hello</akronim>"
                + "<baloo_thambi> Word  </baloo_thambi>"
                + " Normal "
                + " <xyz> testing </xyz> "
                + "<akronim>Styles</akronim>";
        textViewTest.setText(processToFontStyle(html));

    }


    public Spannable processToFontStyle(String text) {

        Document doc = Jsoup.parse(text);
        Elements tags = doc.getAllElements();
        String cleanText = doc.text();
        Log.d("ClearTextTag", "Text " + cleanText);
        Spannable spannable = new SpannableString(cleanText);
        List<String> tagsFromString = new ArrayList<>();
        List<Integer> startTextPosition = new ArrayList<>();
        List<Integer> endTextPosition = new ArrayList<>();
        for (Element tag : tags) {
            String nodeText = tag.text();
            if (myCustomTag.contains(tag.tagName())) {
                int startingIndex = cleanText.indexOf(nodeText);
                tagsFromString.add(tag.tagName());
                startTextPosition.add(startingIndex);
                endTextPosition.add(startingIndex + nodeText.length());
            }
        }

        Typeface CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.akronim);
        Typeface SECOND_CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.baloo_thambi);
        Typeface XYZ_CUSTOM_TYPEFACE = ResourcesCompat.getFont(this, R.font.architects_daughter);


        for (int i = 0; i < tagsFromString.size(); i++) {
            String fontName = tagsFromString.get(i);
            Typeface selected = null;
            switch (fontName) {
                case "akronim":
                    selected = CUSTOM_TYPEFACE;
                    break;
                case "baloo_thambi":
                    selected = SECOND_CUSTOM_TYPEFACE;
                    break;
                case "xyz":
                    selected = XYZ_CUSTOM_TYPEFACE;
                    break;
            }
            if (selected != null)
                spannable.setSpan(new MultipleFamilyTypeface(fontName, selected), startTextPosition.get(i), endTextPosition.get(i), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        }


        return spannable;
    }

Мощно

enter image description here

...