- SurfaceView
- Создание нового объекта SurfaceView
- SurfaceView с поддержкой касаний
- Гроза эпилептиков
- Поймай меня
- Отскакивающие значки
- Somethings
- пятница, 27 марта 2009 г.
- Пишем игру для Android. Часть 1. Surface
- Создание проекта
- Surface
- SurfaceView
- SurfaceHolder
- SurfaceHolder.Callback
- Класс для отображения игры
- GameView.java
- main.xml
- Класс для рисования
- GameManager.java
- GameView.java
- GameView.java
- GameView.java
- GameView.java
- Поворот экрана
- GameManager.java
- GameManager.java
- GameView.java
- 25 комментариев:
- Анимация на Android с помощью SurfaceView
SurfaceView
Класс SurfaceView предоставляет объект Surface, который поддерживает рисование в фоновом потоке и дает возможность использовать OpenGL для трехмерной графики. Это отличный вариант для насыщенных графикой элементов, которые нуждаются в частых обновлениях или должны отображать сложную графическую информацию, как в случае с играми и трехмерной визуализацией.
Найти данный элемент можно в разделе Advanced.
В основе SurfaceView объект Surface, а не Canvas. Это важно, потому как Surface поддерживает рисование из фоновых потоков. Данное отличие особенно полезно для ресурсоемких операций или быстрых обновлений, а также когда необходимо обеспечить высокую частоту изменения кадров (использование трехмерной графики, создание игр или предпросмотр видеопотока с камеры в режиме реального времени.
Возможность рисовать вне зависимости от графического потока ведет к повышенному потреблению памяти. Таким образом, хоть это и полезный (а иногда просто необходимый) способ создания нестандартных представлений, будьте осторожны, используя его.
SurfaceView используется точно таким же образом, как любые производные от View классы. Вы можете применять анимацию и размещать их внутри разметки так же, как и другие компоненты.
Применяя OpenGL, вы можете рисовать на Surface любые поддерживаемые двумерные или трехмерные объекты, получая при этом все выгоды от аппаратного ускорения (если таковое имеется). Таким образом, вы значительно повышаете производительность, если сравнивать с теми же операциями, выполненными на двумерном Canvas.
Объекты SurfaceView особенно пригодятся для отображения динамических трехмерных изображений, к примеру, в интерактивных играх, их можно назвать лучшим выбором для отображения предварительного просмотра видеопотоков с камеры в режиме реального времени.
Создание нового объекта SurfaceView
Чтобы создать данный тип, наследуйте класс SurfaceView и реализуйте интерфейс SurfaceHolder.Callback, описывающий функцию обратного вызова. Он уведомляет представление о том, что исходный объект Surface был создан/уничтожен/модифицирован и передает в объект SurfaceHolder ссылку, содержащую допустимый экземпляр Surface.
Типичный шаблон проектирования SurfaceView предусматривает классы, производные от Thread, которые принимают ссылку на текущий объект SurfaceHolder и немедленно его обновляют.
SurfaceView с поддержкой касаний
Напишем простой пример использования SurfaceView, на котором можно рисовать линии.
Запустив проект, вы можете рисовать пальцем по экрану.
Гроза эпилептиков
Попробовал данный пример. При рисовании рисуются окружности, которые постоянно мигают. Жуткое зрелище.
В примере реализовано:
- новый класс MySurfaceView, наследующий от SurfaceView и реализующий интерфейс SurfaceHolder.Callback
- используются методы surfaceCreated(), surfaceDestroyed(), surfaceChanged() для SurfaceHolder.Callback
- используется класс Thread для SurfaceView
Поймай меня
Тот автор на основе предыдущего примера сделал новый пример. На экране рисуется белая точка, которая начинает двигаться в место касания экрана пальцем пользователя.
Этот же пример, но с использованием разметки FrameLayout, внутри которой находится SurfaceView, находится здесь. Не буду приводить код.
Продолжение серии примеров — Exercise of SurfaceView: SurfaceView overlap with a LinearLayout. Здесь появляется возможность выбрать цвет точки.
Отскакивающие значки
Ещё один пример — при касании экрана в области SurfaceView будет появляться новая картинка, которая будет двигаться внутри контейнера, отскакивая от стенок.
Источник
Somethings
problems and solutions
пятница, 27 марта 2009 г.
Пишем игру для Android. Часть 1. Surface
На прошлую статью про Android я получила хорошие отзывы, вдохновилась и решила продолжать. Эта серия статей будет посвящена написанию игры для Android.
Писать мы будем игру в пинг-понг. Изначально задумывался арканоид, но для мануала получалось слишком громоздко, так что я решила упростить до пинг-понга. Итак, есть прямоугольное поле, на нем две ракетки, управляемые игроками, и мячик. Мячик летает, отражаясь от ракеток и боковых стенок. Когда один игрок не успевает отбить мячик, его противнику засчитывается очко. Игра продолжается, пока один из игроков не наберет определенное число очков. Вот такую игру мы и будем писать. Одна ракетка будет управляться пользователем, другая — компьютером.
Создание проекта
Проект будем делать, как и в прошлый раз, в Eclipse. Создаём:
Получили автоматически сгенерившийся HelloWorld. На форме у нас единственный элемент управления — TextView . Но нам нужно разместить на форме компонент, который бы отрисовывал игровое поле и обрабатывал нажатия клавиш. Среди стандартных такого нет, так что придется создавать свой.
Surface
Прежде, чем создавать такой класс, рассмотрим некоторые базовые понятия и классы.
SurfaceView
SurfaceView унаследован от View и является элементом управления, предоставляющим область для рисования (Surface). Суть в том, чтобы дать отдельному потоку возможность рисовать на Surface, когда он захочет, а не только тогда, когда приложению вздумается обновить экран. Понятие Surface очень похоже на Canvas, но все же немного не то. Canvas — это область рисования на компоненте, а Surface сам является компонентом, т.е. у Surface есть Canvas.
SurfaceView является элементом управления, т.е. можно его непосредственно разместить на форме. Однако, в этом случае толку толку от него будет мало. Так что мы будем писать свой класс, унаследованный от SurfaceView, а также класс для потока, который будет на нем рисовать.
SurfaceHolder
Интерфейс, с помощью которого происходит вся непосредственная работа с областью рисования. Выглядит это примерно так:
SurfaceHolder.Callback
Интерфейс содержит функции обработки изменения состояния Surface:
- surfaceCreated(SurfaceHolder holder) — первое создание Surface. Здесь можно, например, запускать поток, который будет рисовать на Surface.
- surfaceChanged(SurfaceHolder holder, int format, int width, int height) — любое изменение Surface (например, поворот экрана).
- surfaceDestroyed(SurfaceHolder holder) — уничтожение Surface. Здесь можно останавливать процесс, который рисует на Surface.
Класс для отображения игры
Итак, узнав, что такое Surface, можно двигаться дальше. Cоздаем класс GameView.java , унаследованный от SurfaceView и реализующий интерфейс SurfaceHolder.Callback . Добавим интерфейсные функции и перегрузим конструктор. Кроме того, следует завести в этом классе ссылку на SurfaceHolder. В результате получится что-то вроде того:
GameView.java
Теперь мы можем запросто писать в разметке формы такое:
main.xml
И, запустив программу, увидим пустой экран. Теперь давайте что-нибудь нарисуем.
Класс для рисования
Поставим себе первую цель: нарисовать на экране прямоугольное поле размером 300 x 250.
Как было уже ранее сказано, все рисование должно производиться из отдельного потока. Создадим класс, GameManager , унаследованный от Thread.
GameManager.java
Стоит отдельно упомянуть о классе Paint . Этот класс используется для хранения всяческих используемых при рисовании стилей — цветов, толщины и стиля линий, шрифтов (это мы рассмотрим позже) и тому подобного. В остальном код достаточно прозрачен. Собственно рисование проходит всегда одинаково — лочим Canvas, рисуем, разлочиваем.
Теперь надо запустить рисовательный поток в нашем контроле. Добавляем в класс соответствущее поле:
GameView.java
В конструкторе GameView :
GameView.java
При создании области рисования надо будет запустить наш поток:
GameView.java
А при удалении — прибить:
GameView.java
Теперь, запустив программу, видим следующее:
Поворот экрана
Как уже было упомянуто, при повороте экрана вызывается обработчик surfaceChanged . Впрочем, при создании surface он тоже вызывается. В параметрах можно получить размеры доступной части экрана, что очень приятно, потому что с помощью класса DisplayMetrics можно получить только полный размер экрана, куда еще входит верхнее поле, на котором рисовать нельзя).
Итак, в surfaceChanged мы будем пересчитывать положение нашего поля на экране. Добавим в GameManager такую функцию:
GameManager.java
Эта функция ставит наше игровое поле в центр экрана. Инициализацию положения mField в конструкторе GameManager можно вовсе убрать, оставив только:
GameManager.java
Теперь в surfaceChanged можно написать следующее:
GameView.java
Теперь при изменении Surface (в том числе при его создании) будет пересчитываться положение поля. Так что приложение будет выглядеть так:
Мы рассмотрели необходимые понятия, написали простое приложение, которое умеет что-то рисовать, обработали поворот экрана. Исходники, как обычно, прилагаются.
25 комментариев:
Отличные статьи, спасибо!
У меня тоже была проблема с перерисовкой View’a, но я ее решил, вызывая из другого потока по таймеру функцию View.postInvalidate()
Здравствуйте, Дарья. Я давно слежу за вашим творчеством и являюсь вашим тайным поклонником. Особенное впечатление произвели на меня Андроид-публикации. Дарья, как вам удается сочетать в себе внутреннюю красоту и стремление к знанием и внешнее очарование?
Спасибо за статьи, Дарья! Очень помогают мне в изучении андроида 🙂
Извините за тупость, но как вы расположили эмулятор горизонтально?
Прощу прощение.
Но я не совсем понял те действия, что выполняются в потоке:
public void run() <
while (mRunning) <
Получается, что пока mRunning == true, поток непрерывно рисует прямоугольник, хотя он и не изменяется. Разве это правильно ?
МегаДарья, а будет ли продолжение?
Имел ввиду продолжение примеров под Андроид =)
2 Дмитрий:
Не удивлюсь, если будет. Но для статей нужно время и сам девайс, а я сейчас не располагаю ни тем, ни другим.
Здравствуйте, Дарья.
Прежде всего хочу поблагодарить вас за такие прекрасные статьи, они очень помогли мне — спасибо вам!
Не могли бы вы помочь мне разобраться в вашем проекте? Когда я запускаю код, который представлен выше (до «Поворот экрана»), то на эмуляторе появляется только пустое чёрное окно без синего прямоугольника. При перезапуске в консоль выдаётся предупреждение (Warning: Activity not started, its current task has been brought to the front). Вы можете подсказать, в чём ошибка или где её стоит искать?
В очередной раз, внимательно пересмотрев ваш код, ошибка нашлась 🙂
Привет, извените за глупый вопрос.
Я скачал исходник игры, а как запустить игру на телефоне не могу понять? «Helo World» запустилось без проблем!
Спасибо, очень познавательная статья!
IMHO тэг «игра» тут все-таки лишний 😀
Вы великолепны Дарья! =)
Так держать! Покажите миру, что женщины тоже не плохо разбираются в программистике!
Опечаточка вышла. В предложении «Однако, в этом случае толку толку от него будет мало.» слово толку употреблено 2 раза подряд.
Есть очень неприятная проблема в этом примере. Если после старта нажать в эмуляторе или на устройстве кнопку «домой», то при попытке повторного старта выдается ошибка «Произошла неожиданная остановка приложения. Повторите попытку». При нажатии кнопки «назад» и повторном запуске такой проблемы нет.
Удалось избавиться от этой проблемы добавлением finish() в onStop для activity.
Не рисует прямоугольник даже..(( я только начинающий в чем может быть проблема?
и как происходит доступ к другим классам через GanmeScreen??
как сделать так чтоб размер стола был по всей ширине экрана?
я определяю размеры экрана в GameScreen.java и как мне их теперь передать в Gamemanager.java?
Дарья, спасибо за статью. Вопрос.. прорисовка ректа происходит в постоянном цикле? Ведь run() запущен всегда.
Есть такой метод вывода графики
public class GraphicsView extends View<
public GraphicsView(Context context) <
super(context);
@Override
protected void onDraw(Canvas canvas) <
super.onDraw(canvas);
canvas.drawPaint(grndPaint);
canvas.drawText(String.valueOf(touchCounter), 10, 32, txtPaint);
@Override
public boolean onTouchEvent(MotionEvent event) <
int action;
action = event.getAction();
invalidate();
return true; //super.onTouchEvent(event);
Чем Ваш метод лучше?
Здравствуйте! Очень интересная статья, спасибо! Есть вопрос, а можно ли залить черным цветом не весь экран а только часть? А в другой части, чтоб отображались обычные кнопки и т.д.
Источник
Анимация на Android с помощью SurfaceView
В уроке Анимация на Android я показал, как делать анимацию с помощью View и функции invalidate(). Но у такого метода есть недостаток — метод invalidate() вызывает onDraw() не мгновенно, а когда решит операционная система. И если анимация требует тяжелых расчетов, она может заметно тормозить.
Для тяжелых случаев есть другой метод рисования через SurfaceView.
Класс SurfaceView предоставляет объект Surface, который поддерживает рисование в фоновом потоке и дает возможность использовать OpenGL для трехмерной графики. Это отличный вариант для насыщенных графикой элементов, которые нуждаются в частых обновлениях или должны отображать сложную графическую информацию, как в случае с играми и трехмерной визуализацией.
Основной плюс SurfaceView в том, что он работает в паре с объектом класса Thread, что позволяет выполнять тяжелые графические расчеты в отдельном потоке.
Создадим в нашем проекте еще два класса: MySurfaceView и MyThread.
MySurfaceView:
MyThread:
Не рекомендуется делать REDRAW_TIME слишком маленьким, это может сильно нагружать систему. 10 мс вполне достаточно.
Последний штрих: вернемся в MyActivity и сменим
setContentView ( new MyView ( this ) ) ;
на
setContentView ( new MySurfaceView ( this ) ) ;
Приложение должно выглядеть так:
Источник