В этом уроке:
- работаем с текстом
В прошлых уроках мы уже выводили текст, настраивали для него выравнивание, использовали path для указания линии текста. Посмотрим какие еще операции доступны при работе с текстом.
Определение размеров
Есть несколько методов, позволяющих определить размеры текста.
Для начала определим ширину всего текста и каждого символа по отдельности.
Создадим проект:
Project name: P1491_CanvasText
Build Target: Android 2.3.3
Application name: CanvasText
Package name: ru.startandroid.develop.p1491canvastext
Create Activity: MainActivity
MainActivity.java:
package ru.startandroid.develop.p1491canvastext;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawView(this));
}
class DrawView extends View {
Paint fontPaint;
Paint redPaint;
String text = "Test width text";
int fontSize = 100;
float[] widths;
float width;
public DrawView(Context context) {
super(context);
redPaint = new Paint();
redPaint.setColor(Color.RED);
fontPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
fontPaint.setTextSize(fontSize);
fontPaint.setStyle(Paint.Style.STROKE);
// ширина текста
width = fontPaint.measureText(text);
// посимвольная ширина
widths = new float[text.length()];
fontPaint.getTextWidths(text, widths);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
canvas.translate(50, 250);
// вывод текста
canvas.drawText(text, 0, 0, fontPaint);
// линия шириной в текст
canvas.drawLine(0, 0, width, 0, fontPaint);
// посимвольные красные точки
canvas.drawCircle(0, 0, 3, redPaint);
for (float w : widths) {
canvas.translate(w, 0);
canvas.drawCircle(0, 0, 3, redPaint);
}
}
}
}
В конструкторе DrawView мы создаем и настраиваем fontPaint, который будет использован для вывода текста. Здесь же мы вычисляем размеры текста. Метод measureText вернет ширину указанного текста. Сохраним ее в переменную width. А метод getTextWidths позволяет получить массив, содержащий значения ширины для каждого символа текста. Используем массив widths, размер которого равен кол-ву символов в тексте.
В onDraw рисуем текст. А под текстом рисуем линию шириной равной ранее полученной ширине текста - width, и используя массив widths выводим красные точки, отмечая ширину каждого символа.
Результат:

Далее рассмотрим метод breakText. Он позволит нам узнать сколько символов текста поместится в указанную нами ширину.
Перепишем класс DrawView:
class DrawView extends View {
Paint p;
String text = "Test width text";
int fontSize = 80;
int maxWidth = 350;
float realWidth = 0;
int cnt = 0;
String info = "";
public DrawView(Context context) {
super(context);
p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setTextSize(fontSize);
// кол-во символов и их ширина
float[] measuredWidth = new float[1];
cnt = p.breakText(text, true, maxWidth, measuredWidth);
realWidth = measuredWidth[0];
info = "cnt = " + cnt + ", realWidth = " + realWidth
+ ", maxWidth = " + maxWidth;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
// данные о ширине
p.setTextSize(24);
canvas.drawText(info, 50, 50, p);
// текст
p.setTextSize(fontSize);
canvas.drawText(text, 50, 250, p);
p.setStrokeWidth(10);
// полоса реальной ширины урезанного текста
p.setColor(Color.BLUE);
canvas.drawLine(50, 260, 50 + realWidth, 260, p);
// полоса лимита
p.setColor(Color.GREEN);
canvas.drawLine(50, 270, 50 + maxWidth, 270, p);
}
}
В конструкторе DrawView вызываем метод breakText. На вход передаем:
- текст
- true, означает что пойдем по тексту вперед, начиная с первого символа. Если false, то пойдем с конца.
- ширину, которая будет ограничивать текст
- массив, для получения точного значения ширины
Метод breakText возвращает кол-во символов.
Т.е. мы у объекта Paint спрашиваем, сколько символов указанного текста text влезет в указанную ширину maxWidth. Ответ мы получаем в переменную cnt. А в массив measuredWidth также попадает точная ширина урезанного текста, для удобства сохраним ее в переменную realWidth.
В onDraw выводим текст с полученными данными, искомый текст и две полосы для наглядности. Синяя полоса покажет точную ширину (realWidth) урезанного текста, а зеленая покажет лимит (maxWidth), который мы задавали.
Результат:

