I have problem with drawing in Java. I think my code is right, but when i place it to loop with timer, it will not print anything. I want to do every second repeat the doDrawing(g) method. Now it will only prints the text prom system.out but no drawing.
package src;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.JPanel;
import javax.swing.Timer;
public class surface extends JPanel{
private void doDrawing(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
//souradnice stredu ciferniku
final int sx = 250;
final int sy = 250;
// inicializace promennych
int uhel = 0;
int delka = 150;
int xHodina,xMinuta,xSekunda,
yHodina,yMinuta,ySekunda;
// získání aktuálního času
int HOUR = Calendar.getInstance().get(Calendar.HOUR);
int MINUTE = Calendar.getInstance().get(Calendar.MINUTE) + 1;
int SECOND = Calendar.getInstance().get(Calendar.SECOND) + 1 ;
//výpočet jednotlivých úhlů pro jednotlivé ručičky a jejich vykreslení
xSekunda = (int) ((int) sx + Math.round( Math.sin(( 6 * SECOND * Math.PI / 180)) * delka));
ySekunda = (int) ((int) sy - Math.round( Math.cos(( 6 * SECOND * Math.PI / 180)) * delka));
//vyhreslení ručiček
g2d.drawLine(sx, sy, xSekunda, ySekunda);
//info
System.out.print(" "+(6 * SECOND)+ " "+ HOUR+" "+MINUTE+" "+SECOND+" "+xSekunda+" "+ySekunda+"\n");
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
int delay = 1000; //milliseconds
//Somewhere there is that problem ..
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doDrawing(g);
}
};
new Timer(delay, taskPerformer).start();
}
}
The problem is somewhere here.
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
int delay = 1000; //milliseconds
//Somewhere there is that problem ..
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doDrawing(g);
}
};
new Timer(delay, taskPerformer).start();
}
}
Never start a Timer inside of paintComponent. This method is for drawing and drawing only and nothing, I mean absolutely nothing else.
You should start your Timer elsewhere, perhaps in the class's constructor, and have it change fields of your class, and then call repaint(). The paintComponent method should then use those fields to decide what and where to paint.
For example your doDrawing() method (without the Graphics parameter) could create a Line2D, add this to a List<Line2D>, and then call repaint(). The paintComponent(...) method could then iterate through this list drawing each line:
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.Timer;
public class surface extends JPanel {
private List<Line2D> lineList = new ArrayList<>();
public surface() {
int delay = 1000; // milliseconds
// Somewhere there is that problem ..
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doDrawing();
}
};
new Timer(delay, taskPerformer).start();
}
private void doDrawing() {
// souradnice stredu ciferniku
final int sx = 250;
final int sy = 250;
// inicializace promennych
int uhel = 0;
int delka = 150;
int xHodina, xMinuta, xSekunda, yHodina, yMinuta, ySekunda;
// získání aktuálního času
int HOUR = Calendar.getInstance().get(Calendar.HOUR);
int MINUTE = Calendar.getInstance().get(Calendar.MINUTE) + 1;
int SECOND = Calendar.getInstance().get(Calendar.SECOND) + 1;
// výpočet jednotlivých úhlů pro jednotlivé ručičky a jejich vykreslení
xSekunda = (int) ((int) sx + Math.round(Math
.sin((6 * SECOND * Math.PI / 180)) * delka));
ySekunda = (int) ((int) sy - Math.round(Math
.cos((6 * SECOND * Math.PI / 180)) * delka));
Line2D line = new Line2D.Double(sx, sy, xSekunda, ySekunda);
lineList.add(line);
repaint();
// info
System.out.print(" " + (6 * SECOND) + " " + HOUR + " " + MINUTE + " "
+ SECOND + " " + xSekunda + " " + ySekunda + "\n");
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
for (Line2D line2d : lineList) {
g2d.draw(line2d);
}
}
}
Edit my bad, you only want to draw one line with this code, not a List of lines. If so, get rid of the list and instead create a Line2D field that is changed by your Timer and drawn by your paintComponent:
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Line2D.Double;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
// !! Class names should begin with upper-case letter
public class Surface extends JPanel {
private static final double DELKA = 150;
private static final int SX = 250;
private static final int SY = SX;
private static final int DELAY = 1000;
// private List<Line2D> lineList = new ArrayList<>();
private Timer timer = new Timer(DELAY, new TaskPerformer());
private Line2D line;
public Surface() {
timer.start();
}
private void doDrawing() {
int xSekunda, ySekunda;
int HOUR = Calendar.getInstance().get(Calendar.HOUR);
int MINUTE = Calendar.getInstance().get(Calendar.MINUTE) + 1;
int SECOND = Calendar.getInstance().get(Calendar.SECOND) + 1;
xSekunda = (int) ((int) SX + Math.round(Math
.sin((6 * SECOND * Math.PI / 180)) * DELKA));
ySekunda = (int) ((int) SY - Math.round(Math
.cos((6 * SECOND * Math.PI / 180)) * DELKA));
// Line2D line = new Line2D.Double(SX, SY, xSekunda, ySekunda);
line = new Line2D.Double(SX, SY, xSekunda, ySekunda);
// lineList.add(line);
repaint();
// info
System.out.print(" " + (6 * SECOND) + " " + HOUR + " " + MINUTE + " "
+ SECOND + " " + xSekunda + " " + ySekunda + "\n");
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(2 * SX, 2 * SY);
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
// to give smoother lines
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through our list and draw lines it holds
if (line != null) {
g2d.draw(line);
}
}
private class TaskPerformer implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (!isShowing() && timer != null && timer.isRunning()) {
timer.stop();
} else {
doDrawing();
}
}
}
private static void createAndShowGui() {
Surface mainPanel = new Surface();
JFrame frame = new JFrame("surface");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Related
I have a program that retrieves data, where each retrieval can not be determined its completion time. My idea would be to make a progress that keeps repeating and flagging if the capture is complete.
my current source code, method from main class:
public JComponent makeUI(boolean displayProgressBar) {
if(displayProgressBar){
jpbCircularProg.setUI(new principal.ProgressCircleUI());
jpbCircularProg.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
//Border emptyBorder = BorderFactory.createEmptyBorder();
//jpbCircularProg.setBorder(emptyBorder);
jpbCircularProg.setStringPainted(true);
jpbCircularProg.setFont(jpbCircularProg.getFont().deriveFont(24f));
jpbCircularProg.setForeground(Color.ORANGE);
if(jpbCircularProg.isVisible()==false){
jpbCircularProg.setVisible(true);
}
(new Timer(10, e -> {// percepat
System.out.println("progressbar on :");
int iv = Math.min(100, jpbCircularProg.getValue() + 1);
jpbCircularProg.setValue(iv);
if(jpbCircularProg.getValue()==100){
jpbCircularProg.setValue(1);
}
})).start();
}else{
if(jpbCircularProg.isVisible()==true){
jpbCircularProg.setVisible(false);
}
(new Timer(10, e -> {// percepat
System.out.println("progressbar on :");
int iv = Math.min(100, jpbCircularProg.getValue() + 1);
jpbCircularProg.setValue(iv);
if(jpbCircularProg.getValue()==100){
jpbCircularProg.setValue(0);
jpbCircularProg.setStringPainted(true);
jpbCircularProg.setVisible(false);
}
})).start();
}
jPanel2.setOpaque(false);
jPanel2.add(jpbCircularProg);
return jPanel2;
}
ProgressCircleUI.java:
public class ProgressCircleUI extends BasicProgressBarUI {
#Override
public Dimension getPreferredSize(JComponent c) {
Dimension d = super.getPreferredSize(c);
int v = Math.max(d.width, d.height);
d.setSize(v, v);
return d;
}
#Override public void paint(Graphics g, JComponent c) {
Insets b = progressBar.getInsets(); // area for border
int barRectWidth = progressBar.getWidth() - b.right - b.left;
int barRectHeight = progressBar.getHeight() - b.top - b.bottom;
if (barRectWidth <= 0 || barRectHeight <= 0) {
return;
}
// draw the cells
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(progressBar.getForeground());
double degree = 360 * progressBar.getPercentComplete();
double sz = Math.min(barRectWidth, barRectHeight);
double cx = b.left + barRectWidth * .5;
double cy = b.top + barRectHeight * .5;
double or = sz * .5;
double ir = or * .5; //or - 20;
Shape inner = new Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2);
Shape outer = new Arc2D.Double(
cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE);
Area area = new Area(outer);
area.subtract(new Area(inner));
g2.fill(area);
g2.dispose();
// Deal with possible text painting
if (progressBar.isStringPainted()) {
paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b);
}
}
}
Source code diatassaya get from a website then i modified a bit. I give the loop and the result is successful, but when the data retrieval more than once circular progress cycle becomes very fast and always increase faster for next data retrieval. I tried by using setIntermedinate (true) but it seems to ProgressBar not a Circular. Please help.
but when the data retrieval more than once circular progress cycle becomes very fast and always increase faster for next data retrieval
You're not stopping the timer, in fact, you seem to be using a second Timer to hide the progress bar ... for some reason.
This means, each time you want to use the progress indicator, you create ANOTHER Timer, which creates a bunch of competing Timers all changing the state and interfering with each other.
A better and simpler solution would be to create a single Timer and simply stop/start it as need, for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.plaf.basic.BasicProgressBarUI;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar jpbCircularProg;
private Timer timer;
public TestPane() {
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int iv = Math.min(100, jpbCircularProg.getValue() + 1);
jpbCircularProg.setValue(iv);
if (jpbCircularProg.getValue() == 100) {
jpbCircularProg.setValue(1);
}
}
});
jpbCircularProg = new JProgressBar();
jpbCircularProg.setUI(new ProgressCircleUI());
jpbCircularProg.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
//Border emptyBorder = BorderFactory.createEmptyBorder();
//jpbCircularProg.setBorder(emptyBorder);
jpbCircularProg.setStringPainted(true);
jpbCircularProg.setFont(jpbCircularProg.getFont().deriveFont(24f));
jpbCircularProg.setForeground(Color.ORANGE);
if (jpbCircularProg.isVisible() == false) {
jpbCircularProg.setVisible(true);
}
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = GridBagConstraints.REMAINDER;
add(jpbCircularProg, gbc);
JButton toggle = new JButton("Toggle");
toggle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timer.isRunning()) {
timer.stop();
} else {
timer.restart();
}
}
});
add(toggle, gbc);
}
}
public class ProgressCircleUI extends BasicProgressBarUI {
#Override
public Dimension getPreferredSize(JComponent c) {
Dimension d = super.getPreferredSize(c);
int v = Math.max(d.width, d.height);
d.setSize(v, v);
return d;
}
#Override
public void paint(Graphics g, JComponent c) {
Insets b = progressBar.getInsets(); // area for border
int barRectWidth = progressBar.getWidth() - b.right - b.left;
int barRectHeight = progressBar.getHeight() - b.top - b.bottom;
if (barRectWidth <= 0 || barRectHeight <= 0) {
return;
}
// draw the cells
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(progressBar.getForeground());
double degree = 360 * progressBar.getPercentComplete();
double sz = Math.min(barRectWidth, barRectHeight);
double cx = b.left + barRectWidth * .5;
double cy = b.top + barRectHeight * .5;
double or = sz * .5;
double ir = or * .5; //or - 20;
Shape inner = new Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2);
Shape outer = new Arc2D.Double(
cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE);
Area area = new Area(outer);
area.subtract(new Area(inner));
g2.fill(area);
g2.dispose();
// Deal with possible text painting
if (progressBar.isStringPainted()) {
paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b);
}
}
}
}
Since you're trying to display this as a "intermediate" progress bar, it would be better to wrap up the animation functionality into the UI delegate itself.
The following example makes use of the "intermediate" state of the JProgressBar to self animate the progress, managing the Timer state itself
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.beans.PropertyChangeEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.plaf.basic.BasicProgressBarUI;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JProgressBar jpbCircularProg;
public TestPane() {
jpbCircularProg = new JProgressBar();
jpbCircularProg.setUI(new ProgressCircleUI());
jpbCircularProg.setIndeterminate(true);
jpbCircularProg.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
//Border emptyBorder = BorderFactory.createEmptyBorder();
//jpbCircularProg.setBorder(emptyBorder);
jpbCircularProg.setStringPainted(true);
jpbCircularProg.setFont(jpbCircularProg.getFont().deriveFont(24f));
jpbCircularProg.setForeground(Color.ORANGE);
if (jpbCircularProg.isVisible() == false) {
jpbCircularProg.setVisible(true);
}
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = GridBagConstraints.REMAINDER;
add(jpbCircularProg, gbc);
JButton toggle = new JButton("Toggle");
toggle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jpbCircularProg.setIndeterminate(!jpbCircularProg.isIndeterminate());
}
});
add(toggle, gbc);
}
}
public class ProgressCircleUI extends BasicProgressBarUI {
private Timer timer;
private Handler handler = new Handler();
#Override
public void installUI(JComponent c) {
initTimer();
super.installUI(c);
}
protected void initTimer() {
if (timer == null) {
timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int iv = Math.min(100, progressBar.getValue() + 1);
progressBar.setValue(iv);
if (progressBar.getValue() == 100) {
progressBar.setValue(1);
}
progressBar.repaint();
}
});
}
}
#Override
protected void startAnimationTimer() {
timer.restart();
}
#Override
protected void stopAnimationTimer() {
timer.stop();
}
private void initIndeterminateValues() {
initTimer();
// we only bother installing the HierarchyChangeListener if we
// are indeterminate
progressBar.addHierarchyListener(handler);
// start the animation thread if necessary
if (progressBar.isDisplayable()) {
startAnimationTimer();
}
}
/**
* Invoked by PropertyChangeHandler.
*/
private void cleanUpIndeterminateValues() {
// stop the animation thread if necessary
if (progressBar.isDisplayable()) {
stopAnimationTimer();
}
progressBar.setValue(0);
progressBar.removeHierarchyListener(handler);
}
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if ("indeterminate" == prop) {
if (progressBar.isIndeterminate()) {
initIndeterminateValues();
} else {
//clean up
cleanUpIndeterminateValues();
}
progressBar.repaint();
}
}
#Override
public Dimension getPreferredSize(JComponent c) {
Dimension d = super.getPreferredSize(c);
int v = Math.max(d.width, d.height);
d.setSize(v, v);
return d;
}
#Override
public void paint(Graphics g, JComponent c) {
Insets b = progressBar.getInsets(); // area for border
int barRectWidth = progressBar.getWidth() - b.right - b.left;
int barRectHeight = progressBar.getHeight() - b.top - b.bottom;
if (barRectWidth <= 0 || barRectHeight <= 0) {
return;
}
// draw the cells
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(progressBar.getForeground());
double degree = 360 * progressBar.getPercentComplete();
double sz = Math.min(barRectWidth, barRectHeight);
double cx = b.left + barRectWidth * .5;
double cy = b.top + barRectHeight * .5;
double or = sz * .5;
double ir = or * .5; //or - 20;
Shape inner = new Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2);
Shape outer = new Arc2D.Double(
cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE);
Area area = new Area(outer);
area.subtract(new Area(inner));
g2.fill(area);
g2.dispose();
// Deal with possible text painting
if (progressBar.isStringPainted() && !progressBar.isIndeterminate()) {
paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b);
}
}
protected class Handler implements HierarchyListener {
public void hierarchyChanged(HierarchyEvent he) {
if ((he.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
if (progressBar.isIndeterminate()) {
if (progressBar.isDisplayable()) {
startAnimationTimer();
} else {
stopAnimationTimer();
}
}
}
}
}
}
}
I am creating simple animation of ball moving from one side of the screen to the other with different speed. The problem is that with higher speeds of the ball I can see noticeable flickering of the ball, actually it is hard to explain but something like I could see repaints when part of ball is still in previous step.
I have tried number of things including:
native swing animation using first thread/sleep/repain, then moved to timers
switched to javafx canvas/pane inside swing jframe. Tried both transitions and AnimationTimer
tinkering with CreateBufferStrategy, for 1,2,3 - to be honest haven't seen any difference (maybe I was doing something wrong...)
My question how can I improve smoothness and whether what I want to achieve is possible with native java or maybe it is better to use some external libraries ? and if so could you recommend something ?
below shown my example code for 2nd/3rd attempt.
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
import javax.swing.JFrame;
public class FXTrackerPanel extends JFrame {
/**
*
*/
private static final long serialVersionUID = 1L;
public int crSize = 30;
public double xPos = crSize;
public double yPos = 100;
public int xSize = 100;
public int ySize = 100;
public Circle r;
int dir = 1;
public void updateScreenSize() {
int screen = 0;
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
if( screen > -1 && screen < gs.length )
{
xSize = gs[screen].getDisplayMode().getWidth();
ySize = gs[screen].getDisplayMode().getHeight();
}
else if( gs.length > 0 )
{
xSize = gs[0].getDisplayMode().getWidth();
ySize = gs[0].getDisplayMode().getHeight();
}
else
{
throw new RuntimeException( "No Screens Found" );
}
yPos = ySize / 2;
}
private void initFXPanel(JFXPanel fxPanel) {
updateScreenSize();
xPos = crSize;
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
r = new javafx.scene.shape.Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
// new AnimationTimer() {
//
// #Override
// public void handle(long now) {
// double speed = 20;
// try {
// speed = Double.valueOf(TETSimple.mp.speedSinus.getText());
// }
// catch (Exception ex) {
// speed = 20;
// }
// double xMov = (speed * 4 * Math.sin( xPos * Math.PI / xSize ) );
// if (xMov <= 0) {
// xMov = 1;
// }
// if (dir == 1) {
// if (xPos >= xSize - crSize)
// dir = 0;
// xPos += xMov;
// } else {
// if (xPos <= 1)
// dir = 1;
// xPos -= xMov;
// }
//
// r.setTranslateX(xPos);
// }
// }.start();
fxPanel.setScene(new Scene(root));
}
public FXTrackerPanel() {
updateScreenSize();
this.setSize(new Dimension(xSize, ySize));
this.setPreferredSize(new Dimension(xSize, ySize));
this.setVisible(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JFXPanel fxPanel = new JFXPanel();
this.add(fxPanel);
this.createBufferStrategy(3);
Platform.runLater(new Runnable() {
#Override
public void run() {
initFXPanel(fxPanel);
}
});
}
public static void main(String[] args)
{
new FXTrackerPanel();
}
}
And here example for swing code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.Timer;
import java.lang.Math;
import java.util.Random;
public class TrackerPanel extends JPanel implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
Shape cr;
Color c;
public int crSize = 30;
public double xPos = crSize;
public double yPos = 100;
public double xPosPrev = crSize;
public double yPosPrev = 100;
public int xSize = 100;
public int ySize = 100;
int dir = 1; // left
int timer = 50; // 50 - sins, 1500 - linear
int method = 1; // 1 - jump, 2 - sinus
int timeToChange = 1000;
int passes = 0;
Timer tt;
boolean clickedClose = false;
private int repeats = 0;
// t - timer interval, m - method of ball movement: 1 - jump, 2 - sinus
public TrackerPanel(int t, int m) {
this.setPreferredSize(new Dimension(300, 200));
this.timer = t;
this.method = m;
c = Color.red;
repaint();
this.updateScreenSize();
tt = new Timer(t, null);
tt.addActionListener(this);
tt.start();
}
public void updateScreenSize() {
int screen = TETSimple.suppMonitor;
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
if (screen > -1 && screen < gs.length) {
xSize = gs[screen].getDisplayMode().getWidth();
ySize = gs[screen].getDisplayMode().getHeight();
} else if (gs.length > 0) {
xSize = gs[0].getDisplayMode().getWidth();
ySize = gs[0].getDisplayMode().getHeight();
} else {
throw new RuntimeException("No Screens Found");
}
yPos = ySize / 2;
yPosPrev = ySize / 2;
}
public void actionPerformed(ActionEvent arg0) {
if (method == 1)
lineMovement();
else
sinusMovement();
repaint(0, ySize / 2, xSize, crSize);
}
private Double parseText2Int(String literal) {
try {
return Double.valueOf(literal);
} catch (Exception ex) {
ex.printStackTrace();
}
return 10.0;
}
private void checkFinishCondition() {
if (passes + 1 > repeats && repeats != 0) {
if (!clickedClose) {
TETSimple.mp.bStop.doClick();
clickedClose = true;
}
return;
}
}
private void sinusMovement() {
this.updateScreenSize();
this.repeats = parseText2Int(TETSimple.mp.repeatsCount.getText()).intValue();
checkFinishCondition();
double speed = parseText2Int(TETSimple.mp.speedSinus.getText());
double xMov = (speed * Math.sin(xPos * Math.PI / xSize));
if (xMov <= 0) {
xMov = 1;
}
if (dir == 1) {
if (xPos >= xSize - crSize)
dir = 0;
xPosPrev = xPos;
xPos += xMov;
} else {
if (xPos <= 1 + crSize) {
dir = 1;
passes++;
}
xPosPrev = xPos;
xPos -= xMov;
}
}
private void lineMovement() {
this.repeats = parseText2Int(TETSimple.mp.repeatsCount.getText()).intValue();
checkFinishCondition();
double left = crSize;
double center = xSize / 2 - crSize * 1.5;
double right = xSize - crSize * 2;
Random r = new Random();
if (timeToChange <= 0) {
passes++;
if (xPos == left || xPos == right) {
timeToChange = 300 + r.nextInt(12) * 100;
xPos = center;
} else if (xPos == center) {
timeToChange = 300 + r.nextInt(7) * 100;
if (r.nextBoolean())
xPos = left;
else
xPos = right;
}
} else {
timeToChange -= 100;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setColor(Color.green);
g2d.fill(new Ellipse2D.Double(xPos, yPos, crSize, crSize));
g2d.dispose();
}
}
It's difficult to know exactly what might be going wrong without a runnable example, but I would, where you can, avoid mixing JavaFX and Swing, as they have different rendering mechanisms.
The following is a VERY simple example, which simply increases the speed of the balls been animated by simply changing the amount by which they are moved on each update...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class BouncyBall {
public static void main(String[] args) {
new BouncyBall();
}
public BouncyBall() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ControlPane extends JPanel {
private JSlider speed;
private JSlider quanity;
private BallPitPane ballPitPane;
public ControlPane() {
setLayout(new BorderLayout());
ballPitPane = new BallPitPane();
add(ballPitPane);
JPanel controls = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
speed = new JSlider(1, 100, 4);
quanity = new JSlider(1, 100, 1);
controls.add(new JLabel("Speed:"), gbc);
gbc.gridy++;
controls.add(new JLabel("Quanity:"), gbc);
gbc.gridx++;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
controls.add(speed, gbc);
gbc.gridy++;
controls.add(quanity, gbc);
add(controls, BorderLayout.SOUTH);
speed.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ballPitPane.setSpeed(speed.getValue());
}
});
quanity.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
ballPitPane.setQuanity(quanity.getValue());
}
});
}
}
public class BallPitPane extends JPanel {
private List<Ball> balls;
private int speed;
public BallPitPane() {
balls = new ArrayList<>(25);
setSpeed(2);
setQuanity(1);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.update(getWidth(), speed);
}
repaint();
}
});
timer.start();
}
public void setSpeed(int speed) {
this.speed = speed;
}
public void setQuanity(int quanity) {
while (balls.size() > quanity) {
balls.remove(0);
}
while (balls.size() < quanity) {
int radius = 4 + (int) (Math.random() * 48);
Ball ball = new Ball(
randomColor(),
(int) Math.abs(Math.random() * getWidth() - radius),
(int) Math.abs(Math.random() * getHeight() - radius),
radius
);
balls.add(ball);
}
}
protected Color randomColor() {
int red = (int) Math.abs(Math.random() * 255);
int green = (int) Math.abs(Math.random() * 255);
int blue = (int) Math.abs(Math.random() * 255);
return new Color(red, green, blue);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
for (Ball ball : balls) {
ball.paint(g2d);
}
g2d.dispose();
}
public class Ball {
private Color color;
private int x;
private int y;
private int radius;
private int delta;
public Ball(Color color, int x, int y, int radius) {
this.color = color;
this.x = x;
this.y = y;
this.radius = radius;
delta = Math.random() > 0.5 ? 1 : -1;
}
public void update(int width, int speed) {
x += speed * delta;
if (x + radius >= width) {
x = width - radius;
delta *= -1;
} else if (x < 0) {
x = 0;
delta *= -1;
}
}
public void paint(Graphics g) {
g.setColor(color);
g.fillOval(x, y, radius, radius);
}
}
}
}
This example works within the confines of Swing's painting process, if you need more control over the painting process you will need to use a BufferStrategy (to work within Swing)
I think the issue is caused by the rendering of the JFXPanel in AWT: there is some complex stuff happening behind the scenes to synchronize between the two different system threads (the AWT event dispatch thread and the FX Application Thread).
If you can write this as a "pure" JavaFX application (i.e. with no Swing/AWT code), it runs more smoothly:
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
public class FXAnimationTest extends Application {
#Override
public void start(Stage primaryStage) {
Group root = new Group();
double speed = 5;
int repeats = Timeline.INDEFINITE;
Screen screen = Screen.getPrimary();
Rectangle2D screenBounds = screen.getBounds();
double xSize = screenBounds.getWidth();
double ySize = screenBounds.getHeight();
double crSize = 30 ;
double xPos = crSize ;
double yPos = ySize / 2 ;
Circle r = new Circle(xPos, yPos, crSize / 2, Color.RED);
TranslateTransition tt = new TranslateTransition(Duration.seconds(speed), r);
tt.setFromX(xPos);
tt.setToX(xSize - crSize * 3);
tt.setCycleCount(repeats);
tt.setAutoReverse(true);
tt.setInterpolator(Interpolator.EASE_BOTH);
tt.play();
root.getChildren().add(r);
Scene scene = new Scene(root, xSize, ySize);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you have to have JavaFX embedded in a Swing application, and you're using Java 8 (I tested this on 8u20), then there is a system property that runs both UI toolkits on the same thread. I think this is still currently experimental, so use at your own risk, but try
java -Djavafx.embed.singleThread=true FXTrackerPanel
This improves things a bit, but it's still pretty flickery and not as good as the "pure JavaFX" version.
So I've got this challenge to make one of those spirograph drawrings in an applet. The problem is, I'm new to Java and have absolutely no idea how to get any components appearing on the screen. I'm hoping someone can just add an extra line of code to fix this, though I'm really grateful for any answers :)
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.awt.Toolkit;
public class SpaceCadets3_WB extends JApplet {
int numOfPoints = 1080;
int[] x = new int[numOfPoints];
int[] y = new int[numOfPoints];
int x1, x2, y1, y2, width, height, animationSleep = 0;
int R = 75;
int r = 10;
int O = 75;
JTextField changeNumOfPoints = new JTextField(15);
public SpaceCadets3_WB() {
}
public void start() {
this.setSize(600, 600);
this.setBackground(new Color(100,100,255));
this.getContentPane().setLayout(null);
this.getContentPane().add(changeNumOfPoints);
changeNumOfPoints.setVisible(true);
changeNumOfPoints.setLocation(width - changeNumOfPoints.getSize().width - 25, 25);
}
public void calculatePoints(){
width = SpaceCadets3_WB.this.getWidth();
height = SpaceCadets3_WB.this.getHeight();
for(int t = 0; t<numOfPoints; t++){
x[t] = (int) ((R+r)*Math.cos(t) - (r+O)*Math.cos(((R+r)/r)*t) + width/2);
y[t] = (int) ((R+r)*Math.sin(t) - (r+O)*Math.sin(((R+r)/r)*t) + height/2);
}
}
public void paint(Graphics g){
g.setColor(Color.RED);
calculatePoints();
for(int i = 0; i<numOfPoints;i++){
x1 = x[i];
x2 = x[i+1];
y1 = y[i];
y2 = y[i+1];
g.drawLine(x1, y1, x2, y2);
try {
Thread.sleep(animationSleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Your sleeping the paint method, something you should never do. It's bad to call Thread.sleep(...) on the Swing event thread, but it's a sin to do it in any painting method. So now the applet can't draw. Remember that any painting method must do nothing but painting, and must do it blazingly fast. It shouldn't do any program logic, it shouldn't directly do animation, it should just paint. Use a Swing timer as any similar question will show you how to do.
Also, never draw directly in the applet but instead in the paintComponent method of a JPanel that the applet holds.
So create your JPanel, override its paintComponent method, and draw using fields of the class. Then change the state of those fields in your Timer and call repaint() on the JPanel.
Edit
And yes, you should avoid using null layouts as that's preventing you from easily and adequately adding components to your GUI. Instead in this situation, a FlowLayout, the default layout for JPanel, would work great.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpaceCadets extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
SpaceCadetsPanel panel = new SpaceCadetsPanel();
getContentPane().add(panel);
setSize(SpaceCadetsPanel.PREF_W, SpaceCadetsPanel.PREF_H);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("serial")
class SpaceCadetsPanel extends JPanel {
public static final int PREF_W = 600;
public static final int PREF_H = 600;
public final static int TOTAL_POINTS = 1080;
private static final int R = 75;
private static final int R2 = 10;
private static final int O = 75;
private static final Color DRAW_COLOR = Color.RED;
private static final int ANIMATION_DELAY = 20;
private Point[] pts;
private JSpinner pointCountSpinner = new JSpinner(new SpinnerNumberModel(
800, 100, 1080, 1));
private JButton doItButton = new JButton(new DoItBtnAction("Do It!"));
private BufferedImage bufImg = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
private Timer timer;
public int imageIndex;
private CalcWorker calcWorker;
public Graphics2D g2;
public SpaceCadetsPanel() {
System.out.println(pointCountSpinner.getEditor().getClass());
add(pointCountSpinner);
add(doItButton);
setBackground(Color.white);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bufImg != null) {
g.drawImage(bufImg, 0, 0, this);
}
}
class DoItBtnAction extends AbstractAction {
public DoItBtnAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
imageIndex = 0;
if (timer != null && timer.isRunning()) {
timer.stop();
}
if (g2 != null) {
g2.dispose();
}
pts = new Point[0];
bufImg = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
g2 = bufImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(DRAW_COLOR);
int totalPoints = 0;
totalPoints = ((Integer) pointCountSpinner.getValue()).intValue();
timer = new Timer(ANIMATION_DELAY, new TimerListener(totalPoints));
calcWorker = new CalcWorker(totalPoints);
calcWorker.addPropertyChangeListener(new CalcWorkerListener());
calcWorker.execute();
}
}
class CalcWorker extends SwingWorker<Point[], Void> {
private int totalPoints;
public CalcWorker(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override
protected Point[] doInBackground() throws Exception {
Point[] pts2 = new Point[totalPoints];
for (int i = 0; i < pts2.length; i++) {
int x = (int) ((R + R2) * Math.cos(i) - (R2 + O)
* Math.cos(((R + R2) / R2) * i) + PREF_W / 2);
int y = (int) ((R + R2) * Math.sin(i) - (R2 + O)
* Math.sin(((R + R2) / R2) * i) + PREF_H / 2);
pts2[i] = new Point(x, y);
}
return pts2;
}
}
class CalcWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
try {
pts = calcWorker.get();
timer.start();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
class TimerListener implements ActionListener {
private int totalPoints;
public TimerListener(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override
public void actionPerformed(ActionEvent e) {
int x1 = pts[imageIndex].x;
int y1 = pts[imageIndex].y;
int x2 = pts[imageIndex + 1].x;
int y2 = pts[imageIndex + 1].y;
g2.drawLine(x1, y1, x2, y2);
repaint();
imageIndex++;
if (imageIndex == totalPoints - 1) {
((Timer) e.getSource()).stop();
}
}
}
}
Which displays as,
I am making a button where when you click on it it changes the text. But when I click on the button and change the text, the button does not change size according to the text. Instead, it gets smaller and attempts to make that "..." thing when it does not have enough room. Here is my code:
Text.java
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.add(panel);
final CButton button = new CButton("");
panel.add(button);
button.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent arg0) {
button.setText(button.getText()+"f");
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
});
frame.setSize(500,500);
frame.setVisible(true);
}
}
CButton.java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JOptionPane;
public class CButton extends JButton implements ComponentListener, KeyListener {
protected static final int BORDER_WIDTH = 5;
private static final Font font = new Font("Arial", Font.PLAIN, 30);
private static final Insets INSETS_MARGIN = new Insets(2, 5, 2, 5);
private static final long serialVersionUID = 1L;
protected Area m_areaDraw = null;
private Area m_areaFill = null;
private double m_dHeightDraw = 0d;
private double m_dHeightFill = 0d;
private double m_dWidthDraw = 0d;
private double m_dWidthFill = 0d;
private int m_nMinHeight = 0;
private int m_nMinWidth = 0;
private int m_nStringHeightMax = 0;
private int m_nStringWidthMax = 0;
private RoundRectangle2D m_rrect2dDraw = null;
private RoundRectangle2D m_rrect2dFill = null;
private Shape m_shape = null;
public CButton(String strLabel) {
super(strLabel);
setContentAreaFilled(false);
setMargin(CButton.INSETS_MARGIN);
setFocusPainted(false);
addComponentListener(this);
addKeyListener(this);
// Determine the buttons initial size
setFont(CButton.font);
Frame frame = JOptionPane.getRootFrame();
FontMetrics fm = frame.getFontMetrics(getFont());
m_nStringWidthMax = fm.stringWidth(getText());
m_nStringWidthMax = Math.max(m_nStringWidthMax,
fm.stringWidth(getText()));
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getMargin().left
+ this.getInsets().left + getMargin().right
+ this.getInsets().right);
m_nStringHeightMax = fm.getHeight();
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nHeight = Math.max(m_nMinHeight, m_nStringHeightMax
+ getMargin().left + this.getInsets().left + getMargin().right
+ this.getInsets().right);
setPreferredSize(new Dimension(
nWidth + ((2 * getFont().getSize()) / 5), nHeight
+ ((2 * getFont().getSize()) / 5)));
// Set the initial draw and fill dimensions
setShape();
}
#Override
public void componentHidden(ComponentEvent e) {
}
#Override
public void componentMoved(ComponentEvent e) {
}
// Needed if we want this button to resize
#Override
public void componentResized(ComponentEvent e) {
m_shape = new Rectangle2D.Float(0, 0, getBounds().width,
getBounds().height);
m_dWidthFill = (double) getBounds().width - 1;
m_dHeightFill = (double) getBounds().height - 1;
m_dWidthDraw = ((double) getBounds().width - 1)
- (CButton.BORDER_WIDTH - 1);
m_dHeightDraw = ((double) getBounds().height - 1)
- (CButton.BORDER_WIDTH - 1);
setShape();
repaint();
}
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public boolean contains(int nX, int nY) {
if ((null == m_shape) || m_shape.getBounds().equals(getBounds())) {
m_shape = new Rectangle2D.Float(0, 0, this.getBounds().width,
this.getBounds().height);
}
return m_shape.contains(nX, nY);
}
// This is so the button is triggered when it has focus
// and we press the Enter key.
#Override
public void keyPressed(KeyEvent e) {
if ((e.getSource() == this) && (e.getKeyCode() == KeyEvent.VK_ENTER)) {
doClick();
}
};
#Override
public void keyReleased(KeyEvent e) {
};
#Override
public void keyTyped(KeyEvent e) {
};
#Override
public void paintBorder(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
g2.setColor(Color.black);
Stroke strokeOld = g2.getStroke();
g2.setStroke(new BasicStroke(CButton.BORDER_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(m_areaDraw);
if (getModel().isRollover()) {
g2.setColor(Color.GRAY);
g2.draw(m_areaDraw);
}
g2.setStroke(strokeOld);
};
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
if (getModel().isArmed()) {
g2.setColor(Color.CYAN.darker());
} else {
g2.setColor(Color.CYAN);
}
g2.fill(m_areaFill);
super.paintComponent(g2);
}
private void setShape() {
// Area
double dArcLengthFill = Math.min(m_dWidthFill, m_dHeightFill);
m_rrect2dFill = new RoundRectangle2D.Double(0d, 0d, m_dWidthFill,
m_dHeightFill, dArcLengthFill, dArcLengthFill);
// WARNING: arclength and archeight are divided by 2
// when they get into the roundedrectangle shape
m_areaFill = new Area(m_rrect2dFill);
// Border
double dArcLengthDraw = Math.min(m_dWidthDraw, m_dHeightDraw);
m_rrect2dDraw = new RoundRectangle2D.Double(
(CButton.BORDER_WIDTH - 1) / 2, (CButton.BORDER_WIDTH - 1) / 2,
m_dWidthDraw, m_dHeightDraw, dArcLengthDraw, dArcLengthDraw);
m_areaDraw = new Area(m_rrect2dDraw);
}
#Override
public void setText(String strText) {
super.setText(strText);
int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getInsets().left
+ getInsets().right);
int nHeight = Math.max(0, getPreferredSize().height);
setPreferredSize(new Dimension(nWidth, nHeight));
m_dWidthFill = getBounds().width - 1;
m_dHeightFill = getBounds().height - 1;
if ((m_dWidthFill <= 0) || (m_dHeightFill <= 0)) {
m_dWidthFill = (double) getPreferredSize().width - 1;
m_dHeightFill = (double) getPreferredSize().height - 1;
}
m_dWidthDraw = m_dWidthFill - (CButton.BORDER_WIDTH - 1);
m_dHeightDraw = m_dHeightFill - (CButton.BORDER_WIDTH - 1);
setShape();
}
}
You call
setPreferredSize(new Dimension(
nWidth + ((2 * getFont().getSize()) / 5), nHeight
+ ((2 * getFont().getSize()) / 5)));
in the constructor, but never change it. This means that each time the layout manager asks the component what size it would like to be, it always gets the same value.
A preferred solution would be to override getPreferredSize and calculate the size there, if it's not to complicated or time consuming
The next question is why are you going to all this extra effort. Basically, you should simply allow the parent JButton to provide its preferred size and add your requirements around it, or simply use the margins properties or even a Border
You're KeyListener also seems useless as this is the default behaviour of the button anyway and in any case, key bindings would be a preferred solution
My first thought was that it had to do with your humongous CButton implementation. At least, copy/paste of your code created a working application. That's a big plus in my book.
When I simply replaced the strange nWidth calculation in the setText() method with:
int nWidth = 100;
The size was increased after a click and showed an "f", which I assume is what you wanted.
So, I can't really say how to calculate the width, but at least you know where to look and which line to change.
I have a JButton that when I call setText(), the text does not update. But as soon as I hover over the button, it updates. How can I fix this so that when I call setText(), the button updates? I will just post the methods that the EDT takes to get to it. It goes in order :)
public void mouseClicked(MouseEvent e) {//This is a differn't event. This is not executed by clicking on the same button. It's a differn't one.
click(new Point(e.getX() + gridScrollPaneViewport.getViewPosition().x, e.getY() + gridScrollPaneViewport.getViewPosition().y));
}
public void click(Point point) {
map.selectPlot((PlotGUI) map.getComponentAt(point));
}
public void selectPlot(PlotGUI plot) {
ChickenTycoon.getInstance().getScreen().getPlayingScreen().sideBar
.setPlot(plot);
selectedPlot = plot;
}
public void setPlot(PlotGUI plot) {
title.setText(plot.getName() + plot.getX() + plot.getY());
for (Component comp : stuffPanel.getComponents()) {
stuffPanel.remove(comp);
}
for (Component comp : plot.getComponentList()) {
stuffPanel.add(comp);
}
update();
}
public void update() {
if (ChickenTycoon.getInstance().getScreen().getPlayingScreen().map
.getSelectedPlot() != null) {
ChickenTycoon.getInstance().getScreen().getPlayingScreen().map
.getSelectedPlot().update();
}
}
public void update() {
Log.m("Updating LandGUI");
((CButton) componentList[0]).setText("Buy for $ "//I know I am casting this to a CButton, it's my custom version of JButton. I tried it with a JButton and the same thing happens.
+ ((Land) parentPlot).getPurchasePrice());
}
CButton.java
package gui;
import game.GameConfiguration;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import chicken.GlobalConfiguration;
public class CButton extends JButton implements ComponentListener {
protected static final int BORDER_WIDTH = 5;
private static final Font font = GlobalConfiguration.font;
private static final Insets INSETS_MARGIN = new Insets(2, 5, 2, 5);
private static final long serialVersionUID = 1L;
protected Area m_areaDraw = null;
private Area m_areaFill = null;
private double m_dHeightDraw = 0d;
private double m_dHeightFill = 0d;
private double m_dWidthDraw = 0d;
private double m_dWidthFill = 0d;
private int m_nMinHeight = 0;
private int m_nMinWidth = 0;
private int m_nStringHeightMax = 0;
private int m_nStringWidthMax = 0;
private RoundRectangle2D m_rrect2dDraw = null;
private RoundRectangle2D m_rrect2dFill = null;
private Shape m_shape = null;
public CButton(String strLabel) {
setContentAreaFilled(false);
setMargin(CButton.INSETS_MARGIN);
setFocusPainted(false);
addComponentListener(this);
setFont(CButton.font);
setText(strLabel);
}
#Override
public void componentHidden(ComponentEvent e) {
}
#Override
public void componentMoved(ComponentEvent e) {
}
// Needed if we want this button to resize
#Override
public void componentResized(ComponentEvent e) {
m_shape = new Rectangle2D.Float(0, 0, getBounds().width,
getBounds().height);
m_dWidthFill = (double) getBounds().width - 1;
m_dHeightFill = (double) getBounds().height - 1;
m_dWidthDraw = ((double) getBounds().width - 1)
- (CButton.BORDER_WIDTH - 1);
m_dHeightDraw = ((double) getBounds().height - 1)
- (CButton.BORDER_WIDTH - 1);
setShape();
repaint();
}
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public boolean contains(int nX, int nY) {
if ((null == m_shape) || m_shape.getBounds().equals(getBounds())) {
m_shape = new Rectangle2D.Float(0, 0, this.getBounds().width,
this.getBounds().height);
}
return m_shape.contains(nX, nY);
}
#Override
public void paintBorder(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
g2.setColor(Color.black);
Stroke strokeOld = g2.getStroke();
g2.setStroke(new BasicStroke(CButton.BORDER_WIDTH,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g2.draw(m_areaDraw);
if (getModel().isRollover()) {
g2.setColor(Color.GRAY);
g2.draw(m_areaDraw);
}
g2.setStroke(strokeOld);
};
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHints(hints);
if (getModel().isArmed()) {
g2.setColor(Color.CYAN.darker());
} else {
g2.setColor(Color.CYAN);
}
g2.fill(m_areaFill);
super.paintComponent(g2);
}
private void setShape() {
// Area
double dArcLengthFill = Math.min(m_dWidthFill, m_dHeightFill);
m_rrect2dFill = new RoundRectangle2D.Double(0d, 0d, m_dWidthFill,
m_dHeightFill, dArcLengthFill, dArcLengthFill);
// WARNING: arclength and archeight are divided by 2
// when they get into the roundedrectangle shape
m_areaFill = new Area(m_rrect2dFill);
// Border
double dArcLengthDraw = Math.min(m_dWidthDraw, m_dHeightDraw);
m_rrect2dDraw = new RoundRectangle2D.Double(
(CButton.BORDER_WIDTH - 1) / 2, (CButton.BORDER_WIDTH - 1) / 2,
m_dWidthDraw, m_dHeightDraw, dArcLengthDraw, dArcLengthDraw);
m_areaDraw = new Area(m_rrect2dDraw);
}
#Override
public void setText(final String strText) {
CButton.super.setText(strText);
Frame frame = JOptionPane.getRootFrame();
FontMetrics fm = frame.getFontMetrics(font);
m_nStringWidthMax = fm.stringWidth(getText());
m_nStringWidthMax = Math.max(m_nStringWidthMax,
fm.stringWidth(getText()));
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getMargin().left
+ getInsets().left + getMargin().right + getInsets().right);
m_nStringHeightMax = fm.getHeight();
// WARNING: use getMargin. it refers to dist btwn text and border.
// Also use getInsets. it refers to the width of the border
int nHeight = Math.max(m_nMinHeight, m_nStringHeightMax
+ getMargin().left + getInsets().left + getMargin().right
+ getInsets().right);
setPreferredSize(new Dimension(
nWidth + ((2 * getFont().getSize()) / 5), nHeight
+ ((2 * getFont().getSize()) / 5)));
// Set the initial draw and fill dimensions
setShape();
}
}
I fixed it. I needed to add a repaint to the container for the button. Simple as that!
button.setText("Some Text");
buttonContainer.repaint();