Курс по JavaFX | Принцип действия привязки данных в языке JavaFX | Java FX
В начало!
   
Изучение языка программирования JavaFX

Урок 8: Привязка данных и триггеры

   
« Предыдущий 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Следующий »
 
Привязка данных, или способность создавать непосредственную связь между двумя переменными, является одной из ключевых возможностей языка программирования JavaFX Script. В начале этого урока представлена информация о связывании двух простых переменных, после чего описывается более сложное связывание между переменной и значением функции или выражения. После понимания данной концепции следует ознакомиться с уроком "Применение привязки данных к объектам интерфейса пользователя" в руководстве Создание приложений GUI с помощью JavaFX, где показано, каким мощным инструментом может быть механизм привязки данных при построении приложений JavaFX.

Триггер замены представляет собой блок кода, который связан с переменной. В случае изменения переменной происходит автоматическое выполнение кода. В уроке приводится практический пример использования триггера замены.

Для получения подробной информации о привязке данных см. главу 7. Привязка в Руководстве по языку программирования JavaFX Script.
 
Содержание
 
Обзор механизма привязки данных
Привязка данных и объекты
Привязка данных и функции
Привязка данных в последовательностях
Триггеры замены
 

Зарезервированное слово bind связывает значение целевой переменной со значением связываемого выражения. Связываемое выражение может быть простым значением основного типа, объектом, результатом выполнения функции или результатом выражения. Ниже представлены примеры для каждого из этих вариантов.

В большинстве реальных ситуаций в программировании привязка данных используется для синхронизации графического интерфейса пользователя (GUI) приложения с лежащими в его основе данными. (Программирование GUI описывается в руководстве Создание приложений GUI с помощью JavaFX; здесь же рассматриваются основные принципы с некоторыми примерами).

Начнем с простых примеров: следующий скрипт привязывает переменную x к переменной y, изменяет значение переменной x, а затем выводит значение переменной y. Поскольку переменные связаны, значение переменной y автоматически обновляется.

var x = 0;
def y = bind x;
x = 1;
println(y); // y now equals 1
x = 47;
println(y); // y now equals 47
 

Обратите внимание, что переменная была объявлена y как def. Тем самым, никакой код не может прямо присвоить значение этой переменной (в то время как это значение может изменяться благодаря использованию оператора bind). Используйте такое же решение, применяя привязку к объекту (напомним, что ранее был введен объект Address в уроке Использование объектов):

var myStreet = "1 Main Street";
var myCity = "Santa Clara";
var myState = "CA";
var myZip = "95050";

def address = bind Address {
     street: myStreet;
     city: myCity;
     state: myState;
     zip: myZip;
};

println("address.street == {address.street}");
myStreet = "100 Maple Street";
println("address.street == {address.street}");
 

При изменении значения myStreet также изменяется переменная street в объекте address:

address.street == 1 Main Street
address.street == 100 Maple Street
 

Обратите внимание, что изменение значения myStreet на самом деле приводит к созданию нового объекта Address, а затем к переназначению переменной address. Для отслеживания изменений без создания нового объекта Address, следует применить оператор bind непосредственно к переменным экземпляра объекта:

def address = bind Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};
 

Кроме того, можно опустить первый оператор bind (тот, который находится непосредственно перед Address) если используется явная привязка к переменным экземпляра:

def address = Address {
     street: bind myStreet;
     city: bind myCity;
     state: bind myState;
     zip: bind myZip;
};
 

В предыдущих уроках была предоставлена информация о функциях, однако имеется еще одно отличие, с которым также требуется ознакомиться: связываемые функции относительно несвязываемых функций.

Рассмотрим следующую функцию, которая создает и возвращает объект класса Point:

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}

class Point { 
     var x : Number;
     var y : Number;
}
 

Такая функция известна как связываемая функция, так как ей предшествует зарезервированное слово bound.


Примечание. Зарезервированное слово bound не является заменой слова bind, оба этих слова используются в комбинации, как описано ниже.

Добавим следующий код, чтобы вызвать эту функцию и протестировать привязку данных:

var scale = 1.0;

bound function makePoint(xPos : Number, yPos : Number) : Point {
     Point {
          x: xPos * scale
          y: yPos * scale
     }
}

class Point {
     var x : Number;
     var y : Number;
}

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);

myX = 10.0;
println(pt.x);

scale = 2.0;
println(pt.x);
 

Результат работы этого скрипта будет следующим:

3.0
10.0
20.0
 

Проанализируем этот скрипт небольшими фрагментами.

Код:

var myX = 3.0;
var myY = 3.0;
def pt = bind makePoint(myX, myY);
println(pt.x);
 

инициализирует переменные скрипта myX и myY значениями 3.0. Эти значения затем передаются в качестве параметров для функции makePoint, которая создает и возвращает новый объект класса Point. Зарезервированное слово bind, расположенное сразу перед вызовом функции makePoint, связывает только что созданный объект класса Point (pt) с результатом выполнения функции makePoint.

Далее, код

myX = 10.0;
println(pt.x);
 

изменяет значение myX на 10.0 и выводит значение pt.x. Таким образом, в результате pt.x выводит 10.0.

Наконец, код:

scale = 2.0;
println(pt.x);
 

изменяет значение scale и снова выводит значение pt.x. Теперь значение pt.x равно 20.0. Однако если убрать зарезервированное слово bound из этой функции (тем самым сделав ее несвязанной функцией), результат будет выглядеть следующим образом:

3.0
10.0
10.0
 

Причина этого заключается в том, что несвязанные функции вызываются повторно только в том случае, когда изменяется один из их параметров. Так как scale не является параметром функции, изменение его значения приводит к еще одному вызову функции.

Оператор bind можно также использовать с выражениями for. Рассмотрим этот случай подробнее. Для начала определим две последовательности и выведем значения их элементов:

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
printSeqs();

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}
 

Последовательность seq1 содержит десять элементов (числа от 1 до 10). Последовательность seq2 также содержит десять элементов; эти элементы должны были бы иметь те же самые значения, что и элементы последовательности seq1, но так как к каждому ее элементу было применено выражение item*2, их значения соответственно удвоились.

Следовательно, будет получен следующий результат:

First Sequence:
1
2
3
4
5
6
7
8
9
10
Second Sequence:
2
4
6
8
10
12
14
16
18
20
 

Две этих последовательности можно связать друг с другом, разместив зарезервированное слово bind непосредственно перед зарезервированным словом for.

def seq2 = bind for (item in seq1) item*2;
 

Возникает вопрос: если последовательность seq1 каким-либо образом изменится, изменятся ли все или лишь некоторые элементы последовательности seq2? Это можно протестировать, вставив один элемент (значение 11) в конце последовательности seq1, а затем инициировав вывод значений обеих последовательностей, чтобы увидеть, что изменилось в этом случае:

var seq1 = [1..10];
def seq2 = bind for (item in seq1) item*2;
insert 11 into seq1;
printSeqs();

function printSeqs() {
     println("First Sequence:");
     for (i in seq1){println(i);}
     println("Second Sequence:");
     for (i in seq2){println(i);}
}
 

Результат:

First Sequence:
1
2
3
4
5
6
7
8
9
10
11
Second Sequence:
2
4
6
8
10
12
14
16
18
20
22
 

Вывод показывает, что добавление значения 11 в конце последовательности seq1 не влияет на 10 элементов в последовательности seq2; новый элемент автоматически добавляется в конец последовательности seq2 и получает значение 22.

Триггеры замены представляют собой произвольные блоки кода, которые прилагаются к переменным и выполняются всякий раз при изменении значения переменной. Основной синтаксис показан в следующем примере: определяется значение переменной password и к переменной присоединяется триггер; когда значение переменной password изменяется, триггер выводит сообщение с новым значением этой переменной:

var password = "foo" on replace oldValue {
     println("\nALERT! Password has changed!");
     println("Old Value: {oldValue}");
     println("New Value: {password}");
};

password = "bar";

 

Результат выполнения этого примера показан ниже:

ALERT! Password has changed!
Old Value: 
New Value: foo

ALERT! Password has changed!
Old Value: foo
New Value: bar
 

В этом примере триггер срабатывает два раза: первый раз, когда переменная password инициализируется значением "foo", и второй раз, когда ее значение становится "bar". Обратите внимание, что в переменной oldValue сохраняется значение переменной до вызова триггера. Эту переменную можно назвать любым именем; в данном примере для наглядности использовано имя oldValue.

 
« Предыдущий 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Следующий »
 
Оценка и обзор
Сообщите нам ваше мнение о содержимом данной страницы.
Отлично   Хорошо   Удовлетворительно   Плохо  
Комментарии:
Адрес электронной почты (ответ невозможен, если адрес не указан):
Политика конфиденциальности Sun

Обратите внимание на то, что мы не можем ответить на все поступившие комментарии.