maxWidth у нас равен 350. Метод breakText выяснил, что из указанного текста в 350 px влезет лишь 9 символов и они займут 330 px по ширине.
Синяя полоса показывает ширину урезанного текста. Видно что над этой полосой 9 символов (включая пробел).
Зеленая полоса показала лимит, который мы ставили - 350.
Среди методов измерения также есть метод getTextBounds, который позволит получить вам прямоугольник, в который заключен текст.
Шрифты, стили
Рассмотрим возможность использования типов и стилей шрифтов.
Перепишем класс DrawView:
class DrawView extends View {
Paint p;
String text = "Test width text";
int fontSize = 60;
float y = 80;
public DrawView(Context context) {
super(context);
p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setTextSize(fontSize);
p.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
// обычный текст
canvas.translate(50, y);
canvas.drawText(text, 0, 0, p);
// моноширинный
canvas.translate(0, y);
p.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.NORMAL));
canvas.drawText(text, 0, 0, p);
// с засечками
canvas.translate(0, y);
p.setTypeface(Typeface.create(Typeface.SERIF, Typeface.NORMAL));
canvas.drawText(text, 0, 0, p);
// обычный жирный
canvas.translate(0, y);
p.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
canvas.drawText(text, 0, 0, p);
// обычный жирный курсивный
canvas.translate(0, y);
p.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC));
canvas.drawText(text, 0, 0, p);
// обычный курсивный
canvas.translate(0, y);
p.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC));
canvas.drawText(text, 0, 0, p);
}
}
В onDraw мы выводим один и тот же текст, используя различные типы и стили шрифта. Для этого используем метод setTypeface, который требует на вход Typeface. Создать Typeface можно методом create, который требует на вход тип и стиль.
Результат:

Сначала используем шрифт по умолчанию, затем строим различные комбинации
из типов:
MONOSPACE – моноширинный, т.е. ширина всех символов одинакова
SERIF – шрифт с засечками
DEFAULT - шрифт по умолчанию
и стилей:
NORMAL – обычный
BOLD – жирный
BOLD_ITALIC – жирный курсивный
ITALIC - курсивный
Кроме системных шрифтов, существует возможность использовать свои шрифты. Для этого вместо метода Typeface.create необходимо использовать другие его разновидности. Например createFromAsset. Помещаете ваш шрифт в папку assets и в методе createFromAsset указываете имя файла.
Рассмотрим еще несколько методов форматирования текста.
Перепишем класс DrawView:
class DrawView extends View {
Paint p;
String text = "Test width text";
int fontSize = 60;
float y = 80;
public DrawView(Context context) {
super(context);
p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setTextSize(fontSize);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
// обычный текст
canvas.translate(50, y);
canvas.drawText(text, 0, 0, p);
// растянутый
canvas.translate(0, y);
p.setTextScaleX(1.5f);
canvas.drawText(text, 0, 0, p);
p.setTextScaleX(1);
// наклоненный
canvas.translate(0, y);
p.setTextSkewX(0.5f);
canvas.drawText(text, 0, 0, p);
p.setTextSkewX(0);
// подчеркнутый
canvas.translate(0, y);
p.setUnderlineText(true);
canvas.drawText(text, 0, 0, p);
p.setUnderlineText(false);
// зачеркнутый
canvas.translate(0, y);
p.setStrikeThruText(true);
canvas.drawText(text, 0, 0, p);
p.setStrikeThruText(false);
}
}
setTextScaleX – позволяет растянуть/сжать текст
setTextSkewX – наклон текста
setUnderlineText – подчеркнутый текст
setStrikeThruText – зачеркнутый текст
Результат:

Прочее
Метод drawPosText позволяет при выводе раскидать текст посимвольно по различным точкам
Перепишем класс DrawView:
class DrawView extends View {
Paint p;
String text = "Test text";
int fontSize = 100;
float pos[];
public DrawView(Context context) {
super(context);
p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setTextSize(fontSize);
pos = new float[] { 100, 300, 200, 150, 300, 500, 400, 300, 500,
250, 600, 350, 700, 400, 800, 200, 900, 500 };
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
canvas.drawPosText(text, pos, p);
}
}
В конструкторе создаем массив pos. В нем указаны координаты точек, по которым поочередно будут раскиданы символы текста.
В onDraw вызываем drawPosText и передаем ему текст и массив.
Результат:

Методы измерения можно использовать не только в рисовании. Вы можете для обычного TextView получить объект Paint методом getPaint, и далее уже вызывать необходимые вам методы. А метод setTypeface (для задания шрифта) у TextView есть свой.
На следующем уроке:
- используем PathMeasure для работы c Path
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
