Сегодня я напишу как в javafx создаются (наследуются) классы, как работать с массивами и немного всякой всячины. Сделаю я это на примере меню, которое будет оформлено в стиле MacOS дока. Итак поехали. Часть 1 — Создаем свои кнопки
Создадим новый JavaFX class который будет представлять из себя кастомную кнопку в доке. делается это легко и просто (импорты я упущу ибо бумагу надо экономить): ImageButton.fx
package lesson2;
public class ImageButton extends CustomNode
{
// Подпись кнопки
public var title: String;
// Изображение из которого будем делать кнопку
public var buttonImage: Image;
// URL того самого изображения <b>*</b>
public var imageUrl: String on replace {
buttonImage = Image{
url: imageUrl
};
}
// Степень увеличения размера когда курсор не наведен (больше единицы
//уменьшение реального размера, меньше увеличение измеряются в процентах)
public var nonRolloverScale:Number = 0.9;
// Степень прозрачности когда курсор не наведен
public var nonRolloverOpacity:Number = .80;
// параметр который нам пригодится когда будем менять размер
// кнопки которая активна
public var fade:Number = 1.0;
// параметр, который говорит курсор мыши на объекте или нет
var mouseInside = false;
//собственно анимация <b>**</b>
public var fadeTimeLine =
Timeline {
keyFrames: [
KeyFrame {
time: 0ms
values: [
fade => 0.0
]
},
KeyFrame {
time: 600ms
values: [
fade => 1.0 tween Interpolator.LINEAR
]
}
]
};
//функция которая будет выполнятся при клике на кнопку
//у нас она не будет делать ничего.
public var action:function():Void;
//подпись кнопки
public var textRef:Text;
}
Теперь немного пояснений по пунктам. *on replace означает что при изменении параметра imageUrl нужно изменить сам параметр buttonImage ** Класс Timeline отвечает за изменение некоторых параметров с течением времени. Должен содержать массив объектов KeyFrame — которые отвечают за изменение определенных параметром с течением времени. В нашем простом случае будет 2 KayFrame`а логика действия следующая. Когда вызывается метод стартующий анимацию (у нас он будет написан чуть позже play() или playFromStart())вызывается первый KeyFrame (c минимальным значением параметра time)в нем будет изменет параметр (fade => 0.0), затем через 600ms вызывается второй и меняет параметр на 1.0 линейно. Если бы был третий KeyFrame то затем вызвался бы он. Все до безобразия просто. Надо отметить что по-умолчанию все переменные имею модификатор видимости script-only т.е. private.
Перейдем к последнему методу который вы должны обязятельно реализовать если наследуете класс CustomNode — метод create(). ImageButton.fx
//функция create которая должна вернуть объект типа Node
override public function create():Node {
// мы же вернем группу (т.е. несколько виджетов объединены в один)
return Group {
content: [
// выделяем прямоугольник размером с картинку
Rectangle {
width: bind buttonImage.width
height: bind buttonImage.height
opacity: 0.0
},
//затем добавляем собственно картинку. ничего интересного с точки зрения
//изучения языка тут нет кроме обработки событий мыши, которые я поясню ниже <b>*</b>
ImageView {
var scale = bind if (mouseInside) fade * (1.0 - nonRolloverScale) +
nonRolloverScale
else 1.0 - fade * (1.0 - nonRolloverScale);
image: buttonImage
opacity: bind if (mouseInside) fade * (1.0 - nonRolloverOpacity) +
nonRolloverOpacity
else 1.0 - fade * (1.0 - nonRolloverOpacity)
scaleX: bind scale
scaleY: bind scale
translateX: bind buttonImage.width / 2 - buttonImage.width * scale / 2
translateY: bind buttonImage.height - buttonImage.height * scale
onMouseEntered:
function(me:MouseEvent):Void {
mouseInside = true;
fadeTimeLine.playFromStart();
}
onMouseExited:
function(me:MouseEvent):Void {
mouseInside = false;
fadeTimeLine.playFromStart();
me.node.effect = null
}
onMousePressed:
function(me:MouseEvent):Void {
me.node.effect = Glow {
level: 0.9
};
}
onMouseReleased:
function(me:MouseEvent):Void {
me.node.effect = null;
}
onMouseClicked:
function(me:MouseEvent):Void {
action();
}
},
//добавляем подпись картинки
textRef = Text {
translateX: bind buttonImage.width / 2
translateY: bind buttonImage.height
textOrigin: TextOrigin.TOP
content: title
fill: Color.WHITE
opacity: bind if (mouseInside) fade else 1.0 - fade
font: Font.font("Segoe",16)
},
]
};
}
* если вы хотите обрабатывать то или иное событие то просто нужно вписать свою функцию-обработчик в нужное вам поле. Функция обязательно должна принимать событие которым вызвана (т.е. у нас принимается me:MouseEvent, т.к. обрабатываем события мыши). При нажатии на нашу кнопку будет использован эффект Glow (осветление). Часть 2 — создаем панельку в которой будут кнопки DockNode.fx
package lesson2;
public class DockNode extends CustomNode{
//Высота панели
public var height: Float;
//Ширина панели
public var widght: Float;
var spacing = 9;
public var buttons: ImageButton[];
//Уже известный нам метод create()
override public function create(): Node{
height = 0.0;
//Мы впервые используем цикл :) <b>*</b>
for (i in buttons){
widght = widght + buttons[{indexof i}].buttonImage.width + 3*spacing;
var loc_height = buttons[{indexof i}].buttonImage.height*2 + 16;
if(height<loc_height)
height = loc_height;
}
//Возвращаем горизонтальную патель с эффектом отражения
return HBox{
hpos: HPos.CENTER
vpos: VPos.CENTER
spacing: spacing
content: buttons
effect: Reflection {
fraction: 0.75
topOffset: 0.0
topOpacity: 0.5
bottomOpacity: 0.0
}
}
};
}
Тут по большому счету ничего интересного нет, кроме того что мы использовали цикл for. Формат этого оператора может быть либо
for( <variable> in <sequence> ){
// Тело цикла
}
ну или
for (i in [1..100]){
println("Iteration: {i}");
}
Первый случай похож на реализацию for`а в языке lua — на каждой итерации вы получаете очередное значение из последовательности (массива). Узнать положение элемента a в массиве array можно так: array[indexof a].
Во втором случае вы просто указываете что переменная i изменяется от 1 до 100.
Ну и наконец главный скрипт который будет все это запускать: Main.fx
Тут в пояснениях может нуждаться только запись вида __DIR__. Так же как и __FILE__ означают директорию и файл сответственно, скрипта в котором это написано.
В результате таких мучений у нас получится что-то вроде такого:
Когда наведена мышь
Когда кликнули
Всем спасибо, за внимание. В следующем топике постараюсь ответить на пожелание Reorcs:«но так как меня мало интересует КАК это писать, кроме основных вещей. мне было бы больше интересно читать о том КАК это дизайнится, и насколько сложно поддерживается, и как БЫСТРО работает»