I'm working on creating a simulation program for a game called Counter-Strike: Global Offensive, and I'm stuck on how I should animate certain images within an HBox. In the game, there are Weapon Cases containing various skins of different rarity. Upon clicking the 'Open' button, the possible items to win should start scrolling through the HBox. Think of it almost like Wheel of Fortune, where it begins fast and gradually slows down until it stops on 1 name, but in this case, instead of names there are items, and instead of a 'wheel', I have a horizontal HBox with images scrolling through. Here is a good example of what I'm trying to make: http://cases.goaggro.com/.
Currently, I have an instance of the ImageView class assigned to each individual weapon image that should be included in the total group of items to be rolled, and an array to hold all of these images. On the GUI, I have the HBox's max width and height set so that any 3 of these images placed inside the HBox from left to right fill its' volume perfectly. My first problem resides here. If I try to do something like
Hbox.getChildren().addAll(itemArray);
it will add the first 3 just fine, but will continue to add images beyond the boundaries of the HBox until reaching the main windows border. Is there any way to add more images into the HBox than the max displayed image count (being 3 due to the current HBox size I have set) while preventing them from exceeding the HBox boundaries and simply being hidden in the back?
The second question is, what would be the best way to go about animating each image inside of my HBox so that it scrolls towards the left but not beyond or outside of the HBox borders? I need the left-most image to slide left off the screen, while the middle image slides to the left position, and a new image slides in from the right to fill the right position, and repeat this at a speed that gradually slows to the point of landing on an item.
Currently, adding this code to the 'Open' button's event handler adds the images in item1, item2, and item3 correctly into the HBox. But if I exceed 3 images (like setting the HBox .addAll() to itemArray instead of the first 3 items individually, for example), it exceeds the HBox border and starts laying them on top of the scene until reaching the main windows border. NOTE: There are a total of 15 instances of the ImageView class (item1-item15) but I shortened the code to only 4, as well as the arrays contents to those 4, since the issue happens regardless when any more than 3 images are placed into the HBox.
public class Controller
{
#FXML
private HBox hBox;
public void openCaseAction(ActionEvent actionEvent)
{
final ImageView item1 = new ImageView(new Image(getClass().getResourceAsStream("../images/image1.png")));
final ImageView item2 = new ImageView(new Image(getClass().getResourceAsStream("../images/image2.png")));
final ImageView item3 = new ImageView(new Image(getClass().getResourceAsStream("../images/image3.png")));
final ImageView item4 = new ImageView(new Image(getClass().getResourceAsStream("../images/image4.png")));
final ImageView[] itemArray ={item1,item2,item3,item4};
hBox.getChildren().addAll(item1,item2,item3);
}
}
You probably want to use a custom layout for this rather than a HBox. Take a look at the Display Shelf example:
Download the Java 8 demos and samples from Oracle.
Extract the sample package.
Run the demo/javafx_samples/Ensemble8.jar program.
Type "Display Shelf" into the program's search bar.
View the Display Shelf example UI and source code.
Copy and modify as you see fit while respecting the original license terms.
It won't be exactly what you are looking for but it will be a much closer starting point than trying to animate items in a HBox.
Oracle DisplayShelf sample code:
/*
* Copyright (c) 2008, 2014, Oracle and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*
* This file is available and licensed under the following license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* - Neither the name of Oracle Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package ensemble.samples.graphics2d.displayshelf;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.control.ScrollBar;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;
/**
* Simple 7 segment LED style digit. It supports the numbers 0 through 9.
*/
/**
* A ui control which displays a browse-able display shelf of images
*/
public class DisplayShelf extends Region {
private final Duration DURATION = Duration.millis(500);
private final Interpolator INTERPOLATOR = Interpolator.EASE_BOTH;
private final double SPACING = 50;
private final double LEFT_OFFSET = -110;
private final double RIGHT_OFFSET = 110;
private final double SCALE_SMALL = 0.7;
private PerspectiveImage[] items;
private Group centered = new Group();
private Group left = new Group();
private Group center = new Group();
private Group right = new Group();
private int centerIndex = 0;
private Timeline timeline;
private ScrollBar scrollBar = new ScrollBar();
private boolean localChange = false;
private Rectangle clip = new Rectangle();
public DisplayShelf(Image[] images) {
// set clip
setClip(clip);
// set ids for styling via CSS
setId("displayshelf");
scrollBar.setId("display-scrollbar");
// create items
items = new PerspectiveImage[images.length];
for (int i = 0; i < images.length; i++) {
final PerspectiveImage item =
items[i] = new PerspectiveImage(images[i]);
final double index = i;
item.setOnMouseClicked((MouseEvent me) -> {
localChange = true;
scrollBar.setValue(index);
localChange = false;
shiftToCenter(item);
});
}
// setup scroll bar
scrollBar.setMax(items.length - 1);
scrollBar.setVisibleAmount(1);
scrollBar.setUnitIncrement(1);
scrollBar.setBlockIncrement(1);
scrollBar.valueProperty().addListener((Observable ov) -> {
if (!localChange) {
shiftToCenter(items[(int) Math.round(scrollBar.getValue())]);
}
});
// create content
centered.getChildren().addAll(left, right, center);
getChildren().addAll(centered, scrollBar);
// listen for keyboard events
setFocusTraversable(true);
setOnKeyPressed((KeyEvent ke) -> {
if (ke.getCode() == KeyCode.LEFT) {
shift(1);
localChange = true;
scrollBar.setValue(centerIndex);
localChange = false;
} else if (ke.getCode() == KeyCode.RIGHT) {
shift(-1);
localChange = true;
scrollBar.setValue(centerIndex);
localChange = false;
}
});
// update
update();
}
#Override
protected void layoutChildren() {
// update clip to our size
clip.setWidth(getWidth());
clip.setHeight(getHeight());
// keep centered centered
centered.setLayoutY((getHeight() - PerspectiveImage.HEIGHT) / 2);
centered.setLayoutX((getWidth() - PerspectiveImage.WIDTH) / 2);
// position scroll bar at bottom
scrollBar.setLayoutX(10);
scrollBar.setLayoutY(getHeight() - 25);
scrollBar.resize(getWidth() - 20, 15);
}
private void update() {
// move items to new homes in groups
left.getChildren().clear();
center.getChildren().clear();
right.getChildren().clear();
for (int i = 0; i < centerIndex; i++) {
left.getChildren().add(items[i]);
}
center.getChildren().add(items[centerIndex]);
for (int i = items.length - 1; i > centerIndex; i--) {
right.getChildren().add(items[i]);
}
// stop old timeline if there is one running
if (timeline != null) {
timeline.stop();
}
// create timeline to animate to new positions
timeline = new Timeline();
// add keyframes for left items
final ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames();
for (int i = 0; i < left.getChildren().size(); i++) {
final PerspectiveImage it = items[i];
double newX = -left.getChildren().size()
* SPACING + SPACING * i + LEFT_OFFSET;
keyFrames.add(new KeyFrame(DURATION,
new KeyValue(it.translateXProperty(), newX, INTERPOLATOR),
new KeyValue(it.scaleXProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.scaleYProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.angle, 45.0, INTERPOLATOR)));
}
// add keyframe for center item
final PerspectiveImage centerItem = items[centerIndex];
keyFrames.add(new KeyFrame(DURATION,
new KeyValue(centerItem.translateXProperty(), 0, INTERPOLATOR),
new KeyValue(centerItem.scaleXProperty(), 1.0, INTERPOLATOR),
new KeyValue(centerItem.scaleYProperty(), 1.0, INTERPOLATOR),
new KeyValue(centerItem.angle, 90.0, INTERPOLATOR)));
// add keyframes for right items
for (int i = 0; i < right.getChildren().size(); i++) {
final PerspectiveImage it = items[items.length - i - 1];
final double newX = right.getChildren().size()
* SPACING - SPACING * i + RIGHT_OFFSET;
keyFrames.add(new KeyFrame(DURATION,
new KeyValue(it.translateXProperty(), newX, INTERPOLATOR),
new KeyValue(it.scaleXProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.scaleYProperty(), SCALE_SMALL, INTERPOLATOR),
new KeyValue(it.angle, 135.0, INTERPOLATOR)));
}
// play animation
timeline.play();
}
private void shiftToCenter(PerspectiveImage item) {
for (int i = 0; i < left.getChildren().size(); i++) {
if (left.getChildren().get(i) == item) {
int shiftAmount = left.getChildren().size() - i;
shift(shiftAmount);
return;
}
}
if (center.getChildren().get(0) == item) {
return;
}
for (int i = 0; i < right.getChildren().size(); i++) {
if (right.getChildren().get(i) == item) {
int shiftAmount = -(right.getChildren().size() - i);
shift(shiftAmount);
return;
}
}
}
public void shift(int shiftAmount) {
if (centerIndex <= 0 && shiftAmount > 0) {
return;
}
if (centerIndex >= items.length - 1 && shiftAmount < 0) {
return;
}
centerIndex -= shiftAmount;
update();
}
}
/*
* Copyright (c) 2008, 2014, Oracle and/or its affiliates.
* All rights reserved. Use is subject to license terms.
*
* This file is available and licensed under the following license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
* - Neither the name of Oracle Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package ensemble.samples.graphics2d.displayshelf;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Parent;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.effect.Reflection;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
/**
* A Node that displays a image with some 2.5D perspective rotation around the Y
* axis.
*/
public class PerspectiveImage extends Parent {
private static final double REFLECTION_SIZE = 0.25;
public static final double WIDTH = 200;
public static final double HEIGHT = WIDTH + (WIDTH * REFLECTION_SIZE);
private static final double RADIUS_H = WIDTH / 2;
private static final double BACK = WIDTH / 10;
private PerspectiveTransform transform = new PerspectiveTransform();
/**
* Angle Property
*/
public final DoubleProperty angle = new SimpleDoubleProperty(45) {
#Override
protected void invalidated() {
// when angle changes calculate new transform
double lx = (RADIUS_H - Math.sin(Math.toRadians(angle.get())) * RADIUS_H - 1);
double rx = (RADIUS_H + Math.sin(Math.toRadians(angle.get())) * RADIUS_H + 1);
double uly = (-Math.cos(Math.toRadians(angle.get())) * BACK);
double ury = -uly;
transform.setUlx(lx);
transform.setUly(uly);
transform.setUrx(rx);
transform.setUry(ury);
transform.setLrx(rx);
transform.setLry(HEIGHT + uly);
transform.setLlx(lx);
transform.setLly(HEIGHT + ury);
}
};
public final double getAngle() {
return angle.getValue();
}
public final void setAngle(double value) {
angle.setValue(value);
}
public final DoubleProperty angleModel() {
return angle;
}
public PerspectiveImage(Image image) {
ImageView imageView = new ImageView(image);
Reflection reflection = new Reflection();
reflection.setFraction(REFLECTION_SIZE);
imageView.setEffect(reflection);
setEffect(transform);
getChildren().addAll(imageView);
}
}
Related
I am making a text editor, And i want to make it when the user type a article then if the user select a specific word then change it's color the color will be changed and also if the user select the same word but in another place this word's color only will be changed
As indicated in this picture:
because of all of my search results was that i change only a word's color even if it repeated it will be also colored
I am trying in JtextPane, I have searched in "Oracle Java Docs" for the JtextPane methodes but i didn't found anything
It is necessary to use JTextPane.
Just use new FindManager(yourTextPane, Color.RED);.
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import java.awt.Color;
import java.util.ArrayList;
/**
* #author Swimer
*/
public class FindManager {
/**
* Constructor
* #param pane Swing text pane
* #param selection The color to apply to characters similar to selection
*/
public FindManager(JTextPane pane, Color selection) {
Style selected = pane.getStyledDocument().addStyle("selection-text", null);
Style defaultStyle = pane.getStyledDocument().addStyle("default", null);
StyleConstants.setForeground(selected, selection);
pane.addCaretListener(e -> {
Runnable doHighlight = () -> {
try {
String selectedText = pane.getSelectedText();
String text = pane.getStyledDocument().getText(0, pane.getStyledDocument().getLength());
if (selectedText != null && selectedText.length() > 0) {
for (Integer string : getStrings(text, selectedText, pane.getSelectionStart())) {
pane.getStyledDocument().setCharacterAttributes(string, selectedText.length(), selected, true);
}
}else{
pane.getStyledDocument().setCharacterAttributes(0, text.length(), defaultStyle, true);
}
} catch (Exception exception) {
exception.printStackTrace();
}
};
SwingUtilities.invokeLater(doHighlight);
});
}
/**
* Get positions of similar words
* #param text The string to look for
* #param toFind What to look for
* #param ignore What to ignore
* #return List of similar word positions
*/
public static ArrayList<Integer> getStrings(String text, String toFind, int ignore){
ArrayList<Integer> out = new ArrayList<>();
int index = text.indexOf(toFind);
while (index >= 0 && index != ignore) {
out.add(index);
index = text.indexOf(toFind, index+toFind.length());
}
return out;
}
}
I have the sample code below, and I'm having problems understanding it. So I'd like two ask 2 questions:
Why does JFormattedTextField have a "value" (getValue(), setValue()) and a JTextField does not?
In the propertyChange() method, why is it sufficient to write:
amount = ((Number)amountField.getValue()).doubleValue();
to update the amount so that it is displayed the right way, but for the paymentField I have to call the setValue() method?
Sample:
/*
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.text.*;
/**
* FormattedTextFieldDemo.java requires no other files.
*
* It implements a mortgage calculator that uses four
* JFormattedTextFields.
*/
public class FormattedTextFieldDemo extends JPanel
implements PropertyChangeListener {
//Values for the fields
private double amount = 100000;
private double rate = 7.5; //7.5%
private int numPeriods = 30;
//Labels to identify the fields
private JLabel amountLabel;
private JLabel rateLabel;
private JLabel numPeriodsLabel;
private JLabel paymentLabel;
//Strings for the labels
private static String amountString = "Loan Amount: ";
private static String rateString = "APR (%): ";
private static String numPeriodsString = "Years: ";
private static String paymentString = "Monthly Payment: ";
//Fields for data entry
private JFormattedTextField amountField;
private JFormattedTextField rateField;
private JFormattedTextField numPeriodsField;
private JFormattedTextField paymentField;
//Formats to format and parse numbers
private NumberFormat amountFormat;
private NumberFormat percentFormat;
private NumberFormat paymentFormat;
public FormattedTextFieldDemo() {
super(new BorderLayout());
setUpFormats();
double payment = computePayment(amount,
rate,
numPeriods);
//Create the labels.
amountLabel = new JLabel(amountString);
rateLabel = new JLabel(rateString);
numPeriodsLabel = new JLabel(numPeriodsString);
paymentLabel = new JLabel(paymentString);
//Create the text fields and set them up.
amountField = new JFormattedTextField(amountFormat);
amountField.setValue(new Double(amount));
amountField.setColumns(10);
amountField.addPropertyChangeListener("value", this);
rateField = new JFormattedTextField(percentFormat);
rateField.setValue(new Double(rate));
rateField.setColumns(10);
rateField.addPropertyChangeListener("value", this);
numPeriodsField = new JFormattedTextField();
numPeriodsField.setValue(new Integer(numPeriods));
numPeriodsField.setColumns(10);
numPeriodsField.addPropertyChangeListener("value", this);
paymentField = new JFormattedTextField(paymentFormat);
paymentField.setValue(new Double(payment));
paymentField.setColumns(10);
paymentField.setEditable(false);
paymentField.setForeground(Color.red);
//Tell accessibility tools about label/textfield pairs.
amountLabel.setLabelFor(amountField);
rateLabel.setLabelFor(rateField);
numPeriodsLabel.setLabelFor(numPeriodsField);
paymentLabel.setLabelFor(paymentField);
//Lay out the labels in a panel.
JPanel labelPane = new JPanel(new GridLayout(0,1));
labelPane.add(amountLabel);
labelPane.add(rateLabel);
labelPane.add(numPeriodsLabel);
labelPane.add(paymentLabel);
//Layout the text fields in a panel.
JPanel fieldPane = new JPanel(new GridLayout(0,1));
fieldPane.add(amountField);
fieldPane.add(rateField);
fieldPane.add(numPeriodsField);
fieldPane.add(paymentField);
//Put the panels in this panel, labels on left,
//text fields on right.
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
add(labelPane, BorderLayout.CENTER);
add(fieldPane, BorderLayout.LINE_END);
}
/** Called when a field's "value" property changes. */
public void propertyChange(PropertyChangeEvent e) {
Object source = e.getSource();
if (source == amountField) {
amount = ((Number)amountField.getValue()).doubleValue();
} else if (source == rateField) {
rate = ((Number)rateField.getValue()).doubleValue();
} else if (source == numPeriodsField) {
numPeriods = ((Number)numPeriodsField.getValue()).intValue();
}
double payment = computePayment(amount, rate, numPeriods);
paymentField.setValue(new Double(payment));
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event dispatch thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("FormattedTextFieldDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new FormattedTextFieldDemo());
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Turn off metal's use of bold fonts
UIManager.put("swing.boldMetal", Boolean.FALSE);
createAndShowGUI();
}
});
}
//Compute the monthly payment based on the loan amount,
//APR, and length of loan.
double computePayment(double loanAmt, double rate, int numPeriods) {
double I, partial1, denominator, answer;
numPeriods *= 12; //get number of months
if (rate > 0.01) {
I = rate / 100.0 / 12.0; //get monthly rate from annual
partial1 = Math.pow((1 + I), (0.0 - numPeriods));
denominator = (1 - partial1) / I;
} else { //rate ~= 0
denominator = numPeriods;
}
answer = (-1 * loanAmt) / denominator;
return answer;
}
//Create and set up number formats. These objects also
//parse numbers input by user.
private void setUpFormats() {
amountFormat = NumberFormat.getNumberInstance();
percentFormat = NumberFormat.getNumberInstance();
percentFormat.setMinimumFractionDigits(3);
paymentFormat = NumberFormat.getCurrencyInstance();
}
}
"Why has a JFormattedTextField an "value" (getValue(), setValue()) and a jTextField not?"
The text attributes of both JTextField and JFormattedTextField are the actual text you see displayed by each. But JFormattedTextField isn't just displaying some text that you give it. It, with the help of a Formatter that you provided, translates between some Object--in this case a Number--and the convenient human-readable text representation you see on the screen. The object whose value you're displaying is the "value" of the JFormattedTextField, how it looks on the screen is the "text". A regular JTextField, on the other hand, just shows whatever text you tell it to show and returns whatever the you type into it, so there's no need for a separate value attribute because it would always be identical to the "text".
"Why is it sufficient to write "amount = ((Number)amountField.getValue()).doubleValue();" to update the amount so that is is displayed the right way but for the paymentField i have to call the setValue-Methode?"
You're not writing
amount = ((Number)amountField.getValue()).doubleValue();
to update amountField, you're doing it to find out what to user typed into amountField. amountField is updating itself in response to user input because that's what JFormattedTextFields do.
Now, having learned what the user typed into amountField, you recalculate the payment amount and tell the paymentField to show the new payment by calling its setValue method.
I've used an example by Ian F. Darwin on how to display a file system on a JTree, and although the code is clear to me, I can't figure out why I can't expand the tree on the valueChanged of the TreeSelectionListener. Here's the code:
package ericsonwrp.republica.vintage.caixa;
/*
* Copyright (c) Ian F. Darwin, http://www.darwinsys.com/, 1996-2002.
* All rights reserved. Software written by Ian F. Darwin and others.
* $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Java, the Duke mascot, and all variants of Sun's Java "steaming coffee
* cup" logo are trademarks of Sun Microsystems. Sun's, and James Gosling's,
* pioneering role in inventing and promulgating (and standardizing) the Java
* language and environment is gratefully acknowledged.
*
* The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
* inventing predecessor languages C and C++ is also gratefully acknowledged.
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.io.File;
import java.util.Collections;
import java.util.Vector;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
/**
* Display a file system in a JTree view
*
* #version $Id: FileTree.java,v 1.9 2004/02/23 03:39:22 ian Exp $
* #author Ian Darwin
*/
public class FileTree extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private JTree tree;
private JScrollPane scrollpane;
/** Construct a FileTree */
public FileTree(File dir) {
setLayout(new BorderLayout());
tree = new JTree(addNodes(null, dir));
tree.setRootVisible(false);
// Add a listener
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) e
.getPath().getLastPathComponent();
TreePath p = new TreePath(node.getPath());
tree.expandPath(p); // HERE <<<<<<<<<<<<<<
}
});
scrollpane = new JScrollPane();
scrollpane.getViewport().add(tree);
add(BorderLayout.CENTER, scrollpane);
}
/** Add nodes from under "dir" into curTop. Highly recursive. */
DefaultMutableTreeNode addNodes(DefaultMutableTreeNode curTop, File dir) {
String curPath = dir.getPath();
DefaultMutableTreeNode curDir = new DefaultMutableTreeNode(curPath);
if (curTop != null) { // should only be null at root
curTop.add(curDir);
}
Vector<String> ol = new Vector<String>();
String[] tmp = dir.list();
for (int i = 0; i < tmp.length; i++)
ol.addElement(tmp[i]);
Collections.sort(ol, String.CASE_INSENSITIVE_ORDER);
File f;
Vector<String> files = new Vector<String>();
// Make two passes, one for Dirs and one for Files. This is #1.
for (int i = 0; i < ol.size(); i++) {
String thisObject = (String) ol.elementAt(i);
String newPath;
if (curPath.equals("."))
newPath = thisObject;
else
newPath = curPath + File.separator + thisObject;
if ((f = new File(newPath)).isDirectory())
addNodes(curDir, f);
else
files.addElement(thisObject);
}
// Pass two: for files.
for (int fnum = 0; fnum < files.size(); fnum++)
curDir.add(new DefaultMutableTreeNode(files.elementAt(fnum)));
return curDir;
}
public Dimension getMinimumSize() {
return new Dimension(200, 400);
}
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
}
Here's a visual example of my "FileChooser" with this JTree:
As you can see, the node was supposed to expand on the valueChanged, and that does not happen ("Games" is a folder).
In your picture Games is not a folder. Look at the picture from your link. Something went wrong in addNodes(). You cant expand a leaf ;)
Try to call FileTree with new File("."). You should see your project folder. This worked for me. When I try to call new FileTree(new File("/")) to get all folders... here addNodes() fails.
Use this solution.
Maybe you have to make some changes:
... implements Comparable<File> {
and
public int compareTo(File object)...
Is it possible to discard loaded content of Image and later load it again? Is it possible to load it on demand?
Can I have ImageView which loads it's image only on show?
The Image class is essentially immutable with respect to its image data, in the sense that you can specify a source for the image data at construction time, and then cannot modify it via the API subsequently.
The ImageView class provides functionality for displaying an image in the UI. The ImageView class is mutable, in the sense that you can change the image it displays.
The basic strategy you need to implement "tiled images" functionality is to create a virtualized container, which has a collection of "cells" or "tiles" which are reused to display different content. This is essentially how controls such as ListView, TableView, and TreeView are implemented in JavaFX. You may also be interested in Tomas Mikula's Flowless implementation of the same kind of idea.
So to implement "tiled images" functionality, you could use an array of ImageViews as the "cells" or "tiles". You can place these in a pane and implement panning/scrolling in the pane, and when image views scroll out of view, reuse the ImageViews by moving the images from one image view to another, loading new images only for the tiles that need it. Obviously, images that are no longer referenced by any image view will be eligible for garbage collection in the usual way.
There are probably other ways to achieve this, such as using WritableImages and using a PixelWriter to update the pixel data when needed. Which works best probably depends somewhat on which is most convenient for the actual format you have for the image data; there is probably little performance difference between different strategies.
If you are loading the images from a server or database, you should do so in the background. If the image is loaded from a URL, the Image class provides functionality to do this directly. If you are loading from an input stream (e.g. from a database BLOB field), you will need to implement the background threading yourself.
Here is the basic idea (no threading):
import java.util.Random;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class PanningTilesExample extends Application {
private static final int TILE_WIDTH = 100;
private static final int TILE_HEIGHT = 100;
private static final int PANE_WIDTH = 800;
private static final int PANE_HEIGHT = 800;
// amount scrolled left, in pixels:
private final DoubleProperty xOffset = new SimpleDoubleProperty();
// amount scrolled right, in pixels:
private final DoubleProperty yOffset = new SimpleDoubleProperty();
// number of whole tiles shifted to left:
private final IntegerProperty tileXOffset = new SimpleIntegerProperty();
// number of whole tiles shifted up:
private final IntegerProperty tileYOffset = new SimpleIntegerProperty();
private final Pane pane = new Pane();
// for enabling dragging:
private double mouseAnchorX;
private double mouseAnchorY;
// array of ImageViews:
private ImageView[][] tiles;
private final Random rng = new Random();
#Override
public void start(Stage primaryStage) {
// update number of tiles offset when number of pixels offset changes:
tileXOffset.bind(xOffset.divide(TILE_WIDTH));
tileYOffset.bind(yOffset.divide(TILE_HEIGHT));
// create the images views, etc. This method could be called
// when the pane size changes, if you want a resizable pane with fixed size tiles:
build();
// while tile offsets change, allocate new images to existing image views:
tileXOffset.addListener(
(obs, oldOffset, newOffset) -> rotateHorizontal(oldOffset.intValue() - newOffset.intValue()));
tileYOffset.addListener(
(obs, oldOffset, newOffset) -> rotateVertical(oldOffset.intValue() - newOffset.intValue()));
// Simple example just has a fixed size pane:
pane.setMinSize(PANE_WIDTH, PANE_HEIGHT);
pane.setPrefSize(PANE_WIDTH, PANE_HEIGHT);
pane.setMaxSize(PANE_WIDTH, PANE_HEIGHT);
// enable panning on pane (just update offsets when dragging):
pane.setOnMousePressed(e -> {
mouseAnchorX = e.getSceneX();
mouseAnchorY = e.getSceneY();
});
pane.setOnMouseDragged(e -> {
double deltaX = e.getSceneX() - mouseAnchorX;
double deltaY = e.getSceneY() - mouseAnchorY;
xOffset.set(xOffset.get() + deltaX);
yOffset.set(yOffset.get() + deltaY);
mouseAnchorX = e.getSceneX();
mouseAnchorY = e.getSceneY();
});
// display in stage:
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
private void build() {
// create array of image views:
int numTileCols = (int) (PANE_WIDTH / TILE_WIDTH + 2);
int numTileRows = (int) (PANE_HEIGHT / TILE_HEIGHT + 2);
tiles = new ImageView[numTileCols][numTileRows];
// populate array:
for (int colIndex = 0; colIndex < numTileCols; colIndex++) {
final int col = colIndex;
for (int rowIndex = 0; rowIndex < numTileRows; rowIndex++) {
final int row = rowIndex;
// create actual image view and initialize image:
ImageView tile = new ImageView();
tile.setImage(getImage(col - tileXOffset.get(), row - tileYOffset.get()));
tile.setFitWidth(TILE_WIDTH);
tile.setFitHeight(TILE_HEIGHT);
// position image by offset, and register listeners to keep it updated:
xOffset.addListener((obs, oldOffset, newOffset) -> {
double offset = newOffset.intValue() % TILE_WIDTH + (col - 1) * TILE_WIDTH;
tile.setLayoutX(offset);
});
tile.setLayoutX(xOffset.intValue() % TILE_WIDTH + (col - 1) * TILE_WIDTH);
yOffset.addListener((obs, oldOffset, newOffset) -> {
double offset = newOffset.intValue() % TILE_HEIGHT + (row - 1) * TILE_HEIGHT;
tile.setLayoutY(offset);
});
tile.setLayoutY(yOffset.intValue() % TILE_HEIGHT + (row - 1) * TILE_HEIGHT);
// add image view to pane:
pane.getChildren().add(tile);
// store image view in array:
tiles[col][row] = tile;
}
}
}
// tiles have been shifted off-screen in vertical direction
// need to reallocate images to image views, and get new images
// for tiles that have moved into view:
// delta represents the number of tiles we have shifted, positive for up
private void rotateVertical(int delta) {
for (int colIndex = 0; colIndex < tiles.length; colIndex++) {
if (delta > 0) {
// top delta rows have shifted off-screen
// shift top row images by delta
// add new images to bottom rows:
for (int rowIndex = 0; rowIndex + delta < tiles[colIndex].length; rowIndex++) {
// stop any background loading we no longer need
if (rowIndex < delta) {
Image current = tiles[colIndex][rowIndex].getImage();
if (current != null) {
current.cancel();
}
}
// move image up from lower rows:
tiles[colIndex][rowIndex].setImage(tiles[colIndex][rowIndex + delta].getImage());
}
// fill lower rows with new images:
for (int rowIndex = tiles[colIndex].length - delta; rowIndex < tiles[colIndex].length; rowIndex++) {
tiles[colIndex][rowIndex].setImage(getImage(-tileXOffset.get() + colIndex, -tileYOffset.get() + rowIndex));
}
}
if (delta < 0) {
// similar to previous case...
}
}
}
// similarly, rotate images horizontally:
private void rotateHorizontal(int delta) {
// similar to rotateVertical....
}
// get a new image for tile represented by column, row
// this implementation just snapshots a label, but this could be
// retrieved from a file, server, or database, etc
private Image getImage(int column, int row) {
Label label = new Label(String.format("Tile [%d,%d]", column, row));
label.setPrefSize(TILE_WIDTH, TILE_HEIGHT);
label.setMaxSize(TILE_WIDTH, TILE_HEIGHT);
label.setAlignment(Pos.CENTER);
label.setBackground(new Background(new BackgroundFill(randomColor(), CornerRadii.EMPTY , Insets.EMPTY)));
// must add label to a scene for background to work:
new Scene(label);
return label.snapshot(null, null);
}
private Color randomColor() {
return Color.rgb(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256));
}
public static void main(String[] args) {
launch(args);
}
}
Complete code (with thread handling) here, complete version without threading in a previous revision
There is obviously more functionality (and performance enhancements) that could be added here, for example you could allow for resizing the pane (update: the latest version of the gist linked above does this), and create or remove tiles when the pane changes size, etc. But this should function as a basic template for this functionality.
It is best practice to have your images loaded before they are displayed!
If you wish to get rid of the image simply set the image to null! But you will then have ro reinitialize that image in order to be able to view! I do not recommend this!
If you will reuse that image just keep it memory!
Load it once and use it on unlimited imageViews!
No, there is no such functionality in Image contract. Image can load in background, but once loaded, it cannot be unloaded.
If using ImageView, then you should assign Image to it explicitly, but JavaFX doesn't provide a way for you to know when ImageView is actually shown.
To implement required close to ImageView, I was to fork it and highly utilize deprecated API with Prism, including NGImageView class.
Hi I having been trying to program an algorithm using breadth-first-search that finds the shortest path for the blue dot to exit in the game. I am new to java and having trouble running/understanding the algorithm for the class. I have a class called gameModel which stores the statuses of each dot. The algoirthm is meant to test the fastest way the blue dot can exit the board without going through an orange dot (SELECTED) and if no way out than the player wins. I keep running the program and getting compile errors which i dont know how to fix. I included the controller class where the short dot was ran.
import java.util.Random;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.*;
/**
* The class <b>GameController</b> is the controller of the game. It implements
* the interface ActionListener to be called back when the player makes a move. It computes
* the next step of the game, and then updates model and view.
*/
public class GameController implements ActionListener {
private int size;
private GameModel gameModel;
private GameView gameView;
private boolean click;
/**
* Constructor used for initializing the controller. It creates the game's view
* and the game's model instances
*
* #param size
* the size of the board on which the game will be played
*/
public GameController(int size) {
this.size = size;
this.gameModel = new GameModel(size);
this.gameView = new GameView (gameModel, this);
click = false;
}
/**
* Starts the game
*/
public void start(){
if (click){
List start = new List {gameModel.getCurrentDot().getX(), gameModel.getCurrentDot().getY()};
List<int> targets = new ArrayList<>();
List<int> blocked = nwq ArrayList<>();
for (int i = 0; i < size; i++){
targets.add(i, 0);
targets.add(i, size);
targets.add(1, size);
targets.add(1, 0);
}
for (int i = 0; i < size; i++){
for (int j = 0; j < size; j++)
if(gameModel.getstatus(i, j) == SELECTED){
blocked.add(i, j);
}
String path = Breadth-First-Start(start, targets, blocked);
gameView = new GameView(gameModel, this);
gameView.getBoardView().update();
}
}
public Breadth-First-Start(start, targets, blocked){ // Need help with
Queue queue = new LinkedList();
queue.add(start + "");
while(!queue.isEmpty()){
String p = queue.remove();
if (p != blocked){ //If p is not in blocked paths
if (p == targets){ //If p is in targets
return "q + {p}";
} else {
queue.add("q + {p}");
blocked.add(p);
}
}
}
You method public Breadth-First-Start(start, targets, blocked) is declared wrong. You cant have - in method name, also you need to specify the return type (only constructors dont have a return type to be defined). Also you need to specify parameter types. From what I understand targets and start look like of type String and blocked looks like a List, please try replace the method head by the following public void breadthFirstSearch(String start, String targets, List blocked) not sure what return type you want as you dont have any returns in the method. But in your case you probably want the path so maybe of type List, or a boolean to know if there is a path or not.
What you're wanting to do has to do with graph theory. If two nodes are connected, an edge between them is created. In this case the orange dots would not be connected to anything as a path cannot exist through them. Dijkstra's algorithm is very useful for doing what you want, although it's breadth first instead of depth first. I'd recommend starting there, I'm sure there are examples of that algorithm being implemented in java.
The edges of the graph have weights which are compared in order to find the shortest path between two nodes.
I see that your blocked list declaration has nwq in it instead of new. That may be your issue right there.
Hope this helps