JavaFX has a method that is added to the controller:
public void initialize(URL url, ResourceBundle rb)
This seems to run before any of the controls are added to the scene, because when I add this to it:
#Override
public void initialize(URL url, ResourceBundle rb){
String treeItemCss = getClass().getResource("/media/css/TreeItem.css").getPath();
main.getScene().getStylesheets().add(treeItemCss);
}
The CSS:
.tree-cell{
-fx-indent: 100;
-fx-underline: true;
}
I get an error from this method: getStylesheets(). But if I move that to an OnAction and execute that action I get no errors.
So my question is, is there a method that runs after all the controls are added to the scene, or a good way to add css to items that are created from a user action, such as a button click?
The initialize() method runs at the end of the FXMLLoader's load() method. Since you don't get a reference to the root of the FXML until that completes, there's obviously no way you can add it to a scene until after then.
You can:
Add the css to the Scene in the application code. I.e. Somewhere you create an FXMLLoader, call load(), and add the result to the Scene. Just set the css file on the scene right there, or:
Add the css stylesheet to the root node instead of to the scene (assuming main is a Parent):
public void initialize() {
String treeItemCss = ... ;
main.getStylesheets().add(treeItemCss);
}
or:
Observe the Scene property and add the stylesheet when it changes to something not null:
public void initialize() {
String treeItemCss = ... ;
main.sceneProperty().addListener((obs, oldScene, newScene) -> {
if (newScene != null) {
newScene.getStylesheets().add(treeItemCss);
}
});
}
Update Here is a complete example to demonstrate the second option. Everything is in the "application" package:
Main.java:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = FXMLLoader.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root,400,400);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.control.TreeItem?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController" fx:id="root">
<center>
<TreeView>
<root>
<TreeItem value="Root">
<children>
<TreeItem value="One"/>
<TreeItem value="Two"/>
<TreeItem value="Three"/>
</children>
</TreeItem>
</root>
</TreeView>
</center>
</BorderPane>
MainController.java:
package application;
import javafx.fxml.FXML;
import javafx.scene.layout.BorderPane;
public class MainController {
#FXML
private BorderPane root ;
public void initialize() {
root.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
}
}
application.css:
.tree-cell{
-fx-indent: 100;
-fx-underline: true;
}
Note that you can add the stylesheet directly in the FXML file with
<BorderPane xmlns:fx="..." fx:controller="..." stylesheets="#application.css">
and then omit it completely from the controller logic.
Related
I am using TitledPanes ScrollPanes and TableViews and I have the problem, when I collapse a titledPane, the horizontal ScrollBar of the TableView resets.
Here is a code example where you can verify it:
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TableView;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.AnchorPane;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private AnchorPane content;
#FXML
private TitledPane titledPane;
#FXML
private TableView<Object> tableView;
#Override
public void initialize(URL location, ResourceBundle resources) {
titledPane.prefHeightProperty().bind(content.heightProperty());
tableView.prefWidthProperty().bind(content.widthProperty());
tableView.getColumns().forEach(col -> col.setPrefWidth(300)); // to have enough "space" to scroll
tableView.setItems(FXCollections.observableArrayList(new Object()));
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.testscroll.Controller"
fx:id="content">
<TitledPane fx:id="titledPane">
<TableView fx:id="tableView">
<columns>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
<TableColumn/>
</columns>
</TableView>
</TitledPane>
</AnchorPane>
Any idea how can I prevent the scroll of the tableview to reset every time I collapse the pane?
After a bit of digging, it looks like some layout optimization in VirtualFlow might be the reason (all seems to be fine if the scrolled content is not a TableView - not thoroughly analyzed, though)
What happens is:
during collapse, the TitledPane's content is resized vertically to 0
in VirtualFlow's layoutChildren a zero height/width is special cased to do nothing except hide everything, including the scrollBars
an internal listener to the scrollBar's visiblilty resets its value to 0
A tentative (read: dirty and might have unwanted side-effects, totally untested beyond this quick outline!) hack around is a custom TableViewSkin that tries to "remember" the last not-zero value and resets it on getting visible again.
An example:
public class TitledPaneTableScroll extends Application {
public static class TableViewScrollSkin<T> extends TableViewSkin<T> {
DoubleProperty hvalue = new SimpleDoubleProperty();
public TableViewScrollSkin(TableView<T> control) {
super(control);
installHBarTweak();
}
private void installHBarTweak() {
// Note: flow and bar could be legally retrieved via lookup
// protected api pre-fx9 and post-fx9
VirtualFlow<?> flow = getVirtualFlow();
// access scrollBar via reflection
// this is my personal reflective access utility method - use your own :)
ScrollBar bar = (ScrollBar) FXUtils
.invokeGetFieldValue(VirtualFlow.class, flow, "hbar");
bar.valueProperty().addListener((s, o, n) -> {
if (n.intValue() != 0) {
hvalue.set(n.doubleValue());
// debugging
// new RuntimeException("who is calling? \n").printStackTrace();
}
//LOG.info("hbar value: " + n + "visible? " + bar.isVisible());
});
bar.visibleProperty().addListener((s, o, n) -> {
if (n) {
bar.setValue(hvalue.get());
}
});
}
}
int counter;
private Parent createContent() {
TableView<Object> table = new TableView<>(FXCollections.observableArrayList(new Object()) ) {
#Override
protected Skin<?> createDefaultSkin() {
return new TableViewScrollSkin<>(this);
}
};
table.getColumns().addAll(Stream
.generate(TableColumn::new)
.limit(10)
.map(col -> {
col.setPrefWidth(50);
col.setText("" + counter++);
return col;
})
.collect(Collectors.toList()));
TitledPane titled = new TitledPane("title", table);
titled.setAnimated(true);
BorderPane content = new BorderPane(titled);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent(), 400, 400));
// stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TitledPaneTableScroll.class.getName());
}
The best article I found was: How to create multiple javafx controllers with different fxml files?
However im really confused on how this works. All examples just seem a bit too complex for the initial learning.
So here I have a simple helloWorld for testing purposes. As you can see in the xml, I have a container, menu and footer. However, I want all 3 of them to have seperate controllers and XML files which are then merged and shown as seen in the XML below after the class:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
FXMLLoader loader = new FXMLLoader();
Parent root = loader.setLocation(getClass().getResource("main.fxml"));
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
MainController mainController = loader.getController();
}
}
XML
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.canvas.*?>
<HBox fx:id="container" id="container" fx:controller="core.GuiController" xmlns:fx="http://javafx.com/fxml">
<HBox fx:id="post" id="post">
<!-- Stuff -->
</HBox>
<HBox fx:id="friends" id="friends">
<!-- Stuff -->
</HBox>
<HBox fx:id="profile" id="profile">
<!-- Stuff -->
</HBox>
</HBox>
I could really benefit from a simple example. How can I keep them in seperate files and merge them while they each retain their own controllers?
You could follow this tutorial
public class MainApp extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("AddressApp");
initRootLayout();
showPersonOverview();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Shows the person overview inside the root layout.
*/
public void showPersonOverview() {
try {
// Load person overview.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane personOverview = (AnchorPane) loader.load();
// Set person overview into the center of root layout.
rootLayout.setCenter(personOverview);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Returns the main stage.
* #return
*/
public Stage getPrimaryStage() {
return primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
In this example You have two fxml files, RootLayout.fxml and PersonOverview.fxml.
You set the scene of your primarystage to (BorderPane)RootLayout.fxml then add PersonOverview.fxml to the BorderPane.
I'm trying to resize a canvas in Javafx. I am using scene builder and fxml. So far, when the user clicks on the canvas the canvas turns black, and when I resize the screen and click on the canvas only the original size of the canvas turns black (canvas is not being resized). I'm not sure how to solve this. Any ideas or solutions would help alot.
Code:
Controller:
public class MainFXMLController implements Initializable
{
#FXML
private Canvas mainCanvas;
#FXML
public GraphicsContext gc;
public void initGraphics()
{
gc = mainCanvas.getGraphicsContext2D();
}
public void drawClicked(MouseEvent me)
{
gc.clearRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
System.out.println("Current mosue position: " + me.getX() + ":" + me.getY());
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
initGraphics();
}
}
Fxml:
<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainFXMLController">
<children>
<Canvas fx:id="mainCanvas" height="565.0" onMouseClicked="#drawClicked" width="750.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />
Main Java file:
public class DrawFx extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("MainFXML.fxml"));
Scene scene = new Scene(root);
stage.setTitle("DrawFx");
stage.getIcons().add(new Image("/icon/icon.png"));
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
First some Javadocs :)
A Canvas node is constructed with a width and height that specifies the size of the image into which the canvas drawing commands are rendered. All drawing operations are clipped to the bounds of that image.
So every time the user resize the window we need to change the width of the canvas and then we need to re-draw the canvas.
Lets start by adding a fx:id to the root layout.
<AnchorPane fx:id="anchorPane" prefHeight="600.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainFXMLController">
<children>
<Canvas fx:id="mainCanvas" height="565.0" onMouseClicked="#drawClicked" width="750.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0"/>
</children>
</AnchorPane>
Next step is to add a change listener to the root layout which will set the new height and width to the canvas and then redraw it. We can do it inside the initialize() of the controller.
public class Controller implements Initializable {
#FXML
AnchorPane anchorPane;
#FXML
private Canvas mainCanvas;
#FXML
public GraphicsContext gc;
public void initGraphics() {
gc = mainCanvas.getGraphicsContext2D();
}
public void drawClicked() {
gc.clearRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
gc.setFill(Color.BLACK);
gc.fillRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
}
#Override
public void initialize(URL url, ResourceBundle rb) {
initGraphics();
anchorPane.prefWidthProperty().addListener((ov, oldValue, newValue) -> {
mainCanvas.setWidth(newValue.doubleValue());
drawClicked();
});
anchorPane.prefHeightProperty().addListener((ov, oldValue, newValue) -> {
mainCanvas.setHeight(newValue.doubleValue());
drawClicked();
});
}
}
I haven't created a new method for reDraw() since your drawClicked() wasn't doing anything. But, you can separate both the methods once it makes more sense.
The last thing is to bind to root layout's prefWidthProperty() and prefHeightProperty() to the scene's width and height respectively.
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws IOException {
AnchorPane root = FXMLLoader.load(getClass().getResource("MainFXML.fxml"));
Scene scene = new Scene(root);
stage.setTitle("DrawFx");
stage.setScene(scene);
stage.show();
root.prefWidthProperty().bind(scene.widthProperty());
root.prefHeightProperty().bind(scene.heightProperty());
}
}
If you want to resize canvas in fxml and presumably redraw its contents afterwards, the absolute minimum set is something like this:
test.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="test.TestController">
<children>
<Pane fx:id="pane" VBox.vgrow="ALWAYS">
<children>
<Canvas fx:id="canvas" height="${pane.height}" width="${pane.width}"
onWidthChange="#redraw" onHeightChange="#redraw" />
</children>
</Pane>
</children>
</VBox>
TestController.java
package test;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
public class TestController {
#FXML
private Canvas canvas;
#FXML
private void redraw() {
double w=canvas.getWidth();
double h=canvas.getHeight();
GraphicsContext gc=canvas.getGraphicsContext2D();
gc.clearRect(0, 0, w, h);
gc.beginPath();
gc.rect(10, 10, w-20, h-20);
gc.stroke();
}
}
Wrapping (it is not part of the functionality, just provided for completeness)
Test.java
package test;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader=new FXMLLoader(getClass().getResource("test.fxml"));
Parent root=loader.load();
primaryStage.setTitle("Test");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
the test package is there for allowing modular magic,
module-info.java
module cnvtest {
requires transitive javafx.graphics;
requires javafx.fxml;
opens test to javafx.fxml;
exports test;
}
and there are really no more files.
I'm new to Java FX and am creating an application for fun. I'm trying to add a TitledPane dynamically and am getting Null Pointer Exceptions when attempting to lookup the title of the TitledPane about 70% of the time. I tried to create a simple demo of my issue, but was unable to reproduce the issue outside of my application, but I could solve my issue. I'm hoping someone could help me understand why my solution works and maybe point me in the direction of a better solution. I'm using an FXML file with a Controller. I'm attempting to lookup the title inside of Platform.runLater() because I'm manually editing the layout and elements of the title. Inside of the Controller's initialize function, I do the following to get null pointer exceptions:
Platform.runLater(new Runnable() {
#Override
public void run() {
titledpane.lookup(".title"); // will return null about 70% of the time
}
});
// Do more stuff
However, if I wrap that call in a timer and execute it in 500 ms, it doesn't seem to ever return Null.
new java.util.Timer().schedule(new java.util.TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
titledpane.lookup(".title"); // doesn't seem to return null
}
});
}
}, 500);
One forum mentioned that CSS had to be applied to the element prior to calling a lookup on the title, so I tried manually applying CSS to the title, but titledpane.lookup(".title") still returned null. Can anyone help me understand what is happening here? Thanks in advance!
I had the same issue. I resolved it by calling applyCss() and layout() on the pane that contains the TitledPane:
Node loadedPane = paneLoader.load();
bodyPane.setCenter(loadedPane);
bodyPane.applyCss();
bodyPane.layout();
You should read the article Creating a Custom Control with FXML.
Here's an example about how you can load a TitledPane dynamically. A TitledPane is added each time you click the "Add Task" button.
Task.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<fx:root collapsible="false" prefHeight="72.0" prefWidth="202.0" text="Task" type="TitledPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<content>
<Pane prefHeight="43.0" prefWidth="200.0">
<children>
</children>
</Pane>
</content>
</fx:root>
Task.java
import java.io.IOException;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.Region;
public class Task extends Region {
TitledPane titledPane;
public Task( String title) {
final FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Task.fxml"));
titledPane = new TitledPane();
fxmlLoader.setRoot( titledPane);
fxmlLoader.setController( this);
try {
fxmlLoader.load();
} catch( IOException exception) {
throw new RuntimeException( exception);
}
titledPane.setText(title);
getChildren().add( titledPane);
}
}
Demo.java
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
public class Demo extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Button addTaskButton = new Button( "Add Task");
addTaskButton.setOnAction(new EventHandler<ActionEvent>() {
double x=0;
double y=0;
int count=0;
#Override
public void handle(ActionEvent event) {
// calculate new position
x+=50;
y+=50;
// task counter
count++;
Task task = new Task( "Task " + count);
task.relocate(x, y);
root.getChildren().add( task);
}
});
root.getChildren().add( addTaskButton);
Scene scene = new Scene( root, 1024, 768);
primaryStage.setScene( scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Screenshot:
There are various solutions in order to create a custom title. Here's one. Note: You need to provide an icon in the proper path or adapt the path. Alternatively you can just disable the ImageView node and instead use the Rectangle for demonstration purposes. It's just another node that's displayed.
public class Task extends Region {
TitledPane titledPane;
public Task( String title) {
final FXMLLoader fxmlLoader = new FXMLLoader( getClass().getResource( "Task.fxml"));
titledPane = new TitledPane();
fxmlLoader.setRoot( titledPane);
fxmlLoader.setController( this);
try {
fxmlLoader.load();
} catch( IOException exception) {
throw new RuntimeException( exception);
}
// create custom title with text left and icon right
AnchorPane anchorpane = new AnchorPane();
double offsetRight = 20; // just an arbitrary value. usually the offset from the left of the title
anchorpane.prefWidthProperty().bind(titledPane.widthProperty().subtract( offsetRight));
// create text for title
Label label = new Label( title);
// create icon for title
Image image = new Image( getClass().getResource( "title.png").toExternalForm());
ImageView icon = new ImageView();
icon.setImage(image);
// Rectangle icon = new Rectangle(16, 16);
// set text and icon positions
AnchorPane.setLeftAnchor(label, 0.0);
AnchorPane.setRightAnchor(icon, 0.0);
// add text and icon to custom title
anchorpane.getChildren().addAll( label, icon);
// set custom title
titledPane.setGraphic( anchorpane);
// show only our custom title, don't show the text of titlePane
titledPane.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
getChildren().add( titledPane);
}
}
I'm trying to populate the value of a text field in Java FX.
I have the Main Class,the controller and the fxml.I have bind the fxml file with controller and the appropriate field in it. When i try to set its value, it fails.
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class Main extends Application {
private Stage primaryStage;
private FlowPane rootLayout;
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("test.fxml"));
rootLayout = (FlowPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
testController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class testController {
#FXML
private TextField t1;
public testController() {
System.out.println("hi");
t1 = new TextField("j");
t1.setText("hi");
}
}
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.FlowPane?>
<FlowPane prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.testController">
<children>
<TextField fx:id="t1" />
</children>
</FlowPane>
You are doing it in the wrong place! If you need to play around with your controls just before your fxml is loaded, you need to do it in the initialize(). For this your controller should implement the Initializable
So your controller becomes :
public class testController implements Initializable{
#FXML
private TextField t1;
public void initialize() {
System.out.println("hi");
//You should not re-initialize your textfield
//t1 = new TextField("j");
t1.setText("hi");
}
}