Рисуем знак рубля в Android приложении

в 13:06, , рубрики: typeface, знак рубля, Разработка под android, рубль

В последнее время перед разработчиками все чаще ставится задача использовать символ рубля в тексте. Однако, символ рубля был утвержден относительно недавно, символ получил свой код в стандате Unicode еще позже. Естественно, гарнитура Roboto на текущих платформах еще не содержит знака рубля.

Идея

Создать гарнитуру состоящую из одного символа рубля с кодом U+20BD, и при отрисовке текста для символов рубля использовать эту гарнитуру.

Немного о Span

В ОС Android существует механизм маркировки строк специальными объектами влияющими на рендеринг текста TextView. Например, существуют объекты для переопределения цвета заливки/фона, стиля или всего сразу (полный список встроенных в Android маркеров). Об этом механизме уже писале на хабре здесь и здесь.

Создание нужной гарнитуры

За основу будущей гарнитуры я взял готовое решение от Артемия Лебедева, о котором писали некоторое время назад на Хабре. Эта гарнитура состоит из различных начертаний символа рубля закрепленных за символами латинского алфавита (от строчной a до s).

Список всех глифов

Я выбрал глиф, который закреплен за буквой i (Символ рубля). Как мне кажется, он наиболее подходит для использования с гарнитурой Roboto.
Для редактирования гарнитуры, я использовал замечательное приложение Glyphs. Я удалил все глифы из гарнитуры и оставил только выбранный. Ему я назначил код U+20BD. Следующий шаг — экспорт в ttf.
В итоге у меня получилось так.
Конечно, можно было бы отредактировать глиф заглавной Р, дорисовав палочку, однако, я не чувствую в себе уверенности в этом деле. Уж больно много нюансов. Если кто-то может сделать это, с удовольствием приму ваш pull request.

Реализация

Для начала создадим проект из шаблона. Я использовал шаблон с Blank Activity из комплекта Android Studio, activity назвал MainActivity.
Разметка для MainActivity выглядит так выглядит так:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/main_price_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="numberDecimal"
        tools:hint="@string/main_price_hint"
        />

</LinearLayout>

Тут все просто, создаем LinearLayout с одним потомком EditText. Свойство hint у EditText мы задаем с namespace tools, для того что бы подсказка отображалась только в предпросмотре. В программе мы будем задавать подсказку программно.

В MainActivity#onCreate мы должны составить spanned string, в котором все знаки рубля будут маркированны TypefaceSpan, который позволяет изменить гарнитуру для отрисовки заданных символов. Тут нас ожидает маленькая неприятность: TypefaceSpan можно создать только с font-family — названием шрифта из набора системы. К счастью, судя по исходному коду TypefaceSpan, этот подход не обусловлен техническими возможностями системы рендеринга текста, что позволяет нам создать собственную версию TypefaceSpan, который поддерживает задание гарнитуры непосредственно объектом Typeface. Копируем

package me.pepyakin.roublesign;

import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;

public class TypefaceSpan2 extends MetricAffectingSpan {
    private final Typeface mTypeface;

    public TypefaceSpan2(Typeface typeface) {
        mTypeface = typeface;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        apply(ds, mTypeface);
    }

    @Override
    public void updateMeasureState(TextPaint paint) {
        apply(paint, mTypeface);
    }

    private static void apply(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);
    }
}

Осталось только сделать маркировку текста с помощью объявленного выше TypefaceSpan2, с необходимой нам гарнитурой.

String priceHint = getString(R.string.main_price_hint);

final Typeface roubleSupportedTypeface =
        Typeface.createFromAsset(getAssets(), "fonts/rouble2.ttf");

SpannableStringBuilder resultSpan = new SpannableStringBuilder(priceHint);
for (int i = 0; i < resultSpan.length(); i++) {
    if (resultSpan.charAt(i) == 'u20BD') {
        TypefaceSpan2 roubleTypefaceSpan = new TypefaceSpan2(roubleSupportedTypeface);
        resultSpan.setSpan(roubleTypefaceSpan, i, i + 1, 0);
    }
}

Здесь мы загружаем шаблон нашей строки, содержащей символ рубля и гарнитуру с глифом рубля.
Затем создаем SpannableStringBuilder на основе шаблона и с помощью setSpan расставляем маркеры для смены гарнитуры.

Назначаем hint,

priceInput.setHint(resultSpan); 

и готово!
Готовый результат

Ссылка на исходники

Автор: knott

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js