Использование RichText в Android. Spannable

Привет Хабраобщество! Эта статья об использовании Spannable в Android. Предназначен он для стилизации текста. Если Вас интересует как реализовать нечто подобное:



тогда добро пожаловать под кат. Статья ориентированная на разработчиков, у которых нет большого опыта разработки под Android.


Теория


Spannable — интерфейс, который описывает маркировку обычного текста некими объектами. Задание этих объектов заключается в присвоению части текста некоторого определенного стиля. Такими маркировочными объектами могут быть экземпляры классов, которые реализуют интерфейс ParcelableSpan. Добавление маркировки осуществляется методом:

Spannable.setSpan(Object span, int start, int end, int flags);

Удаление, соответственно, методом:

Spannable.removeSpan(Object span);

Теории немного, перейдем сразу к практике.

Практика


Для освоения практики необходимо создать android проект или открыть уже существующий. Я создал чистый проект, на главную activity разместил один TextView. Идем в метод, где будем инициализировать наш TextView (у меня onCreate) и добавляем следующий текст:

// Create spannable text and set style.
Spannable text = new SpannableString("This is underline and bold text.");
text.setSpan(new UnderlineSpan(), 8, 17, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new StyleSpan(Typeface.BOLD), 22, 26, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
// Set spannable text in TextView.
TextView textView = (TextView) findViewById(R.id.text);
textView.setText(text);

Итак, разберемся что же мы написали. SpannableString — класс, который реализует интерфейс Spannable (с другими реализациями можно ознакомится на сайте официальной документации). UnderlineSpan — реализация ParcelableSpan, маркирует часть текста как подчеркнутый (в нашем примере это с 8-ой по 17-тую букву). Флаг Spanned.SPAN_EXCLUSIVE_EXCLUSIVE обозначает, что наш span не будет расширятся на вставки текста слева или справа от маркированной части. (Со всеми флагами можно ознакомится на официальном сайте, при работе с текстами readonly они не столь важны).

Также здесь ми использовали еще одну реализацию ParcelableSpanStyleSpan. Как вы уже догадались, с его помощью можно маркировать текст как полужирный (Typeface.BOLD) или курсив (Typeface.ITALIC).

С кодом ми разобрались, запускаем наш проект и смотрим на результат. Текст должен выглядеть следующим образом:



Хорошо, идем дальше: добавим на наше activity кнопку, а в инициализирующий метод следующий текст:

// Create spannable text and set style.
Spannable buttonText = new SpannableString("Italic text");
buttonText.setSpan(new StyleSpan(Typeface.ITALIC), 0, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        
// Set spannable text in TextView.
Button button = (Button) findViewById(R.id.button);
button.setText(buttonText);

На этот раз не будем разбирать код, ничего нового уже для нас нет. Мы использовали маркировку курсивом, которую я описал выше. Запускаем, смотрим.



Мы смогли вывести стилизованный текст на кнопку.

Пойдем еще дальше, реализуем обработчик события клика кнопки, в нем пишем следующий код:

Spannable text = new SpannableString("Italic green text in toast");
text.setSpan(new StyleSpan(Typeface.ITALIC), 0, 18,  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new ForegroundColorSpan(Color.GREEN), 0, 18,  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Toast.makeText(this, text, Toast.LENGTH_LONG).show();

Здесь у нас появляется новый класс — ForegroundColorSpan, который задает цвет нашему тексту, в примере мы задали зеленой. Мы его используем в паре с StyleSpan. Запускаем приложение, смотрим на результат.



Мы смогли запихнуть стилизованный текст даже в Toast. Также мы показали, что для одной части текста можно использовать несколько маркировок со стилями.

Вместо заключения


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

Целью статьи было в доступной форме показать столь сильный и простой инструмент для стилизации своего приложения разработчикам, которые не знали о его существовании или просто не использовали его. Надеюсь что статья будет полезна.

Исходники примера на Github.
Share post

Comments 19

    –2
    Позвольте вопрос: зачем это, если TextView и TextEdit вполне съедает html-разметку?
      +1
      В отличии от html разметки такой подход значительно выигрывает когда нужно, например, динамически менять стиль текста. Если вы будете делать это тегами, то вы, как минимум, офигеете туда-сюда менять теги в разметке. Так же, на сколько я помню, в Toast'ы нельзя пихать html. Хотя, может я и ошибаюсь. Может и ещё есть какие-нибудь плюсы?
        +1
        Можно.
        Toast.makeText(this, Html.fromHtml(«Hello world»), Toast.LENGTH_SHORT).show();

        Только вот, другое дело, что сам класс Html занимается только тем, что парсит HTML-разметку и расставляет нужные Span'ы в нужных местах. Соответственно, если использовать Spannable напрямую, будет некоторый выигрыш в производительности, т.к. отпадает необходимость парсить HTML.
          0
          В Toast можно пихать все, что душе угодно. Метод setView там зачем?
            0
            Хорошо, тогда этот довод отпадает. Но первый — действительно стоящий, согласитесь?
              0
              А никто не сказал, что надо будет пинать сырую строку с html, можно использовать некий класс-контейнер, который в итоге будет генерировать строку. Хотя один хрен класс Html работает с тем же Spannable. Сам его использовал для отображения Markdown подобного синтаксиса, довольно мощный и удобный инструмент.
          +2
          Например, можно подменять в тексте коды emoji картинками смайлов соответствующих, используя ImageSpan.
            0
            Если сохранять стилизирований текст (в ресурсах, базе данных), тогда использовать html-разметку будет лучший вариант, который реализован в пакете android. Если же у нас стоит задание стилизировать текст, который формуется динамически, тогда лучше использовать подход, который описан в статье.
              0
              Спасибо всем ответившим, вопрос снимается :-)
          • UFO just landed and posted this here
              0
              Извините, не заметил что при вставке рисунков полезли такие артефакты, сам текст счел читаемым.
              +1
              Статья интересная, но мне кажется, что в современных андроид-проектах засорять код стилями не очень хорошо. Это удобнее делать в XML.
                0
                А в чем отличие современных проектов от До-современных? :)
                Вопрос о том, засоряют ли стили код, тоже обсуждаемый!
                  0
                  XML не засоряет код потому что, как правило, находится в другом модуле. В ситуации когда быстродействие важно, все стили можно вынести в другой класс или модуль, и даже хорошо организовать локализацию. При этом стили ни коим случаем не будут засорять саму бизнес логику проекта.
                    0
                    Вы путаете код в примере с реальным приложением. Конечно, если работать с текстом, который определен ещё на этапе создания приложения, то нет сомнений — место ему в ресурсах и там сразу прописать оформление с помощью HTML-разметки. Но, когда приходится работать с текстом извне, то такое решение может быть достойной альтернативой встраиванию WebView в приложение.
                    Для себя счел пост интересным. Удивлен, что пропустил данную функциональность в своё время.
                      0
                      Извините, но я не перепутал, и мой предыдущий комментарий не имеет много общего с кодом в примере.
                      Я имел ввиду что если для нашего приложения слишком затратно время парсинга html-разметки, но мы можем себе позволить дополнительное время при старте проекта, то, как альтернативу, можно использовать заранее созданные статические ресурсы в java классах. Этот подход не столь изящный, но имеет право на существование.
                      На счет данной функциональности, я тоже не новичок но узнал про нее относительно недавно, рад что пост Вам был интересен.
                        0
                        Судя по расположению вашего комментария в дереве постов расценил его как ответ для @aleseevpg, к тому же в вашем ответе нет ни слова о производительности. Вот и расценил его как рассуждение о том, стоит ли использовать Spannable вообще для оформления текста.
                          0
                          Но это был ответ для Kalpazan, и о быстродействие там упоминается. Но спасибо Вас за рассуждения.
                  0
                  Перепутал ветку, уже перенес данный комментарий.

                  Only users with full accounts can post comments. Log in, please.