Pull to refresh

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

Reading time 4 min
Views 28K
В последнее время перед разработчиками все чаще ставится задача использовать символ рубля в тексте. Однако, символ рубля был утвержден относительно недавно, символ получил свой код в стандате 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); 

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

Ссылка на исходники
Tags:
Hubs:
+16
Comments 4
Comments Comments 4

Articles