- Класс Canvas
- Методы
- Метод drawArc()
- Метод drawBitmap()
- Метод drawCircle()
- drawLine(s)()
- Метод drawOval()
- Метод drawPaint()
- Метод drawRect()
- Метод drawRoundRect()
- Метод drawPath()
- Метод drawPoint()
- Метод drawText()
- Центрируем текст
- Методы rotate() и restore()
- Методы scale() и translate()
- Пишем игру для Android. Часть 2 — Вывод рисунка на экран и его перемещение
- Перемещение изображения
- Самостоятельное перемещение робота
Класс Canvas
Класс android.graphics.Canvas (Холст) предоставляет методы для рисования, которые отображают графические примитивы на исходном растровом изображении. При этом надо сначала подготовить кисть (класс Paint), который позволяет указывать, как именно графические примитивы должны отображаться на растровом изображении (цвет, обводка, стиль, сглаживание шрифта и т.д.).
Android поддерживает полупрозрачность, градиентные заливки, округлённые прямоугольники и сглаживание. Из-за ограниченных ресурсов векторная графика пока что не поддерживается, вместо этого используется традиционная растровая перерисовка.
Canvas работает с пикселями, поэтому следует заботиться о конвертации единиц dp в px и наоборот при необходимости. Начало координат находится в левом верхнем углу.
Получить доступ к холсту можно через объект Bitmap или компонент View. Очень часто разработчики создают свой собственный компонент, наследуясь от View, и рисуют на его холсте для реализации своих замыслов.
Методы
Ниже представлены некоторые методы класса Canvas, которые что-то рисуют.
- drawARGB()/drawRGB()/drawColor(). Заполняет холст сплошным цветом.
- drawArc(). Рисует дугу между двумя углами внутри заданной прямоугольной области.
- drawBitmap(). Рисует растровое изображение на холсте. Вы можете изменять внешний вид целевой картинки, указывая итоговый размер или используя матрицу для преобразования.
- drawBitmapMesh(). Рисует изображение с использованием сетки, с помощью которой можно управлять отображением итоговой картинки, перемещая точки внутри неё.
- drawCircle(). Рисует круг/окружность с определённым радиусом вокруг заданной точки.
- drawLine(s)(). Рисует линию (или последовательность линий) между двумя точками.
- drawOval(). Рисует овал на основе прямоугольной области.
- drawPaint(). Закрашивает весь холст с помощью заданного объекта Paint.
- drawPath(). Рисует указанный контур, используется для хранения набора графических примитивов в виде единого объекта.
- drawPicture(). Рисует объект Picture внутри заданного прямоугольника.
- drawPoint(). Рисует точку в заданном месте.
- drawPosText(). Рисует текстовую строку, учитывая смещение для каждого символа.
- drawRect(). Рисует прямоугольник.
- drawRoundRect(). Рисует прямоугольник с закруглёнными углами.
- drawText(). Рисует текстовую строку на холсте. Шрифт, размер, цвет и свойства отображения текста задаются в соответствующем объекте Paint.
- drawTextOnPath(). Рисует текст, который отображается вокруг определённого контура.
- drawVertices(). Рисует набор треугольников в виде совокупности вершинных (вертексных) точек.
- rotate() и restore(). Вращение холста
- Методы scale() и translate(). Изменение и перемещение координатной системы
Мы уже изучали основы рисования в первом месяце обучения (Работаем с графикой. Основы). Можно вернуться к этому проекту, закомментировать код вывода графики и продолжить изучение рисования при помощи методов класса Canvas.
Метод drawArc()
В API 21 появилась перегруженная версия метода, в котором можно указать координаты двух точек вместо RectF.
Метод drawArc() позволяет рисовать дуги и сектора. Ниже приводится код для трёх вариантов: сектор с заливкой (похож на PacMan), сектор без заливки (контур) и часть дуги:
Метод drawBitmap()
Вывести готовое изображение просто.
Метод drawCircle()
Первые два аргумента определяют координаты центра окружности/круга, следующий аргумент — её радиус в пикселах, последний — объект Paint. В зависимости от выбранного стиля кисти можно нарисовать закрашенный круг или только окружность.
Нарисуем зелёный круг.
drawLine(s)()
Простой метод — указываем начальные и конечные координаты отрезка.
Метод drawOval()
Метод drawOval() рисует овалы. Естественно, если вы зададите одинаковые размеры ширины и высоты, то получите круг/окружность.
Если вам нужно наклонить овал в ту или иную сторону, то поверните холст на требуемый угол с помощью метода rotate(). Не забудьте потом повернуть холст обратно, что следующие фигуры выводились нормально.
Повернём синий овал из предыдущего примера:
В API 21 появилась перегруженная версия метода, в котором можно указать координаты двух точек вместо RectF:
Метод drawPaint()
Метод позволяет закрасить весь холст одним цветом.
Метод drawRect()
У метода существует три перегруженные версии для рисования прямоугольника. Рассмотрим один из них:
Метод drawRoundRect()
Для рисования прямоугольников с закруглёнными углами используется метод drawRoundRect (RectF rect, float rx, float ry, Paint paint).
В параметрах указываются ограничивающий прямоугольник, радиусы овалов для скругления углов и кисть.
Реализуем три разных способа:
В API 21 появилась перегруженная версия метода, в котором можно указать координаты двух точек вместо RectF.
Метод drawPath()
Для рисования соединённых отрезков можно использовать метод drawPath(), указав в параметрах настройки для рисования и массив координат точек. Для удобства добавим в класс Draw2D новый класс Pt, который позволит быстро создать массив точек с заданными координатами. Далее настраиваем объекты для рисования и формируем путь через созданный массив. В результате получим кошкин дом.
Путь можно составлять не только из точек, но и из фигур, например, дуг. Сначала формируем дугу, добавляем её в путь при помощи метода Path.addArc(), повторяем операцию снова несколько раз, а в конце выводим окончательный вариант:
Можно нарисовать символ парашюта:
Метод drawPoint()
Простой метод для рисования точки в нужно месте указанной кистью. Для координат используются значения типа float.
Метод drawText()
С помощью метода drawText() можно выводить текст в заданной позиции. Добавим сначала несколько эффектов, чтобы казалось, что текст парит над поверхностью:
Центрируем текст
Есть небольшая тонкость, если вам захочется вывести текст в центре холста. Проблем с вычислением центра холста и размером текста нет. Центр можно найти, разделив пополам значения ширины и высоты холста. А ширину и высоту текста можно узнать через метод кисти getTextBounds(), который возвращает ограничивающий прямоугольник.
Но вычисление ширины текста через textBounds.width(); приводит к небольшому смещению. Лучше воспользоваться методом кисти measureText(). Тогда текст отцентрируется точнее.
Пример на Kotlin с дополнительной информацией.
Методы rotate() и restore()
Холст во время рисования можно вращать. Во многих ситуациях такой приём менее затратный по ресурсам, чем рисование самого объекта под углом. Суть в следующем: вы поворачиваете холст на нужный градус, рисуете фигуру, а затем возвращаете холст на место при помощи метода restore(), чтобы следующие фигуры рисовались в ожидаемых местах. Иначе остальные фигуры будут рисоваться уже относительно поворота.
В примере с овалом уже использовался данный метод. В примере Работаем с графикой. Основы мы также поворачивали холст, чтобы вывести текст под углом.
Вращение происходит вокруг начальной точки холста (0, 0). Но можно также использовать перегруженную версию метода rotate(float degrees, float px, float py), в которой можно указать координаты точки поворота.
Методы scale() и translate()
Стандартная система координат начинает свой отсчёт с верхнего левого угла. Иногда, для рисования сложных фигур удобнее назначить свою систему координат. Например, для рисования циферблата часов удобнее рисовать относительно центра экрана в диапазоне от -1 до 1.
Чтобы установить свою систему координат, нужно произвести трансформацию. В следующем примере мы установим координаты в диапазоне от 0 до 10 и нарисуем график в стандартном виде из точки 0,0 в левом нижнем углу в точку 10,10 в верхнем правом углу.
Для наглядности я добавил на оси несколько точек. Следует обратить внимание, что мы задали диапазон от 0 до 10 и все размеры должны масштабироваться в новых величинах, в том числе и ширина обводки в методе setStrokeWidth(). Поэтому значения должны быть достаточно маленькими, иначе толщина обводки может просто оказаться больше самой фигуры. Кстати, в некоторых случаях с текстом и другими методами рисование масштабирование может сыграть злую шутку и дробные значения не позволят увидеть текст и некоторые линии. В этих случаях приходиться создавать цепочку преобразований, когда временно масштаб увеличивается до нормальных размеров, рисуется текст с подходящим размером шрифта, затем опять всё уменьшается и т.д. Это долгая история.
Источник
Пишем игру для Android. Часть 2 — Вывод рисунка на экран и его перемещение
Как Вы помните,первый урок цикла носил несколько обзорный характер. Мы пофилософствовали на тему аркадного игростроения, обсудили модули, из которых будет состоять наша игра, и написали небольшую заготовку, которую мы будем в дальнейшем развивать и усиливать. Если Вы по каким-то причинам пропустили этот урок, настоятельно рекомендую Вам все-таки ознакомиться с ним. Кстати, там же можно скачать исходник проекта, именно он является отправной точкой для этого урока.
Сегодня мы займемся графикой. Вывод изображения на экран телефона в Android задача почти тривиальная. Не будем ходить вокруг да около, а сразу займемся делом. Для начала выведем изображение робота в верхнем левом углу экрана. Для работы с графикой предпочтительно использовать png формат. Наш рисунок будет называтьсяdroid_1.png и иметь размер 20×20 пикселей. Для того, чтобы с рисунком стало можно работать из приложения, просто скопируйте его в папку/res/drawable-mdpi вашего проекта. Проще всего это сделать, перетащив файлик в соответствующую папку в дереве проектов в Eclipse.
Кстати, обратите внимание, в проекте существует несколько папок drawable. Дело в том, что ваша программа может запускаться на устройствах с разными размерами и разрешениями экрана, поэтому имеет смысл подготовить несколько наборов графических файлов, отличающихся размером картинок. В зависимости от того, где будет работать программа, выбирается тот или иной набор. Я выбрал папку drawable-mdpi, которая соответствует устройствам со средней плотностью точек на экране.
Откройте файл MainGamePanel.java и измените метод onDraw(Canvas canvas)
Метод drawBitmap выводит изображение droid_1.png на экран, причем левый верхний угол этого изображения помещается в точку с координатами x=10, y=15 (напоминаю, ось x направлена вправо, y — вниз, начало координат — верхний левый угол экрана). Для вывода изображения мы использовали id нашего ресурса (картинки внутри папки res), то есть droid_1. При копировании картинки в папку /res/drawable-mdpi Eclipse автоматически внес изменение в файл R.java и имя файла стало его идентификатором. В случае, если вы скопировали файл в папку проекта не через Eclipse, а стандартными средствами вашей операционной системы, то файл R.java не изменится. Более того, если Вы раскроете папку res, то не увидите там только что скопированного файла. Чтобы обновить проект щелкните мышкой по папке res в дереве проектов в Eclipse и нажмите кнопку F5, либо выберите Refresh из контекстного меню.
Помимо метода onDraw нам понадобится также внести некоторые изменения в поток MainThread. Откройте файл MainThread.java и измените метод run()
Как видите, мы объявили переменную canvas типа Canvas. Canvas очень распространенная в графических библиотеках концепция. Наиболее адекватно на русский язык canvas переводится как холст. Можно воспринимать этот объект, как лист бумаги, на который можно нанести рисунок. Объект Canvas содержит ряд команд, позволяющие выполнять различные операции рисования.
Мы пытаемся инициализировать эту переменную с помощью метода surfaceHolder.lockCanvas() — то есть связать переменную с активной поверхностью view. Этот код помещен в операторные скобки try <. >finally < . >. Если внутри блока try произойдет ошибка, ну например по каким-то причинам нам не удастся заблокировать canvas под себя, то программа не завершится, а выполнится код в блоке finally. Если нам удастся получить в свое распоряжение Canvas, мы передаем его в качестве параметра методу gamePanel.onDraw, внутри которого и осуществляется рисование.
Обратите также внимание на блок synchronised. Включив этот блок в наш код, мы указываем, что получаем surfaceHolder в свое полное распоряжение, блокируем его. Это значит, что до тех пор, пока блок synchronised не закончится, никто другой параллельно не сможет работать с этим объектом.
Описанная выше логика построения программы довольно проста, тем не менее, эта заготовка может быть использована для создания подавляющего большинства аркадных игр. Запустите программу, Вы увидите следующую картинку.
Перемещение изображения
Итак, мы научились выводить картинку на экран, давайте заставим ее двигаться. Как? Используем наши пальцы. Напишем простую реализацию drag and drop функции (кстати, более подробно об drag and drop рассказанов этой статье). Чтобы переместить рисунок, мы должны щелкнуть по нему и переместить в точку, соответствующую координатам касания. После того, как пользователь отрывает палец от экрана, координаты этой точки запоминаются и рисунок выводится туда.
Давайте создадим для нашего робота отдельный класс Droid.java, в котором определим поля для хранения координат и, собственно, рисунка.
Класс очень простой. В нем определены три поля (x,y и bitmap), методы get и set, позволяющие считывать и записывать их значения, а также конструктор, внутри которого происходит начальная инициализация полей класса.
Нам также понадобятся переменные, для хранения состояния робота. В рамках решаемой в данный момент задачи робот может находиться в одном из двух состояний «перетаскивается» или «не перетаскивается». Определить эти состояния можно с помощью логической переменной touched. В момент, когда пользователь коснулся изображения робота, эта переменная должна принимать значение true. False она примет лишь в момент, когда палец будет снят с экрана.
Давайте немного доработаем класс Droid. Добавим в него метод, осуществляющий рисование draw и метод обработки касания handleActionDown.
Методы draw и handleActionDown обсудим чуть позже, а пока давайте немного поправим код в файле MainGamePanel.java.
В конструкторе класса MainGamePanel мы создали объект droid. В методе onTouchEvent обрабатывается событие касание экрана (MotionEvent.ACTION_DOWN). Если касание произошло, мы вызываем метод handleActionDown, куда передаем координаты. Приведем еще раз листинг этого метода.
Мы сравниваем координаты касания с координатами робота, и если касание происходит внутри прямоугольной области, соответствующей картинке робота, мы изменяем его состояние setTouched(true); в противном случае меняем на false.
Вернемся к методу onTouched. Помимо события MotionEvent.ACTION_DOWN, там обрабатывается событие MotionEvent.ACTION_MOVE. Оно возникает при перемещении пальца по экрану. Внутри обработчика этого события, мы проверяем состояние робота (значение поля touched) и если оно равно true — обновляем координаты робота. В обработчике события MotionEvent.ACTION_UP мы переводим состояние робота в false.
Перейдем к методу Draw. Как помните, он вызывается у нас на каждом шаге игрового цикла. Внутри этого метода мы красим экран в черный цвет, а затем вызываем метод draw объекта droid, который выводит робота на экран.
Можете запустить проект и попытаться поперетаскивать робота по экрану.
Самостоятельное перемещение робота
Давайте теперь придадим нашему роботу самостоятельности и заставим его путешествовать по экрану без вмешательства пользователя. Робот не должен выходить за пределы экрана. Если он дошел до края — он должен изменить направление своего движения. Если наш объект движется самостоятельно — у него должна иметься какая-то скорость. Величина этой скорости влияет на изменение координат. Для понятия «скорость» мы реализуем собственный класс Speed.
Создайте класс Speed.java
Робот имеет горизонтальную и вертикальную составляющую скорости. На каждом шаге цикла к текущей координате робота будим прибавлять соответствующую скорость (на самом деле в физическом смысле правильнее было бы прибавлять величину скорости, умноженную на время прошедшее с прошлой операции перемещения объекта, но мы в целях упрощения не будем так сильно заморачиваться). В конструкторе переменным vx и vy мы присвоили значение 1, то есть на каждом шаге игрового цикла робот будет смещаться на 1 пиксел. Если vx > 0 — робот движется вправо, если vx 0-вниз, vy
Источник