I found related questions on the side, but no without using FXML, MenuItem doesn't have a super class that I can cast down to get the scene. I use the below approach when I found the same problem with a button instance.
(Scene) ((Node) event.getSource()).getScene();
I any ideas on how to solve this issue will be most appreciated.
Thanks In advance
here is the window's view
/**
* #author Jose Gonzalez
*
*/
public class Transaction extends TempletePattern{
private ImageView viewImage;
private Button button;
private TransactionController controller;
private TableView<Saving> table;
private TableColumn dateColum;
private TableColumn descriptionColum;
private TableColumn amountColum;
private TableColumn valanceColum;
/**
*
* #param controller
*/
public Transaction(TransactionController controller)
{
this.controller = controller;
}
/**
* main method all private methods in the class and set them up on the borderpane
* #return pane fully setup to be mount on the scene
*/
public BorderPane setScreen()
{
BorderPane trans = new BorderPane();
trans.setStyle("-fx-background: #FFFFFF;");
VBox topBox = new VBox ();
topBox.getChildren().addAll( setMenu(),setTop() );
trans.setTop(topBox );
trans.setBottom(processUpdate(process) );
trans.setCenter(setCenter() );
return trans;
}
/**
*
* #return vbox holding all note pertaining to the center of the borderpane
*/
private VBox setCenter()
{
VBox center = new VBox();
center.setPadding(new Insets(30, 20, 20, 20) );
table = new TableView<>();
table.setEditable(true);
dateColum = new TableColumn("Date");
dateColum.setCellValueFactory( new PropertyValueFactory<>("firstName"));
dateColum.prefWidthProperty().bind(table.widthProperty().divide(4));
dateColum.setResizable(false);
descriptionColum = new TableColumn("Description");
descriptionColum.prefWidthProperty().bind(table.widthProperty().divide(4));
descriptionColum.setResizable(false);
descriptionColum.setCellValueFactory(new PropertyValueFactory<>("lastName") );
amountColum = new TableColumn("Amount");
amountColum.prefWidthProperty().bind(table.widthProperty().divide(4));
amountColum.setResizable(false);
amountColum.setCellValueFactory( new PropertyValueFactory<>("transaction") );
valanceColum = new TableColumn("Availble Valance");
valanceColum.prefWidthProperty().bind(table.widthProperty().divide(4));
valanceColum.setResizable(false);
valanceColum.setCellValueFactory( new PropertyValueFactory<>("valance"));
table.getColumns().addAll(dateColum, descriptionColum, amountColum,valanceColum );
mockInfo();
center.getChildren().add(table);
return center;
}
/**
*
* #return the screen's menu fully set up
*/
private MenuBar setMenu()
{
MenuBar menubar = new MenuBar();
final Menu UserMenu = new Menu("User");
UserMenu.setId("user");
UserMenu.setOnAction(controller);
MenuItem itemLog = new MenuItem("log out");
itemLog.setId("logout");
itemLog.setOnAction(controller);
MenuItem itemAcount = new MenuItem("new acount");
itemAcount.setId("newAccount");
UserMenu.getItems().addAll(itemLog, itemAcount);
final Menu acctMenu = new Menu("Accounts");
MenuItem itemsavis = new MenuItem("Savings");
MenuItem itemCredit = new MenuItem("Credit");
MenuItem itemChecking = new MenuItem("Checking");
acctMenu.getItems().addAll(itemsavis, itemCredit, itemChecking);
final Menu aboutMenu = new Menu("about");
MenuItem itemHelp = new MenuItem("Help");
aboutMenu.getItems().addAll(itemHelp);
menubar.getMenus().addAll(UserMenu, acctMenu, aboutMenu);
return menubar;
}
/**
* #param receive constumer info from model through controller and set it on table.
*/
public void tableDataSetter(ObservableList<Saving> costumerInfo)
{
table.setItems(costumerInfo);
}
/**
* insert mock data to be displayed as a place holder
*/
private void mockInfo()
{
ObservableList<Saving> data =
FXCollections.observableArrayList(
new Saving("11/10/16", "Deposit", 123, "123" ),
new Saving("11/11/16", "Withdraw", 5, "123" ),
new Saving("11/12/16", "Deposit", 24, "123" ),
new Saving("11/13/16", "Withdraw", 62, "123" ),
new Saving("11/14/16", "Deposit", 134, "123" ),
new Saving("11/15/16", "Deposit", 134, "123" ),
new Saving("11/10/16", "Withdraw", 123, "123" ),
new Saving("11/11/16", "Deposit", 5, "123" ),
new Saving("11/12/16", "Withdraw", 24, "123" ),
new Saving("11/13/16", "Deposit", 62, "123" ),
new Saving("11/14/16", "Withdraw", 134, "123" ),
new Saving("11/15/16", "Deposit", 134, "123" )
);
table.setItems( data );
}
}
and here is the controller. ps I just started working on it.
public class TransactionController implements EventHandler{
//private LoginModel model ;
private Transaction view;
public TransactionController()
{
// model = new LoginModel();
view = new Transaction(this);
}
#Override
public void handle(Event event) {
if( ( event.getSource() instanceof MenuItem))
{
System.out.println( "afe " + (((Object)event.getTarget())) );
if( ( (MenuItem) (event.getSource()) ).getId().equals("logout") )
{
System.err.println("from inside logout");
/// this.goTologInt(event);
}else if( ( (MenuItem) (event.getSource()) ).getId().equals("newAccount") )
{
// this.goToNewAct(event);
}
}
}
private void goToNewAct(Event event)
{
Scene scene = (Scene) ( (Control) event.getSource()).getScene();
// Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
NewCustomerView newAcct = new NewCustomerView();
scene.setRoot( newAcct.newCustomerScreen());
}
private void goTologInt(Event event)
{
Scene scene = (Scene) ((Node)event.getSource()).getScene() ;
//Scene scene = (Scene) ((Control) event.getSource()).getScene();
// Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
LoginView newAcct = new LoginView( new LoginController());
scene.setRoot( newAcct.loginScreen());
}
}
You could go up through the menu hierarchy until you reach a menu with a popup and get the owner window of this popup, which is the window the context menu was opened from.
#Override
public void start(Stage primaryStage) {
MenuItem menuItem = new MenuItem("Something");
Menu menuInner = new Menu("menu", null, menuItem);
Menu menuOuter = new Menu("menu", null, menuInner);
MenuBar menuBar = new MenuBar(menuOuter);
StackPane root = new StackPane();
root.setAlignment(Pos.TOP_LEFT);
root.getChildren().add(menuBar);
Scene scene = new Scene(root);
menuItem.setOnAction(evt -> {
MenuItem m = (MenuItem) evt.getSource();
while (m.getParentPopup() == null) {
m = m.getParentMenu();
}
Scene s = m.getParentPopup().getOwnerWindow().getScene();
System.out.println(s == scene);
});
primaryStage.setScene(scene);
primaryStage.show();
}
In general I'd consider it a better idea though to simply make the information about the scene in question available to the EventHandler of the MenuItem:
final Scene scene = ...
menuItem.setOnAction(evt -> {
// do something with scene
});
The alternative to a (effectively) final local variable would be using a field...
Related
I have one parent controller which contain one button. When I click on button it open new window and show some data into table. The code I have used for opening window is
Stage stage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("../layout/SearchCustomer.fxml"));
Parent parent = (Parent) fxmlLoader.load();
Scene scene = new Scene(parent);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(parent.getScene().getWindow());
stage.setScene(scene);
stage.resizableProperty().setValue(false);
stage.showAndWait();
It opens window properly. Now what I need is, when I double click on the row of table of the child window, It should set some value in parent controller textbox. How would we pass this value from child controller to parent controller?
Expose a property in your child controller and observe it from the "parent" controller. There isn't really enough information in your question to give a precise answer, but it would look something like:
public class ChildController {
#FXML
private TableView<Customer> customerTable ;
private final ReadOnlyObjectWrapper<Customer> currentCustomer = new ReadOnlyObjectWrapper<>();
public ReadOnlyObjectProperty<Customer> currentCustomerProperty() {
return currentCustomer.getReadOnlyProperty() ;
}
public Customer getCurrentCustomer() {
return currentCustomer.get();
}
public void initialize() {
// set up double click on table:
customerTable.setRowFactory(tv -> {
TableRow<Customer> row = new TableRow<>();
row.setOnMouseClicked(e -> {
if (row.getClickCount() == 2 && ! row.isEmpty()) {
currentCustomer.set(row.getItem());
}
}
});
}
}
and then you just do:
Stage stage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("../layout/SearchCustomer.fxml"));
Parent parent = (Parent) fxmlLoader.load();
ChildController childController = fxmlLoader.getController();
childController.currentCustomerProperty().addListener((obs, oldCustomer, newCustomer) -> {
// do whatever you need with newCustomer....
});
Scene scene = new Scene(parent);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(parent.getScene().getWindow());
stage.setScene(scene);
stage.resizableProperty().setValue(false);
stage.showAndWait();
An alternative approach is to use a Consumer as a callback in the child controller:
public class ChildController {
#FXML
private TableView<Customer> customerTable ;
private Consumer<Customer> customerSelectCallback ;
public void setCustomerSelectCallback(Consumer<Customer> callback) {
this.customerSelectCallback = callback ;
}
public void initialize() {
// set up double click on table:
customerTable.setRowFactory(tv -> {
TableRow<Customer> row = new TableRow<>();
row.setOnMouseClicked(e -> {
if (row.getClickCount() == 2 && ! row.isEmpty()) {
if (customerSelectCallback != null) {
customerSelectCallback.accept(row.getItem());
}
}
}
});
}
}
And in this version you do
Stage stage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(
getClass().getResource("../layout/SearchCustomer.fxml"));
Parent parent = (Parent) fxmlLoader.load();
ChildController childController = fxmlLoader.getController();
childController.setCustomerSelectCallback(customer -> {
// do whatever you need with customer....
});
Scene scene = new Scene(parent);
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(parent.getScene().getWindow());
stage.setScene(scene);
stage.resizableProperty().setValue(false);
stage.showAndWait();
In my example below, the account window (child/dialog) passes the newly selected account name back to the parent window when the save button is clicked. On the parent window ...
Stage nstage = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Accounts.fxml"));
Parent root = (Parent) fxmlLoader.load();
AccountsController accController = fxmlLoader.getController();
accController.dialogMode(); //Call a function in acconunt windoow to preset some fields
Scene scene = new Scene(root);
nstage.setTitle("Create Company Account");
nstage.setScene(scene);
Stage stage = (Stage) src.getScene().getWindow();
nstage.initOwner(stage);
nstage.initModality(Modality.APPLICATION_MODAL);
//nstage.initStyle(StageStyle.UNDECORATED);
nstage.show();
nstage.setOnCloseRequest(new EventHandler<WindowEvent>(){
public void handle(WindowEvent we) {
txtCompany.setText( accController.lbl1.getText() );
}
});
I would like to be able to use a modal window to present the contents of a column to the user for editing. I am not able to make this work, and I am not sure where I am going wrong.
I have provided a button in the table which will indicate if there are additional details (in this case comments). When the user selects the button, I want to open a modal dialog to enter the data and when it closes, update the field.
I have gotten the majority of this wired up, but the data is not making it back to my model. I have tried several things, and all without results. It appears that the commit edit call I am making is not seeing the field as in "edit mode" and just skips.
This is my code for my custom table cell:
public class CommentTableCell<T> extends TableCell<T, String> {
private Button actionBtn;
private TextArea textArea;
public CommentTableCell(TableColumn<T, String> column) {
super();
actionBtn = new Button("my action");
actionBtn.setTooltip(new Tooltip("Select to add/edit comments..."));
actionBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
startEdit();
System.out.println("Action: "+getItem());
Stage commentStage = new Stage();
AnchorPane ap = new AnchorPane();
textArea = new TextArea();
AnchorPane.setTopAnchor(textArea, 5.0);
AnchorPane.setBottomAnchor(textArea, 5.0);
AnchorPane.setLeftAnchor(textArea, 5.0);
AnchorPane.setRightAnchor(textArea, 5.0);
ap.getChildren().add(textArea);
Scene commentScene = new Scene (ap, 200, 200);
commentStage.setScene(commentScene);
commentStage.show();
commentStage.setOnCloseRequest(a -> {
commitEdit(textArea.getText());
});
// I have tried with an column.setOnEditCommit() as well as what is noted below which I found here, passing in the column.
final TableView<T> tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
}
});
setText(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
actionBtn.getStyleClass().clear();
setEditable(false);
if (item != null && item.length() > 0) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_EDIT_COMMNET);
setGraphic(actionBtn);
} else if (!empty) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_ADD_COMMNET);
setGraphic(actionBtn);
} else {
setGraphic(null);
}
}
}
During the execution it hits the commitEdit() call and the following has isEditing in the TableCell as null:
#Override public void commitEdit(T newValue) {
if (! isEditing()) return;
My table looks basically like this:
TableView<SomeDTO> addressTableView = new TableView()
addressTableView.setItems(sortedItems);
addressTableView.setEditable(true);
commentsColumn.setCellValueFactory(cellValue -> cellValue.getValue().commentsProperty());
commentsColumn.setCellFactory(tc -> new CommentTableCell<SomeDTO>(commentsColumn));
I have found a solution to my issue - though I am not sure it is the best way or not.
I have changed my CommentTableCell as follows and it seems to work like a charm..
public class CommentTableCell<T> extends TableCell<T, String> {
private Button actionBtn;
public CommentTableCell() {
super();
actionBtn = new Button("my action");
actionBtn.setTooltip(new Tooltip("Select to add/edit comments..."));
actionBtn.setOnAction(event ->
{
Stage commentStage = new Stage();
AnchorPane ap = new AnchorPane();
TextArea textArea = new TextArea();
AnchorPane.setTopAnchor(textArea, 5.0);
AnchorPane.setBottomAnchor(textArea, 5.0);
AnchorPane.setLeftAnchor(textArea, 5.0);
AnchorPane.setRightAnchor(textArea, 5.0);
ap.getChildren().add(textArea);
Scene commentScene = new Scene (ap, 200, 200);
commentStage.setScene(commentScene);
if(getItem() != null) {
String myValue = getItem();
textArea.setText(myValue);
textArea.selectAll();
}
commentStage.show();
commentStage.setOnCloseRequest(a -> {
commitEdit(textArea.getText());
});
});
}
#Override
#SuppressWarnings({"unchecked", "rawtypes"})
public void commitEdit(String item) {
if (isEditing()) {
super.commitEdit(item);
} else {
final TableView table = getTableView();
if (table != null) {
TablePosition position = new TablePosition(getTableView(),
getTableRow().getIndex(), getTableColumn());
CellEditEvent editEvent = new CellEditEvent(table, position,
TableColumn.editCommitEvent(), item);
Event.fireEvent(getTableColumn(), editEvent);
}
updateItem(item, false);
if (table != null) {
table.edit(-1, null);
}
}
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
actionBtn.getStyleClass().clear();
setEditable(false);
if (item != null && item.length() > 0) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_EDIT_COMMNET);
setGraphic(actionBtn);
} else if (!empty) {
actionBtn.getStyleClass().add(CSSConstants.GRID_BUTTON_ADD_COMMNET);
setGraphic(actionBtn);
} else {
setGraphic(null);
}
}
}
My problem is, that after every keyframe the x- and y-Position of the rectangle should change by random.
Right now only when I start the program, the rectangles position is set by random, but not in the animation itself.
How can I do this, thanks a lot...
public class TimeLines extends Application {
private Rectangle rectBasicTimeline;
private Timeline timeline;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Do Animation");
int x = new Random().nextInt(500);
int y = new Random().nextInt(400);
rectBasicTimeline = new Rectangle(x, y, 100, 50);
rectBasicTimeline.setFill(Color.RED);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
final Timeline timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(true);
final KeyValue kx = new KeyValue(rectBasicTimeline.xProperty(), x + 200);
final KeyValue ky = new KeyValue(rectBasicTimeline.yProperty(), y + 200);
final KeyValue kScale = new KeyValue(rectBasicTimeline.scaleXProperty(), 2);
final KeyValue kFade = new KeyValue(rectBasicTimeline.opacityProperty(), 0);
final KeyFrame kf = new KeyFrame(Duration.millis(3000), kx, ky, kScale, kFade);
timeline.getKeyFrames().add(kf);
timeline.play();
}
});
AnchorPane root = new AnchorPane();
root.getChildren().addAll(btn, rectBasicTimeline);
Scene scene = new Scene(root, 800, 600);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I agree with #Roland, no need to create multiple Timeline. Reclicking the button just restarts the timeline with new KeyFrame:
public class StarFall extends Application
{
private Polygon star;
private Timeline timeline;
private final double shs = 5.0; // Star Hand Size
private final Random random = new Random();
#Override
public void start( Stage primaryStage )
{
// init shape
Pos initPos = getRandomPos();
star = new Polygon();
star.setLayoutX( initPos.x );
star.setLayoutY( initPos.y );
star.setFill( Color.YELLOW );
// the shape
star.getPoints().addAll( new Double[]
{
0.0, shs * 3,
shs * 2, shs * 2,
shs * 3, 0.0,
shs * 4, shs * 2,
shs * 6, shs * 3,
shs * 4, shs * 4,
shs * 3, shs * 6,
shs * 2, shs * 4
} );
// init timeline
timeline = new Timeline();
timeline.setCycleCount( Timeline.INDEFINITE );
timeline.setAutoReverse( true );
// init button
Button btnStart = new Button( "Do Animation" );
btnStart.setOnAction( ( e ) -> playNextKeyFrame() );
Button btnStop = new Button( "Stop Animation" );
btnStop.setLayoutX( 200 );
btnStart.setLayoutX( 0 );
btnStop.setOnAction( ( e ) -> timeline.stop() );
// init scene with root
AnchorPane root = new AnchorPane( btnStart, btnStop, star );
Scene scene = new Scene( root, 800, 600 );
// show
primaryStage.setScene( scene );
primaryStage.show();
}
private void playNextKeyFrame()
{
// generate next random start and end positions for star
Pos startPos = getRandomPos();
Pos endPos = getRandomPos();
// initial values (resetting)
star.setLayoutX( startPos.x );
star.setLayoutY( startPos.y );
star.setScaleX( 1 );
star.setScaleY( 1 );
star.setOpacity( 1 );
// target values
KeyValue kx = new KeyValue( star.layoutXProperty(), endPos.x );
KeyValue ky = new KeyValue( star.layoutYProperty(), endPos.y );
KeyValue kScaleX = new KeyValue( star.scaleXProperty(), 3 );
KeyValue kScaleY = new KeyValue( star.scaleYProperty(), 3 );
KeyValue kFade = new KeyValue( star.opacityProperty(), 0.0 );
// delay animation before start. Use this instead of THread.sleep() !!
timeline.setDelay( Duration.millis( random.nextInt( 2000 ) + 100 ) );
// restart timeline with new values
timeline.stop();
timeline.getKeyFrames().clear();
timeline.getKeyFrames().add( new KeyFrame( Duration.millis( 3000 ),
( e ) -> playNextKeyFrame(), kx, ky, kFade, kScaleX, kScaleY ) );
timeline.play();
}
private Pos getRandomPos()
{
int x = random.nextInt( 500 );
int y = random.nextInt( 400 );
Pos p = new Pos();
p.x = x + 200;
p.y = y + 200;
return p;
}
private class Pos
{
int x;
int y;
}
public static void main( String[] args )
{
launch( args );
}
}
You initialize the random numbers and hence the location once and use it all the time. What you need to do is to perform the animation cycle once and then create a new animation when the current one finishes.
Example:
import java.util.Random;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
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.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TimeLines extends Application {
private Rectangle rectBasicTimeline;
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Do Animation");
int x = new Random().nextInt(500);
int y = new Random().nextInt(400);
rectBasicTimeline = new Rectangle(x, y, 100, 50);
rectBasicTimeline.setFill(Color.RED);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
play();
}
});
AnchorPane root = new AnchorPane();
root.getChildren().addAll(btn, rectBasicTimeline);
Scene scene = new Scene(root, 800, 600);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
private void play() {
double x = new Random().nextInt(500);
double y = new Random().nextInt(400);
final Timeline timeline = new Timeline();
// cycle count = 2 because of autoreverse
timeline.setCycleCount(2);
timeline.setAutoReverse(true);
final KeyValue kx = new KeyValue(rectBasicTimeline.xProperty(), x + 200);
final KeyValue ky = new KeyValue(rectBasicTimeline.yProperty(), y + 200);
final KeyValue kScale = new KeyValue(rectBasicTimeline.scaleXProperty(), 2);
final KeyValue kFade = new KeyValue(rectBasicTimeline.opacityProperty(), 0);
final KeyFrame kf = new KeyFrame(Duration.millis(1000), kx, ky, kScale, kFade);
timeline.getKeyFrames().add(kf);
timeline.setOnFinished(e -> {
// create new animation after this animation finishes
play();
});
timeline.play();
}
/**
* #param args
* the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I wouldn't suggest this approach though, e. g. you run into problems with multiple timelines when you click the button multiple times. But I have no information what you're trying to do, so I'll leave it at that.
The root layout in this case which has content...
object SomeApp extends JFXApp {
stage = new PrimaryStage {
title = "SomeApp"
width = 800
height = 600
val TheButton = new Button("Add a Button") {
onAction = {
e: ActionEvent => {
root.dd(new Button("Some Button"))
}
}
}
val SomeLayout = new AnchorPane {
styleClass += "someStyle"
content = TheButton
}
scene = new Scene {
stylesheets = List(getClass.getResource("Extend.css").toExternalForm)
root = new AnchorPane {
content = SomeLayout
}
}
}
}
I am trying to get it to add a button to the root layout when TheButton is clicked...
This works:
object SFXAddElemAfterStart extends JFXApp {
stage = new PrimaryStage {
title = "SFXAddElemAfterStart"
width = 800
height = 600
scene = new Scene {
val paneNeedingButton = new AnchorPane {
content = layout
}
root = paneNeedingButton
def layout: AnchorPane = new AnchorPane {
styleClass += "someStyle"
content = button
}
def button = new Button("Add a Button") {
onAction = {
e: ActionEvent => {
paneNeedingButton.children += new Button("Some Button")
}
}
}
}
}
}
You just need to put the button in a reasonable place.
(BTW sorry for the long wait, scalafx is a low traffic tag)
I have a swing gui application. I want to type into a image path and then click button to load the image into a jpanel. The problem is it won't be loaded, but if I add the extended jpanel which is able to load image when I instiate the jframe, image can be loaded normally. why is that?
code related:
package com.xdg.graphic;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImageLoader extends JPanel {
private String imgPath;
private BufferedImage image;
public ImageLoader(String imgPath) {
this.imgPath = imgPath;
try {
this.image=ImageIO.read(new File(imgPath));
} catch (IOException e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
#Override
protected void paintComponent(Graphics g) {
g.drawImage(image, 0, 0, null);
}
public Dimension getPreferredSize() {
if (image == null) {
return new Dimension(100,100);
} else {
return new Dimension(image.getWidth(null), image.getHeight(null));
}
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
}
invoker class:
package com.xdg.image;
import com.xdg.graphic.ImageLoader;
import sun.awt.windows.ThemeReader;
import java.awt.*;
public class FrmImgCropper extends javax.swing.JFrame {
private ImageLoader imageLoader;
/** Creates new form FrmImgCropper */
public FrmImgCropper() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
//GEN-BEGIN:initComponents
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jPanel2 = new javax.swing.JPanel();
tfImagePath = new javax.swing.JTextField();
btnPreview = new javax.swing.JButton();
tfRatioW = new javax.swing.JTextField();
tfRatioH = new javax.swing.JTextField();
jLabel1 = new javax.swing.JLabel();
btnLoad = new javax.swing.JButton();
jLabel2 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
btnPreview.setText("Preview");
jLabel1.setText(":");
btnLoad.setText("Load");
btnLoad.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnLoadActionPerformed(evt);
}
});
jLabel2.setText("Image Path:");
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(jPanel2Layout
.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
jPanel2Layout
.createSequentialGroup()
.addGap(34, 34, 34)
.addComponent(tfRatioW, javax.swing.GroupLayout.PREFERRED_SIZE, 55,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tfRatioH, javax.swing.GroupLayout.PREFERRED_SIZE, 53,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 123,
Short.MAX_VALUE)
.addComponent(btnLoad, javax.swing.GroupLayout.PREFERRED_SIZE, 82,
javax.swing.GroupLayout.PREFERRED_SIZE).addGap(46, 46, 46)
.addComponent(btnPreview).addGap(276, 276, 276))
.addGroup(
jPanel2Layout.createSequentialGroup().addGap(66, 66, 66).addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(tfImagePath, javax.swing.GroupLayout.DEFAULT_SIZE, 593, Short.MAX_VALUE)
.addGap(29, 29, 29)));
jPanel2Layout.setVerticalGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
javax.swing.GroupLayout.Alignment.TRAILING,
jPanel2Layout
.createSequentialGroup()
.addContainerGap(20, Short.MAX_VALUE)
.addGroup(
jPanel2Layout
.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(tfImagePath, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(
jPanel2Layout
.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(tfRatioW, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(tfRatioH, javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnPreview).addComponent(btnLoad)).addGap(20, 20, 20)));
getContentPane().add(jPanel2, java.awt.BorderLayout.SOUTH);
pack();
}// </editor-fold>
//GEN-END:initComponents
private void btnLoadActionPerformed(java.awt.event.ActionEvent evt) {
if (imageLoader == null) {
imageLoader = new ImageLoader(tfImagePath.getText());
imageLoader.setBackground(Color.green);
getContentPane().add(imageLoader, BorderLayout.CENTER);
getContentPane().repaint();
this.repaint();//image does not show up this way dnamically
} else {
imageLoader.setImgPath(tfImagePath.getText());
imageLoader.repaint();
}
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
FrmImgCropper cropper = new FrmImgCropper();
cropper.getContentPane().add(new ImageLoader("i:\\temp4\\car.jpg")); //if I add the image loader here directly,image shows up
cropper.setSize(800, 900);
cropper.setVisible(true);
}
});
}
//GEN-BEGIN:variables
// Variables declaration - do not modify
private javax.swing.JButton btnLoad;
private javax.swing.JButton btnPreview;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JPanel jPanel2;
private javax.swing.JTextField tfImagePath;
private javax.swing.JTextField tfRatioH;
private javax.swing.JTextField tfRatioW;
// End of variables declaration//GEN-END:variables
}
any ideas? I've been working on it two hours.
To update your panel you should call revalidate() method on panel's parent, not repaint() on the new panel since you change the content itself (the panel) but the image. In your case its content pane, which content should be validated.
But its still not the best way to update the image inside the panel if you ask me...
You can try this simple example (its much more simple and works perfectly):
private static BufferedImage image = null;
public static void main ( String[] args )
{
final JFrame imageFrame = new JFrame ();
imageFrame.setLayout ( new BorderLayout () );
final JPanel panel = new JPanel ()
{
protected void paintComponent ( Graphics g )
{
super.paintComponent ( g );
if ( image != null )
{
g.drawImage ( image, getWidth () / 2 - image.getWidth () / 2,
getHeight () / 2 - image.getHeight () / 2, this );
}
}
};
imageFrame.add ( panel, BorderLayout.CENTER );
imageFrame.add ( new JButton ( "Load image" )
{
{
addActionListener ( new ActionListener ()
{
public void actionPerformed ( ActionEvent e )
{
JFileChooser fc = new JFileChooser ();
fc.setDialogType ( JFileChooser.OPEN_DIALOG );
if ( fc.showOpenDialog ( imageFrame ) == JFileChooser.APPROVE_OPTION )
{
try
{
image = ImageIO.read ( fc.getSelectedFile () );
panel.repaint ();
}
catch ( IOException e1 )
{
//
}
}
}
} );
}
}, BorderLayout.SOUTH );
imageFrame.setSize ( 500, 500 );
imageFrame.setLocationRelativeTo ( null );
imageFrame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
imageFrame.setVisible ( true );
}
The only actual thing you need to do if you change the image inside panel - repaint the affected panel rect (or the whole panel if you don't like to get deep into the way graphics in Swing works).
As you can see i didn't even touch the panel itself - just changed the image source.