Разбираясь дальше с библиотекой libGDX, я дошел до пакета com.badlogic.gdx.scenes.scene2d.ui. Этот пакет предназначен для создания пользовательського интерфейса. И тут меня ждало разочарование: статьи-туториала нет. Поэтому я решил самостоятельно разобраться с пакетом, используя исходники и Javadoc документацию. То есть, это будет туториал по scene2d.ui, но уже не перевод. Я не буду здесь детально описывать конструкторы, методы, приводить подробные сигнатуры. Я постараюсь взглянуть с высоты «птичьего полета», поскольку зная принципы, вы всегда сможете узнать больше из документации. Но даже при таком подходе материала слишком много, поэтому я разобью его на две (возможно больше) статей.
Вспомним предыдущую статью и то, что мы говорили про класс Stage. Класс Stage может содержать в себе актеров (Actor) и управлять ними. Также, есть класс Group, который является и актером, и контейнером для актером. Так вот, все визуальные компоненты из пакета scene2d.ua унаследованы или от Actor, или от Group. Соответсвенно, наследники Group могут содержать в себе другие компоненты. Эти компоненты мы рассмотрим позже, а сейчас взглянем на наследников Actor. Прямым наследником является класс Widget, а от него унаследуются следующие шесть компонентов: Label, TextField, Image, List, SelectBox, Slider.
Следует заметить, что все визуальные компоненты реализуют интерфейс Layout. Этот интерфейс нужен для правильного расположения компонентов в компонентах-контейнерах. Он предоставляет методы для перерисовки компонента, определения предпочтительного, минимального, максимального размера.
Еще одно важное понятие — стиль. Внутри у почти каждого графического компонента есть статический класс с названием <ИмяКомпонента>Style. Например, LabelStyle. В этом классе хранятся нужные для корректной работы этого класса ресурсы — например, шрифт, цвет шрифта и т.д. Дело в том, что OpenGL не может напрямую отрисовывать шрифты, поэтому нужны какие-то дополнительные механизмы. Очевидным вариантом является создание картинки с изображениями букв и текстового файла, который описывает, где какая буква расположена. Потом из этого описания можно сделать класс, который сможет «строить» строчки текста из отдельных букв-картинок. Вот пример класса LabelStyle из класса Label:
Некоторые поля помечены комментарием как optional. Это значит, что их можно не описывать в описании стиля в файле настроек.
Для хранения стилей используется класс Skin. Класс Skin читает стили из файла из настройками и сохраняет их внутри себя. Затем при создании нового графического компонента мы передаем Skin ему в конструктор. Если есть подходящий стиль, он используется. Если нет — бросается исключение. Вот пример простого класса настроек, где определен стиль только для одного компонента — Label:
Некоторые комментарии. Этот файл написанный в формате JSON. Первая секция — это ресурсы. black — цвет, определенный в модели RGBA. default-font — название шрифта. Шрифт я взял из пакета com.badlogic.utils, где они назывались arial-15.fnt и arial-15.png. Вторая же секция — это непосредственно стили. Здесь мы определили один шрифт, которому указали шрифт и цвет шрифта. Заметьте, что названия полей в файле соответсвуют названиям публичных полей в классах стилей. Вы можете определить для каждого компонента несколько стилей с разными именами. Получить нужный стиль вы можете с помощью метода getStyle(Class type, String name) класса Skin. По умолчанию, вы передаете в конструктор компонента Skin и из него компонент берет стиль с названием default.
Для файла настроек нужно, чтобы в том же каталоге лежал одноименный файл-картинка, но с расширением png. Если, например, вы назвали файл настроек SimpleSkin, то графический файл будет называться SimpleSkin.png. Он необходим, если вы будете использовать ресурсы типа TextureRegion. Без этого файла программа выдаст исключение и не запустится.
Нужно заметить, что в данном файле шрифта нет русских букв. Я еще детально не разобрался в данном вопросе, поэтому русского текста в примерах не будет.
Перейдем теперь более подробно к отдельным компонентам, их стилям и использованию.
Являет собой простую текстовую метку с возможностью переноса текста по словам. Имеет несколько конструкторов, самые важные, на мой взгляд, следующие два:
Имеются методы для установки и возвращения стиля — setStyle(), getStyle(), метод для установки текста setText() и метод для установки переноса по словам setWrapt(). По умолчанию, перенос по словам отключен. Чтобы использовать его, предварительно задайте предпочтительный размер метке (preferred size).
В остальном свойства компонента не представляют особого интереса, управление его расположением, вращением и т.д. осуществляется так же, как и классом Actor.
Класс стиля компонента. Как видно, ему требуется всего лишь шрифт и цвет шрифта.
Соответсвующие этому классу ресурсы из файла настроек:
Пример использования:
Следующий класс — TextField. Являет собой поле для ввода текста. Этот класс уже любопытнее, поскольку ему можно назначить слушателя (listener). Библиотеку libGDX писали с использованием паттерна «подписчики-читатели», но ограниченным — вы можете установить только одного слушателя. Установка слушателя производится методом setTextFieldListener(), который принимает параметр типа TextFieldListener. Это интерфейс, вот его код:
Как видно, обрабатывается одно событие — символ введен.
У класса есть несколько конструкторов. Опишем два основных, на мой взгляд:
Некоторые методы класса:
TextField поддерживает копирование и вставку текста на ПК стандартными клавишами. На Android копирование и вставка не поддерживается из-за ограничений Android.
Класс стиля компонента. Как видите, обязательным параметром является лишь шрифт:
Ресурсы из файла настроек:
И пример использования:
Класс Image один из немногих, кто не требует стиля для своей работы. Поэтому его описание будет довольно кратко. Создать обьект этого класса можно несколькими конструкторами, один из наиболее простых — Image(TextureRegion region). Класс поддерживает слушателя типа ClickListener. Вот исходник этого интерфейса:
То есть, мы можем обрабатывать события нажатия на наше изображение.
Полезным методом является setRegion(), который позволяет изменить изображение. Вы можете передать конструктору как текстуру, так и ее регион. Однако масштабирование, вращение будет работать лишь в случае региона текстуры.
Пример использования:
Здесь используется текстура в качестве параметра для конструктора. В реальных приложениях лучше использовать TextureRegion.
Класс List являет собой список текстовых полей с возможностью выбора определенного поля. Аналог List Swing, или ListBox из Delphi.
Традиционно он имеет несколько конструкторов, один из наиболее простых — List(Object[] items, Skin skin). Он создает обьект из списком из массива items. Узнать выделенный элемент вы можете методом getSelection(), а его индекс — getSelectedIndex(). Установить новый список можно методом setItems(), сделать какой-либо элемент выделенным можно или по имени или по индексу: setSelection(), setSelectionIndex(). List поддерживает слушателя SelectionListener. Исходник класса:
Метод selected() вызывается при выборе элемента.
Вот класс стиля:
Как мы видим, цвета уже проинициализированы, а вот шрифт и selectedPatch придется описывать. Маленькое отступление насчет NinePatch. Это обычное изображение (.png), в котором края размером в 1 пиксел содержат некую служебную информацию. Это изображение используется для выделенного элемента. Более подробно вы можете почитать здесь habrahabr.ru/post/113623 Вот отрывки из файла настроек, что отвечают за наш List:
А вот и пример использования:
Класс SelectBox являет собой выпадающий список из нескольких элементов. В неактивном состоянии это строчка, которая показывает выбранный элемент. Когда мы активируем его, нам показывается список элементов, которые можно выбрать нажатием. Имеет несколько конструкторов, самый простой — SelectBox(Object[] items, Skin skin) — создает выпадающий список элементов. Первый элемент считается выбранным. В остальном класс очень похож на List. Он поддерживает таких же слушателей как List — SelectionListener, имеет такие же методы для манипулирования элементами списка. Вот класс стиля:
Как мы видим, в обязательной инициализации нуждаются три NinePatch-a и шрифт.
Приведем отрывки из файла настроек:
Пример использования:
Последний класс-наследник Widget. Класс Slider являет собой ползунок с максимальным и минимальным значениями. Пользователь может изменять значение ползунка перетаскиванием. Класс имеет несколько конструкторов с заданием максимального, минимального значений, количества шагов. Наипростейшим является Slider(Skin skin). Он создаст ползунок с максимальным значением 100 и минимальным 0 с количеством градаций 100. Класс может обрабатывать события изменения значения с помощью класса типа ValueChangedListener:
Когда вы изменяете значение ползунка, вызывается метод сhanged().
Класс стиля требует задания двух параметров:
Вот куски кода из файла настроек:
А вот пример использования:
Первая часть обзора пакета scene2d.ui завершена. Напомню, что мы рассмотрели только те классы, которые унаследованы от Widget. Остается еще большая половина класов-контейнеров — окна, списки, скроллируемые списки, панели и т. д. В их число входит и кнопка (Button). Как только появится время, я напишу продолжение, где рассмотрю остальные классы.
Приложение: проект с демонстрацией возможностей.
Приложение: диаграмма классов, составленная мной в программе ArgoUML.
Вспомним предыдущую статью и то, что мы говорили про класс Stage. Класс Stage может содержать в себе актеров (Actor) и управлять ними. Также, есть класс Group, который является и актером, и контейнером для актером. Так вот, все визуальные компоненты из пакета scene2d.ua унаследованы или от Actor, или от Group. Соответсвенно, наследники Group могут содержать в себе другие компоненты. Эти компоненты мы рассмотрим позже, а сейчас взглянем на наследников Actor. Прямым наследником является класс Widget, а от него унаследуются следующие шесть компонентов: Label, TextField, Image, List, SelectBox, Slider.
Следует заметить, что все визуальные компоненты реализуют интерфейс Layout. Этот интерфейс нужен для правильного расположения компонентов в компонентах-контейнерах. Он предоставляет методы для перерисовки компонента, определения предпочтительного, минимального, максимального размера.
Еще одно важное понятие — стиль. Внутри у почти каждого графического компонента есть статический класс с названием <ИмяКомпонента>Style. Например, LabelStyle. В этом классе хранятся нужные для корректной работы этого класса ресурсы — например, шрифт, цвет шрифта и т.д. Дело в том, что OpenGL не может напрямую отрисовывать шрифты, поэтому нужны какие-то дополнительные механизмы. Очевидным вариантом является создание картинки с изображениями букв и текстового файла, который описывает, где какая буква расположена. Потом из этого описания можно сделать класс, который сможет «строить» строчки текста из отдельных букв-картинок. Вот пример класса LabelStyle из класса Label:
static public class LabelStyle { public BitmapFont font; /** Optional. */ public Color fontColor; public LabelStyle () { } public LabelStyle (BitmapFont font, Color fontColor) { this.font = font; this.fontColor = fontColor; } }
Некоторые поля помечены комментарием как optional. Это значит, что их можно не описывать в описании стиля в файле настроек.
Для хранения стилей используется класс Skin. Класс Skin читает стили из файла из настройками и сохраняет их внутри себя. Затем при создании нового графического компонента мы передаем Skin ему в конструктор. Если есть подходящий стиль, он используется. Если нет — бросается исключение. Вот пример простого класса настроек, где определен стиль только для одного компонента — Label:
{ resources: { com.badlogic.gdx.graphics.Color: { black: { r: 0, g: 0, b: 0, a: 1 } }, com.badlogic.gdx.graphics.g2d.BitmapFont: { default-font: { file: default.fnt } } }, styles: { com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: { default: { font: default-font, fontColor: black } } } }
Некоторые комментарии. Этот файл написанный в формате JSON. Первая секция — это ресурсы. black — цвет, определенный в модели RGBA. default-font — название шрифта. Шрифт я взял из пакета com.badlogic.utils, где они назывались arial-15.fnt и arial-15.png. Вторая же секция — это непосредственно стили. Здесь мы определили один шрифт, которому указали шрифт и цвет шрифта. Заметьте, что названия полей в файле соответсвуют названиям публичных полей в классах стилей. Вы можете определить для каждого компонента несколько стилей с разными именами. Получить нужный стиль вы можете с помощью метода getStyle(Class type, String name) класса Skin. По умолчанию, вы передаете в конструктор компонента Skin и из него компонент берет стиль с названием default.
Для файла настроек нужно, чтобы в том же каталоге лежал одноименный файл-картинка, но с расширением png. Если, например, вы назвали файл настроек SimpleSkin, то графический файл будет называться SimpleSkin.png. Он необходим, если вы будете использовать ресурсы типа TextureRegion. Без этого файла программа выдаст исключение и не запустится.
Нужно заметить, что в данном файле шрифта нет русских букв. Я еще детально не разобрался в данном вопросе, поэтому русского текста в примерах не будет.
Перейдем теперь более подробно к отдельным компонентам, их стилям и использованию.
Label
Являет собой простую текстовую метку с возможностью переноса текста по словам. Имеет несколько конструкторов, самые важные, на мой взгляд, следующие два:
— создает метку без текста.public Label (Skin skin)
— создает метку с текстом.public Label (String text, Skin skin)
Имеются методы для установки и возвращения стиля — setStyle(), getStyle(), метод для установки текста setText() и метод для установки переноса по словам setWrapt(). По умолчанию, перенос по словам отключен. Чтобы использовать его, предварительно задайте предпочтительный размер метке (preferred size).
В остальном свойства компонента не представляют особого интереса, управление его расположением, вращением и т.д. осуществляется так же, как и классом Actor.
Класс стиля компонента. Как видно, ему требуется всего лишь шрифт и цвет шрифта.
... static public class LabelStyle { public BitmapFont font; /** Optional. */ public Color fontColor; public LabelStyle () { } public LabelStyle (BitmapFont font, Color fontColor) { this.font = font; this.fontColor = fontColor; } } ...
Соответсвующие этому классу ресурсы из файла настроек:
resources: { com.badlogic.gdx.graphics.Color: { black: { r: 0, g: 0, b: 0, a: 1 } }, com.badlogic.gdx.graphics.g2d.BitmapFont: { default-font: { file: default.fnt } } }, styles: { com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle: { default: { font: default-font, fontColor: black } } }
Пример использования:
... Skin skin = new Skin(Gdx.files.internal("data/skins/SimpleSkin")); Label label = new Label("I am label", skin); label.x = 10; label.y = 10; stage.add(label); ...
TextField
Следующий класс — TextField. Являет собой поле для ввода текста. Этот класс уже любопытнее, поскольку ему можно назначить слушателя (listener). Библиотеку libGDX писали с использованием паттерна «подписчики-читатели», но ограниченным — вы можете установить только одного слушателя. Установка слушателя производится методом setTextFieldListener(), который принимает параметр типа TextFieldListener. Это интерфейс, вот его код:
... static public interface TextFieldListener { public void keyTyped (TextField textField, char key); } ...
Как видно, обрабатывается одно событие — символ введен.
У класса есть несколько конструкторов. Опишем два основных, на мой взгляд:
— создает текстовое поле с текстомTextField(String text, Skin skin)
— создает пустое текстовое полеTextField(Skin skin)
Некоторые методы класса:
— устанавливает текстsetText()
— вместо текста будут отображаться символы, которые вы выберете методом setPasswordCharacter()setPasswordMode()
— устанавливает текст-подсказку, который будет отображаться, если вы ничего не ввели в текстовое поле.setMessageText()
TextField поддерживает копирование и вставку текста на ПК стандартными клавишами. На Android копирование и вставка не поддерживается из-за ограничений Android.
Класс стиля компонента. Как видите, обязательным параметром является лишь шрифт:
... static public class TextFieldStyle { /** Optional. */ public NinePatch background, cursor; public BitmapFont font; public Color fontColor; /** Optional. */ public TextureRegion selection; /** Optional. */ public BitmapFont messageFont; /** Optional. */ public Color messageFontColor; ...
Ресурсы из файла настроек:
... com.badlogic.gdx.scenes.scene2d.ui.TextField$TextFieldStyle: { default: { font: default-font, fontColor: black } } ...
И пример использования:
... TextField textField = new TextField("I am text field", skin); textField.y = 30; stage.addActor(textField); ...
Image
Класс Image один из немногих, кто не требует стиля для своей работы. Поэтому его описание будет довольно кратко. Создать обьект этого класса можно несколькими конструкторами, один из наиболее простых — Image(TextureRegion region). Класс поддерживает слушателя типа ClickListener. Вот исходник этого интерфейса:
... public interface ClickListener { public void click (Actor actor, float x, float y); } ...
То есть, мы можем обрабатывать события нажатия на наше изображение.
Полезным методом является setRegion(), который позволяет изменить изображение. Вы можете передать конструктору как текстуру, так и ее регион. Однако масштабирование, вращение будет работать лишь в случае региона текстуры.
Пример использования:
... Image image = new Image(new Texture(Gdx.files.internal("data/skins/default.png"))); image.y = 100; stage.addActor(image); ...
Здесь используется текстура в качестве параметра для конструктора. В реальных приложениях лучше использовать TextureRegion.
List
Класс List являет собой список текстовых полей с возможностью выбора определенного поля. Аналог List Swing, или ListBox из Delphi.
Традиционно он имеет несколько конструкторов, один из наиболее простых — List(Object[] items, Skin skin). Он создает обьект из списком из массива items. Узнать выделенный элемент вы можете методом getSelection(), а его индекс — getSelectedIndex(). Установить новый список можно методом setItems(), сделать какой-либо элемент выделенным можно или по имени или по индексу: setSelection(), setSelectionIndex(). List поддерживает слушателя SelectionListener. Исходник класса:
... public interface SelectionListener { public void selected (Actor actor, int index, String value); } ...
Метод selected() вызывается при выборе элемента.
Вот класс стиля:
... static public class ListStyle { public BitmapFont font; public Color fontColorSelected = new Color(1, 1, 1, 1); public Color fontColorUnselected = new Color(1, 1, 1, 1); public NinePatch selectedPatch; ...
Как мы видим, цвета уже проинициализированы, а вот шрифт и selectedPatch придется описывать. Маленькое отступление насчет NinePatch. Это обычное изображение (.png), в котором края размером в 1 пиксел содержат некую служебную информацию. Это изображение используется для выделенного элемента. Более подробно вы можете почитать здесь habrahabr.ru/post/113623 Вот отрывки из файла настроек, что отвечают за наш List:
... com.badlogic.gdx.graphics.g2d.NinePatch: { default-nine : [ {width: 100, height: 100, x: 0, y: 0} ] } ... com.badlogic.gdx.scenes.scene2d.ui.List$ListStyle: { default: { font: default-font, selectedPatch: default-nine } } ...
А вот и пример использования:
... List list = new List(new String[] {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"}, skin); list.x = 300; stage.addActor(list); ...
SelectBox
Класс SelectBox являет собой выпадающий список из нескольких элементов. В неактивном состоянии это строчка, которая показывает выбранный элемент. Когда мы активируем его, нам показывается список элементов, которые можно выбрать нажатием. Имеет несколько конструкторов, самый простой — SelectBox(Object[] items, Skin skin) — создает выпадающий список элементов. Первый элемент считается выбранным. В остальном класс очень похож на List. Он поддерживает таких же слушателей как List — SelectionListener, имеет такие же методы для манипулирования элементами списка. Вот класс стиля:
... static public class SelectBoxStyle { public NinePatch background; public NinePatch listBackground; public NinePatch listSelection; public BitmapFont font; public Color fontColor = new Color(1, 1, 1, 1); public float itemSpacing = 10; ...
Как мы видим, в обязательной инициализации нуждаются три NinePatch-a и шрифт.
Приведем отрывки из файла настроек:
... com.badlogic.gdx.scenes.scene2d.ui.SelectBox$SelectBoxStyle: { default: { font: default-font, background: default-nine, listBackground: default-nine, listSelection: default-nine } } ...
Пример использования:
... SelectBox selectBox = new SelectBox(new String[] {"Item 1", "Item 2", "Item 3"}, skin); selectBox.x = 400; stage.addActor(selectBox); ...
Slider
Последний класс-наследник Widget. Класс Slider являет собой ползунок с максимальным и минимальным значениями. Пользователь может изменять значение ползунка перетаскиванием. Класс имеет несколько конструкторов с заданием максимального, минимального значений, количества шагов. Наипростейшим является Slider(Skin skin). Он создаст ползунок с максимальным значением 100 и минимальным 0 с количеством градаций 100. Класс может обрабатывать события изменения значения с помощью класса типа ValueChangedListener:
... static public interface ValueChangedListener { public void changed (Slider slider, float value); } ...
Когда вы изменяете значение ползунка, вызывается метод сhanged().
Класс стиля требует задания двух параметров:
... static public class SliderStyle { NinePatch slider; //Фон ползунка. Растягивается только по горизонтали. TextureRegion knob; // Изображение части, которую мы передвигаем. ...
Вот куски кода из файла настроек:
... com.badlogic.gdx.graphics.g2d.TextureRegion: { default-region: {width: 10, height: 12, x: 0, y: 0} } ... com.badlogic.gdx.scenes.scene2d.ui.Slider$SliderStyle: { default: { slider: default-nine, knob: default-region } } ...
А вот пример использования:
... Slider slider = new Slider(skin); stage.addActor(slider); ...
Первая часть обзора пакета scene2d.ui завершена. Напомню, что мы рассмотрели только те классы, которые унаследованы от Widget. Остается еще большая половина класов-контейнеров — окна, списки, скроллируемые списки, панели и т. д. В их число входит и кнопка (Button). Как только появится время, я напишу продолжение, где рассмотрю остальные классы.
Приложение: проект с демонстрацией возможностей.
Приложение: диаграмма классов, составленная мной в программе ArgoUML.
