В этом уроке:
- используем Region
Region - это объект, который позволяет нам совмещать несколько фигур в одну, используя различные режимы: объединение, пересечение и пр. На словах трудновато будет объяснить подробно, поэтому давайте пример смотреть.
Создадим проект:
Project name: P1471_Region
Build Target: Android 2.3.3
Application name: Region
Package name: ru.startandroid.develop.p1471region
Create Activity: MainActivity
MainActivity.java:
package ru.startandroid.develop.p1471region;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RegionIterator;
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 p;
Rect rect1;
Rect rect2;
Region region;
RegionIterator iterator;
Path path;
Region.Op op = Region.Op.UNION;
public DrawView(Context context) {
super(context);
p = new Paint();
p.setStrokeWidth(3);
// прямоугольники
rect1 = new Rect(200,200,400,400);
rect2 = new Rect(300,300,500,500);
// создание региона
region = new Region();
region.set(rect1);
region.op(rect2, op);
// создание path из региона
path = region.getBoundaryPath();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
// контуры прямоугольников
p.setStyle(Paint.Style.STROKE);
p.setColor(Color.BLACK);
canvas.drawRect(rect1, p);
canvas.drawRect(rect2, p);
// path
p.setStyle(Paint.Style.FILL);
p.setColor(Color.BLUE);
canvas.drawPath(path, p);
}
}
}
В конструкторе DrawView создаем объекты. У нас в примере будут участвовать два прямоугольника rect1 и rect2. Как видно по их координатам, они пересекаются. Далее создаем регион и методом set присваиваем ему первый прямоугольник (rect). Регион теперь состоит из одного прямоугольника. Чтобы добавлять к нему дополнительные прямоугольники, необходимо использовать метод op. Добавляем второй прямоугольник (rect2) и при этом указываем режим Region.Op.UNION (переменная op).
Если при добавлении нового прямоугольника к региону используется режим UNION, то итоговый регион будет являться объединением области текущего региона и добавляемого прямоугольника. В нашем случае регион состоял из первого прямоугольника, а значит результатом добавления второго будет объединение областей первого и второго прямоугольника.
Далее методом getBoundaryPath получаем итоговую область региона в объект Path, чтобы можно было нарисовать результат объединения.
В методе onDraw сначала рисуем черным цветом контуры прямоугольников. Затем синим цветом с заливкой рисуем path, который представляет из себя итоговую область региона.
Результат:

Видим, что регион представляет собой объединение двух прямоугольников. Объединение мы получили, т.к. использовали режим UNION.
Мы рассмотрели один режим добавления, а всего их 6. Смотрим остальные. Для этого в нашем коде используется переменная op:
Region.Op op = Region.Op.UNION;
Сейчас тут значение UNION. Просто меняйте его на рассматриваемые нами далее режимы.
Помним, что регион изначально содержит первый прямоугольник. А второй прямоугольник мы добавляем с использованием определенного режима.
XOR

Итоговая область региона: области обоих прямоугольников за исключением их пересечения.
DIFFERENCE

Итоговая область региона: область первого прямоугольника за исключением пересечения его со вторым.
REVERSE_DIFFERENCE

Итоговая область региона: область второго прямоугольника за исключением пересечения его с первым.
INTERSECT

Итоговая область региона: пересечение обоих прямоугольников
REPLACE

Итоговая область региона: второй прямоугольник.Т.е. содержимое региона заменилось вторым прямоугольником.
В хелпе можно увидеть, что метод op имеет несколько вариантов, но в целом смысл везде одинаков – добавление прямоугольника или целого региона к текущему региону с использованием режимов.
RegionIterator
Итоговая область региона может быть разбита на набор непересекающихся прямоугольников. Для этого используется RegionIterator – итератор региона. При создании указываете ему регион и методом next перебираете прямоугольники, из которых состоит регион.
Ради интереса повесьте лог в цикл итератора и выведите (Rect.toShortString) координаты областей, из которых состоит регион в примерах выше. Вы увидите, как итератор разбивает регион на непересекающиеся прямоугольники.
В случае с UNION, например, лог будет следующим:
rect = [200,200][400,300]
rect = [200,300][500,400]
rect = [300,400][500,500]
Прочие методы
Рассмотрим еще несколько полезных методов региона.
contains – позволяет определить, содержится ли указанная точка в регионе
getBounds – вернет нам прямоугольник, который является общими границами региона
isComplex – вернет true, если регион состоит из более, чем одного прямоугольников. Причем имеется ввиду вовсе не количество добавленных к региону прямоугольников. Здесь речь о том, сколько прямоугольников содержит итератор региона.
isRect – вернет true, если итоговая область региона является единым прямоугольником
quickContains – вернет true если регион является единым прямоугольником и содержит в себе переданный ему прямоугольник. При этом false вовсе не означает, что переданный прямоугольник обязательно НЕ содержится в этом регионе.
quickReject – вернет true, если регион пуст или не пересекается с переданным прямоугольником/регионом. При этом false вовсе не означает, что переданный прямоугольник/регион обязательно НЕ пересекаются с текущим.
setPath – позволяет нам отсекать от переданного Path кусок, ограниченный переданным регионом. Отсеченный кусок будет итоговой областью текущего региона.
Посмотрим на примере, перепишем DravView:
class DrawView extends View {
Paint p;
Region region;
Region clipRegion;
Path path;
Path pathDst;
Rect rect;
public DrawView(Context context) {
super(context);
p = new Paint();
p.setStrokeWidth(3);
p.setStyle(Paint.Style.STROKE);
// path, треугольник
path = new Path();
path.moveTo(100, 100);
path.lineTo(150, 150);
path.lineTo(100, 200);
path.close();
// регион из прямоугольника обрезки
rect = new Rect(100, 100, 150, 150);
clipRegion = new Region(rect);
// итоговый регион
region = new Region();
// отсекаем от path область clipRegion
region.setPath(path, clipRegion);
// получаем path из региона
pathDst = region.getBoundaryPath();
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
// треугольник
p.setColor(Color.GREEN);
canvas.drawPath(path, p);
canvas.translate(200, 0);
// верхняя часть треугольника
p.setColor(Color.BLUE);
canvas.drawPath(pathDst, p);
}
}
Все основные операции происходят в конструкторе DrawView. Сначала создаем path, в виде треугольника. Затем создаем прямоугольник rect, который, как видно по координатам, заключает в себе верхнюю половину треугольника. Именно эту часть мы сейчас и будет отделять от path. Создаем регион clipRegion, итоговой областью которого будет являться rect.
Далее создаем новый регион и выполняем для него метод setPath. На вход передаем path, от которого надо отделить часть, и регион в пределах которого, находится эта отделяемая часть. В итоге переменная region у нас теперь содержит верхнюю отделенную часть треугольника. Формируем из него новый Path в переменную pathDst методом getBoundaryPath.
В onDraw выводим зеленым цветом изначальный треугольник, а синим цветом рисуем его отрезанный верхний кусок.
Результат:

Я ради интереса создал итератор для верхней половины треугольника и вот, что получил
[300,100][301,101]
[300,101][302,102]
[300,102][303,103]
...
[300,147][348,148]
[300,148][349,149]
[300,149][350,150]
Видно, что регион разбил треугольник на множество прямоугольников с высотой = 1.
В общем, регион - штука специфическая, и для некоторых операций - незаменимая. Мне, например, совсем недавно он пригодился, чтобы для картинки сделать карту изображений.
На следующем уроке:
- используем clip
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

