В этом уроке:
- используем 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
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня