[JavaFX] Problemas com binding

9 respostas
Rafael_Afonso

Olá:

Estou estudando JavaFX pelo Livro Plataforma Pro JavaFx (tradução brasileira). No momento estou no capítulo 7, sobre criação de telas mais complexas. Infelizmente estou com um problema referente a binding referente a este exemplo. A seguir segue-se uma versão simplificada do script problemático:

package testejavafx2;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Tile;
import javafx.scene.text.Text;
import javafx.scene.text.Font;

Stage {
	title: "Teste Bind"
	var scene: Scene;
	scene: scene = Scene {
		width: 600
		height: 120
		content: Tile {
			width: bind scene.width
			height: bind scene.height
			tileWidth: bind scene.width / 2	// Aqui acontece a exeção!!!
			//      tileHeight: bind scene.height
			content: [
				Text {
					font: Font {
						size: 24
					}
					x: 10, y: 30
					content: "Teste de Bind"
				}
			]
		}
	}
}

Quando rodo aparece a seguinte exceção:

Unexpected exception caught in MasterTimer.timePulse():
com.sun.javafx.runtime.AssignToBoundException: Cannot assign to bound variable
	at com.sun.javafx.runtime.FXBase.restrictSet$(FXBase.java:148)
	at testejavafx2.TestaBind$TestaBind$Script$1Tile$ObjLit$2.set$tileWidth(TestaBind.fx)
	at javafx.scene.layout.Tile.doLayout(Tile.fx:400)
	at javafx.scene.layout.Container.impl_layoutChildren(Container.fx:1209)
	at javafx.scene.Parent.layout(Parent.fx:831)
	at javafx.scene.Parent.layout(Parent.fx:837)
	at javafx.scene.Scene.doLayoutPass(Scene.fx:352)
	at javafx.scene.Scene$ScenePulseListener.pulse(Scene.fx:985)
	at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.fx:143)
	at com.sun.javafx.tk.Toolkit.handleMasterTimerPulse(Toolkit.fx:132)
	at com.sun.javafx.tk.Toolkit$1com$ObjLit$5.firePulseImpl(Toolkit.fx:305)
	at com.sun.scenario.ToolkitAccessor.firePulse(ToolkitAccessor.java:55)
	at com.sun.scenario.scenegraph.JSGPanelRepainter$FrameDisplay.run(JSGPanelRepainter.java:133)
	at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:420)
	at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:296)
	at com.sun.embeddedswing.EmbeddedEventQueue.doPulse(EmbeddedEventQueue.java:570)
	at com.sun.embeddedswing.EmbeddedEventQueue.access$000(EmbeddedEventQueue.java:82)
	at com.sun.embeddedswing.EmbeddedEventQueue$2.run(EmbeddedEventQueue.java:473)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Já Procurei pelo Google a respeito dessa mensagem, mas não me ajudou muito :(.
No que se refere ao MasterTimer.timePulse, as sugestões que achei sugerem usar FX.deferAction. Entretanto admito que não entendi como poderia utilizar.
Já que se refere a AssignToBoundException a sugestão é usar bind inverse. Entretanto se eu fizer tileWidth: bind scene.width / 2 with inverse, acontece erro de compilação.
Portanto minha dúvida é sobre o que estou fazendo de errado e como corrigí-lo.

Grato,

9 Respostas

Jesuino_Master

Oi,

Quando você faz um binding, não pode mais modificar o valor da variável, fica semelhante a um def…

O TileWidth e height é manipulado pela classe Tile, ela calcula o tamanho para o Tile de acordo com o Width total do Layout. Provalemente no JavaFX 1.2, versão sobre qual o livro é baseado, não há esse redimensionamento do Tile, daí o erro.

Sugestão:

  • remova esse segundo bind
  • Faça um override na variavel width do scene, quando ela for modificada acione um trigger que atualiza a variavel tilewidth do tile.

Se tiver dúvidas poste ae :slight_smile:

[]'s

Rafael_Afonso

Jesuíno:

Você então sugere criar uma subclasse de Scene para fazer um override de width? Poderia dar um exemplo?
Uma tentativa sem muito sucesso foi a seguinte:

package testejavafx2;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Tile;
import javafx.scene.text.Text;
import javafx.scene.text.Font;

Stage {
  title: "Teste Bind"
  var scene: Scene;
  var myWidth = 600 on replace {
        var tile = scene.content[0] as Tile;
        tile.tileWidth = scene.width / 2;
        println(tile.tileWidth)
      };
  var myHeight = 120 on replace {
        var tile = scene.content[0] as Tile;
        tile.tileHeight = scene.height;
        println(tile.tileHeight);
      }
  scene: scene = Scene {
        width: myWidth
        height: myHeight //120
        content: Tile {
          width: bind scene.width
          height: bind scene.height
          tileWidth: scene.width / 2
          tileHeight: scene.height
          content: [
            Text {
              font: Font {
                size: 24
              }
              x: 10, y: 30
              content: "Teste de Bind"
            }
          ]
        }
      }
}

Em width de Scene tentei fazer bind myWidth. Entretanto por Scene.width public-init, tive erro de compilação.

Jesuino_Master

Então, segue abaixo um exemplo que acredito que não demonstra o Bind, mas mostra uma solução para quando temos problemas com campos que não podem ter referência fixa.

import javafx.stage.Stage;  
import javafx.scene.Scene;  
import javafx.scene.layout.Tile;  
import javafx.scene.text.Text;  
import javafx.scene.text.Font;  

var tileRef:Tile;

var scene: Scene;
var height:Number = bind scene.height on replace{ tileRef.tileHeight = scene.height; }
var width:Number = bind scene.width on replace{ tileRef.tileWidth = scene.width;}

Stage {  
    title: "Teste Bind"
    scene: scene = Scene {  
		width: 600 height: 120
        content: tileRef = Tile {  
            width: bind scene.width height: bind scene.height   
            content: [
                Text{
					font: Font { size: 24 } x: 10, y: 30 
					content: "Teste de Bind"  
                }
			]
		}
    }
}

Escrevi um artigo sobre bind do JavaFX para o JavaFree:

http://javafree.uol.com.br/topic-874587-Bind-em-JavaFX.html?page=1

mas o livro realmente destrói e explica tudo que é possível sobre o binding, o mesmo acontece com a Reflection do JavaFX, ele explica melhor que qualquer tutorial na internerds :slight_smile:

Rafael_Afonso

Jesuíno:

Funcionou! Obrigado!
Ah! Obrigado também pelo site http://www.javafx.com.br/

Rafael_Afonso

Enviei um email para o autor do código original, Stephen Chin. Eia a resposta dele:

O código corrigido ficou assim:

package testejavafx2;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Tile;
import javafx.scene.text.Text;
import javafx.scene.text.Font;

Stage {
  title: "Teste Bind"
  var scene:Scene;
  scene: scene = Scene {
    width: 600
    height: 120
    content: Tile {
      width: bind scene.width
      height: bind scene.height
      tileWidth: bind scene.width / 2
      tileHeight: bind scene.height
      autoSizeTiles: false // Eis a Correção
      content: [
        Text {
          font: Font {
            size: 24
          }
          x: 10, y: 30
          content: "Teste de Bind"
        }
      ]
    }
  }
}

E finalmente funcionou como esperado. :slight_smile:

Jesuino_Master

Ha legal :slight_smile:

recebeu uma resposta direto do Stephen Chin hein :slight_smile:

Assim o Tile não tenta ajustar seu Frame e você pode modificar a vontade o tamanho do mesmo.

Acho uma burrice do JavaFX isso, na verdade… Ele deveria saber que vocÊ fez bind na variável e automaticamente tirar o ajuste automático…

Valeu por compartilhar a solução!

D

Rafael Afonso:
Enviei um email para o autor do código original, Stephen Chin. Eia a resposta dele:

O código corrigido ficou assim:

package testejavafx2;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Tile;
import javafx.scene.text.Text;
import javafx.scene.text.Font;

Stage {
  title: "Teste Bind"
  var scene:Scene;
  scene: scene = Scene {
    width: 600
    height: 120
    content: Tile {
      width: bind scene.width
      height: bind scene.height
      tileWidth: bind scene.width / 2
      tileHeight: bind scene.height
      autoSizeTiles: false // Eis a Correção
      content: [
        Text {
          font: Font {
            size: 24
          }
          x: 10, y: 30
          content: "Teste de Bind"
        }
      ]
    }
  }
}

E finalmente funcionou como esperado. :)

Não querendo reabrir o tópico, mas estou estudando esse mesmo livro, e acabei me deparando com diversos problemas, esse problema com a ligação dos tileWidth e titlHeight já estavam me tirando o sono.

Rafael Afonso você consegui executar todos os códigos do livro com perfeição?
tenho outras partes dos códigos de exemplos apresentando erro, e não consegui resolver.

Rafael_Afonso

Olá:

Nem me lembro mais se consegui tudo o que estava no livro. Mas acho melhor você nem mais esquentar a cabeção com isso. Acho que é melhor você deixar a antiga versão do JavaFX e estudar a versão 2.0. Além disso o Livro já foi atualizado. Seria melhor estudar a partir dele.

Grato,

Rafael U. C. Afonso

/DR:
Rafael Afonso:
Enviei um email para o autor do código original, Stephen Chin. Eia a resposta dele:

O código corrigido ficou assim:

package testejavafx2;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.Tile;
import javafx.scene.text.Text;
import javafx.scene.text.Font;

Stage {
  title: "Teste Bind"
  var scene:Scene;
  scene: scene = Scene {
    width: 600
    height: 120
    content: Tile {
      width: bind scene.width
      height: bind scene.height
      tileWidth: bind scene.width / 2
      tileHeight: bind scene.height
      autoSizeTiles: false // Eis a Correção
      content: [
        Text {
          font: Font {
            size: 24
          }
          x: 10, y: 30
          content: "Teste de Bind"
        }
      ]
    }
  }
}

E finalmente funcionou como esperado. :)

Não querendo reabrir o tópico, mas estou estudando esse mesmo livro, e acabei me deparando com diversos problemas, esse problema com a ligação dos tileWidth e titlHeight já estavam me tirando o sono.

Rafael Afonso você consegui executar todos os códigos do livro com perfeição?
tenho outras partes dos códigos de exemplos apresentando erro, e não consegui resolver.

D

Sim. Estou estudando a versão script por causa de meu TCC. Quando elaborei o projeto de TCC, no inicio do 7° semestre no ano passado, o fiz sobre a versão script do JavaFX, pois era a versão atual naquele período. Agora com meu projeto sendo sobre a versão script somado com a escassez de material sobre o versão 2.0, me parece mais conveniente concluir o TCC utilizando a versão script do projeto. Mas concluindo o TCC vou me atualizar com um bom livro da versão 2.0.
Depois dos diversos erros encontrados no livro Plataforma Pro JavaFx v 1.2, não sei se confio em comprar o livro da Apress com a versão atualizada.

Criado 17 de agosto de 2010
Ultima resposta 14 de mar. de 2012
Respostas 9
Participantes 3