编辑:我通过将动画移动到一个单独的线程来实现它。谢谢大家!
我正在自学Java,以便编写一个小小程序来演示海洋的水柱如何按密度分类。我有一个小动画,它自己包含在自己的类中,名为animationBox,我试图从我的GUI中调用它。 GUI是使用netbeans GUI创建器创建的,您只需单击并放下即可。我认为这个问题与Override paintComponent in Netbeans GUI是一个非常相似的问题,但我尝试交换到flowlayout并且问题没有解决。
GUI当前调用animationBox类,它创建自己的JFrame来运行动画。我知道动画的代码正在运行,因为我尝试了一些System.out.println()命令并且除了实际绘制动画的位之外,一切都运行得很顺利。我猜测基于我在这个网站上读到的内容,我的覆盖paintComponent没有被调用。
我的主要困惑是它在animationBox的main方法中工作,但是当我从GUI调用相同的方法时,animationBox中的main方法调用它不起作用。有关如何解决此问题的任何想法,或者我可以获取更多信息的地方?任何帮助将不胜感激!
这是我的代码,我试图用// RELEVANT标记我认为的相关部分
animationBox代码(更多相关内容):
package water_density_gui_2;
import javax.swing.*;
import java.awt.*;
public class animateBox extends JPanel {
//initial starting conditions for the moving box
int x = 200;
int y = 100;
public static void main(String[] args) {
animateBox gui = new animateBox();
gui.play(1.025); // random input density as placeholder
}
public void play(double boxDensity) {
//initialize frame to hold animation
//RELEVANT
JFrame frame = new JFrame();
JPanel panel = new JPanel();
int height = 212;
int width = 400;
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel); //this seems to do nothing, but I hoped it'd trigger the painting
DrawPanel draw = new DrawPanel(); //this is the class with overriden paintComponent
frame.getContentPane().add(draw);
frame.setVisible(true);
panel.repaint();//these work when I run animateBox, but the GUI stays white
draw.repaint();
//calculate density curve for pycnocline [not relevant to problem]
pycnoclineCalculations pycno = new pycnoclineCalculations();
double[][] pycnoclineAndDepth = pycno.summerPycnocline();
double[] pycnocline = new double[212];
double[] depth = new double[212];
for (int i = 0; i < 212; i++) {
pycnocline[i] = pycnoclineAndDepth[0][i];
depth[i] = pycnoclineAndDepth[1][i];
}//end density curve calculations
//calculate differences between box density and density curve
//also not relevant to problem
double[] deltaDensity = new double[212];
for (int i = 0; i < 212; i++) {
deltaDensity[i] = Math.abs(pycnocline[i] - boxDensity);
}
//get index of minimum difference
int depthIndex = getMinValueIndex(deltaDensity);
int targetDepth = (int) Math.round((height * depth[depthIndex] / 2000));
int deltaDepth = Math.abs(y - targetDepth);
//catch if box density is not shown on curve
if (boxDensity < 1.02346) {
targetDepth = 0;
} else if (boxDensity > 1.02852) {
targetDepth = height;
}//end box density calculations
//kinda RELEVANT towards end of loop
//most of the FOR loop is doing the calculations for where the box should go
//the loop interacts with the drawing by changing x,y & calling draw.repaint()
//initialize variables for FOR loop
int t = 0; //time variable for damping term A*e^-kt
boolean up = false; //direction box is moving
boolean reached = false; //reached destination
depthObj temp = new depthObj(y, false, false); //holds current depth, up, reached
boolean cosine = false; //part of path that requires damping
int currentTarget = targetDepth; //where the box is going to
boolean done = false; //if the animation loop should end
for (int i = 0; !done; i++) {
if (!reached) { //if not at destination
temp = incrementDepth(currentTarget, y);
y = temp.y;
reached = temp.reached;
if (!reached) {
up = temp.up;
}
} else { //if reached, calculate new destination based off of damping term
double A = deltaDepth / 8;
double k = 0.5;
int newTarget = 0;
//new destination calculations
if (Math.round(A * Math.pow(2.71828, -k * t)) != 0) {
if (up & !cosine) {
newTarget = targetDepth - (int) Math.round(A * Math.pow(2.71828, -k * t));
if (newTarget < 0) {
newTarget = 0;
}
cosine = true;
} else if (!up & !cosine) {
newTarget = targetDepth + (int) Math.round(A * Math.pow(2.71828, -k * t));
cosine = true;
} else if (up & cosine) {
newTarget = targetDepth + (int) Math.round(A * Math.pow(2.71828, -k * t));
} else if (!up & cosine) {
newTarget = targetDepth - (int) Math.round(A * Math.pow(2.71828, -k * t));
if (newTarget < 0) {
newTarget = 0;
}
}
t++; //increment time for damping
temp = incrementDepth(currentTarget, y);
currentTarget = newTarget;
y = temp.y;
reached = false;
} else {
// System.out.println("Damping constant is zero");
x++; //move box off of screen sideways
if (x > 410) { //if box is off screen, get rid of frame
frame.setVisible(false); //RELEVANT....?
frame.dispose();
done = true; //end animation
}
}
}
//DEFINITELY RELEVANT PART
draw.repaint(); //tells the panel to redraw itself so we can see the box in new location
//should trigger with every iteration of the FOR loop. Works in this code, not in GUI. The frame is created, but stays white the hold time
try {
Thread.sleep(25); //slows down animation slightly
} catch (Exception e) {
}
}
}
//increments the depth position of the box [probably not relevant]
public static depthObj incrementDepth(int target, int y) {
boolean reached = false;
boolean up;
if (y < target) {
y++;
up = false;
} else if (y > target) {
y--;
up = true;
} else {
reached = true;
up = true; //default, caught above
}
depthObj temp = new depthObj(y, up, reached);
return temp;
}
//calculates sigma-t density value from the water's temperature and salinity signatures
//probably not relevant to drawing problem
public static double calculateDensity(double salinity, double temp) {
/** rhos = density in kg/m^3 as a function of temperature and salinity
*S = salinity in g/kg
*rhos = rho + AS + BS^(3/2) + CS^2
*A = 8.24493E-1 - 4.0899E-3*T + 7.6438E-5*T^2 -8.2467E-7*T^3 + 5.3675E-9*T^4
*B = -5.724E-3 + 1.0227E-4*T - 1.6546E-6*T^2
*C = 4.8314E-4
* rho = density in kg/m^3 as a function of temperature
*T = temperature in C
*rho = 1000(1 - (T+288.9414)/(508929.2*(T+68.12963))*(T-3.9863)^2)
*/
double density = 0;
double rho = 0;
double eqn1 = 0;
double eqn2 = 0;
double eqn3 = 0;
double rhos = 0;
rho = 1000 * (1 - ((temp + 288.9419) / (508929.2 * (temp + 68.12963))) * (Math.pow((temp - 3.9863), 2)));
eqn1 = 0.824493 - temp * 0.0040899 + Math.pow(temp, 2) * Math.pow(10, -5) * 7.6438 - (Math.pow(10, -7) * 8.2467 * Math.pow(temp, 3)) + 5.3675 * Math.pow(10, -9) * Math.pow(temp, 4);
eqn2 = -5.724 * Math.pow(10, -3) + 1.0227 * Math.pow(10, -4) * temp - 1.6546 * Math.pow(10, -6) * Math.pow(temp, 2);
eqn3 = 4.8314 * Math.pow(10, -4);
rhos = rho + eqn1 * salinity + eqn2 * Math.pow(salinity, (3 / 2)) + eqn3 * Math.pow(salinity, 2);
density = rhos - 1000;
density = (double) 1 + (Math.floor(density * 100) / 100) / 1000;
return density;
}
//gets the index of the minimum value of an array
//also probably not relevant
public static int getMinValueIndex(double[] numbers) {
double minValue = numbers[0];
int minValueIndex = 0;
for (int i = 1; i < numbers.length; i++) {
if (numbers[i] < minValue) {
minValue = numbers[i];
minValueIndex = i;
}
}
return minValueIndex;
}
//DEFINITELY RELEVANT PART
//draws the animation. Overriding paintComponent works when I run this program, but not in the GUI. In the GUI the frame gets created, but stays white the whole time
class DrawPanel extends JPanel {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);//call method on...JPanel? Not exactly sure what this does
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
//get dimensions of window
int w = getWidth();
int h = getHeight();
int h1 = (int) Math.round(h * 200 / 2000); //first color change point
int h2 = (int) Math.round(h * 600 / 2000); //second color change point
//add background gradients (NOTE: color corresponds to pycnocline)
Color color1 = Color.RED;
Color color2 = Color.BLUE;
GradientPaint gp = new GradientPaint(0, 0, color1, 0, h1, color1);
GradientPaint gp2 = new GradientPaint(0, h1, color1, 0, h2, color2);
GradientPaint gp3 = new GradientPaint(0, h2, color2, 0, h, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h1);
g2d.setPaint(gp2);
g2d.fillRect(0, h1, w, h2);
g2d.setPaint(gp3);
g2d.fillRect(0, h2, w, h);
//add box
g.setColor(Color.BLACK);
g.fillRect(x - 2, y - 2, 19, 19);
g.setColor(Color.GREEN);
g.fillRect(x, y, 15, 15);
}
}
//object to hold information about depth, direction, if at destination
// probably not relevant to problem
static class depthObj {
int y = 0;
boolean up = false;
boolean reached = false;
public depthObj(int ycur, boolean direction, boolean there) {
this.y = ycur;
this.up = direction;
this.reached = there;
}
}
}
GUI代码(最后的相关部分):
//not really relevant....?
package water_density_gui_2;
import java.awt.*;
import javax.swing.*;
public class thermohalineCirculationGUI2 extends javax.swing.JFrame {
/** Creates new form WaterDensity */
public thermohalineCirculationGUI2() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
seasonsButtons = new javax.swing.ButtonGroup();
tempPermLabel = new javax.swing.JLabel();
tempTextField = new javax.swing.JFormattedTextField();
salPermLabel = new javax.swing.JLabel();
salTextField = new javax.swing.JFormattedTextField();
densityPermLabel = new javax.swing.JLabel();
densityLabel = new javax.swing.JLabel();
runButton = new javax.swing.JButton();
seasonLabel = new javax.swing.JLabel();
summerRadioButton = new javax.swing.JRadioButton();
winterRadioButton = new javax.swing.JRadioButton();
runSimulationButton = new javax.swing.JButton();
jMenuBar1 = new javax.swing.JMenuBar();
fileMenu = new javax.swing.JMenu();
exitMenuComand = new javax.swing.JMenuItem();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Water_Density_GUI");
setBounds(new java.awt.Rectangle(0, 0, 0, 0));
setMinimumSize(new java.awt.Dimension(400, 33));
setResizable(false);
getContentPane().setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));
tempPermLabel.setText("Temperature (C):");
getContentPane().add(tempPermLabel);
double defaultTemp = 20;
tempTextField.setValue(new Double(defaultTemp));
tempTextField.setColumns(3);
getContentPane().add(tempTextField);
salPermLabel.setText("Salinity:");
getContentPane().add(salPermLabel);
double defaultSal = 35;
salTextField.setValue(new Double(defaultSal));
salTextField.setColumns(3);
getContentPane().add(salTextField);
densityPermLabel.setText("Sigma-t:");
getContentPane().add(densityPermLabel);
densityLabel.setText("_______");
getContentPane().add(densityLabel);
runButton.setText("Calculate Density");
runButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
runButtonActionPerformed(evt);
}
});
getContentPane().add(runButton);
seasonLabel.setText("Season:");
getContentPane().add(seasonLabel);
seasonsButtons.add(summerRadioButton);
summerRadioButton.setText("Summer");
getContentPane().add(summerRadioButton);
seasonsButtons.add(winterRadioButton);
winterRadioButton.setText("Winter");
getContentPane().add(winterRadioButton);
runSimulationButton.setText("Run Simulation");
runSimulationButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
runSimulationButtonActionPerformed(evt);
}
});
getContentPane().add(runSimulationButton);
fileMenu.setText("File");
exitMenuComand.setText("Exit");
exitMenuComand.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
exitMenuComandActionPerformed(evt);
}
});
fileMenu.add(exitMenuComand);
jMenuBar1.add(fileMenu);
setJMenuBar(jMenuBar1);
pack();
}// </editor-fold>
private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {
// update sigma-t value on GUI when run button is pressed
if (!(tempTextField.getText().equals("") | salTextField.getText().equals(""))) {
double temp = Double.parseDouble(tempTextField.getText());
double sal = Double.parseDouble(salTextField.getText());
double density = calculateDensity(sal, temp);
densityLabel.setText(Double.toString(density));
} else {
densityLabel.setText("N/a");
}
}
private void exitMenuComandActionPerformed(java.awt.event.ActionEvent evt) {
// exit GUI
System.exit(0);
}
//RELEVANT
//This is the method run when the button is pushed to start the animation
private void runSimulationButtonActionPerformed(java.awt.event.ActionEvent evt) {
//try to retrieve density value. If that fails, do nothing
try {
double density = Double.parseDouble(densityLabel.getText());
startAnimation(density); //this method is below
} catch (Exception e) {
}
}
//no longer relevant until next break
public static double calculateDensity(double salinity, double temp) {
/** rhos = density in kg/m^3 as a function of temperature and salinity
*S = salinity in g/kg
*rhos = rho + AS + BS^(3/2) + CS^2
*A = 8.24493E-1 - 4.0899E-3*T + 7.6438E-5*T^2 -8.2467E-7*T^3 + 5.3675E-9*T^4
*B = -5.724E-3 + 1.0227E-4*T - 1.6546E-6*T^2
*C = 4.8314E-4
* rho = density in kg/m^3 as a function of temperature
*T = temperature in C
*rho = 1000(1 - (T+288.9414)/(508929.2*(T+68.12963))*(T-3.9863)^2)
*/
double density = 0;
double rho = 0;
double eqn1 = 0;
double eqn2 = 0;
double eqn3 = 0;
double rhos = 0;
rho = 1000 * (1 - ((temp + 288.9419) / (508929.2 * (temp + 68.12963))) * (Math.pow((temp - 3.9863), 2)));
eqn1 = 0.824493 - temp * 0.0040899 + Math.pow(temp, 2) * Math.pow(10, -5) * 7.6438 - (Math.pow(10, -7) * 8.2467 * Math.pow(temp, 3)) + 5.3675 * Math.pow(10, -9) * Math.pow(temp, 4);
eqn2 = -5.724 * Math.pow(10, -3) + 1.0227 * Math.pow(10, -4) * temp - 1.6546 * Math.pow(10, -6) * Math.pow(temp, 2);
eqn3 = 4.8314 * Math.pow(10, -4);
rhos = rho + eqn1 * salinity + eqn2 * Math.pow(salinity, (3 / 2)) + eqn3 * Math.pow(salinity, 2);
density = rhos - 1000;
density = (double) 1 + (Math.floor(density * 100) / 100) / 1000;
return density;
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new thermohalineCirculationGUI2().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JLabel densityLabel;
private javax.swing.JLabel densityPermLabel;
private javax.swing.JMenuItem exitMenuComand;
public javax.swing.JMenu fileMenu;
public javax.swing.JMenuBar jMenuBar1;
public javax.swing.JButton runButton;
private javax.swing.JButton runSimulationButton;
private javax.swing.JLabel salPermLabel;
private javax.swing.JFormattedTextField salTextField;
private javax.swing.JLabel seasonLabel;
private javax.swing.ButtonGroup seasonsButtons;
private javax.swing.JRadioButton summerRadioButton;
public javax.swing.JLabel tempPermLabel;
private javax.swing.JFormattedTextField tempTextField;
private javax.swing.JRadioButton winterRadioButton;
// End of variables declaration
//RELEVANT
//This is where I try to run the animation
public void startAnimation(double density) {
animateBox gui = new animateBox();
int height = 212; //dimensions of frame I want
int width = 400;
gui.play(density); //this calls the play() method from animateBox
}
}
其他代码(用于计算pycnocline,未在图纸中使用)
package water_density_gui_2;
public class pycnoclineCalculations {
public static void main(String[] args){
double[][] pycnocline = summerPycnocline();
}
public static double[][] summerPycnocline() {
double[] pycnocline = new double[212];
double[] depth = {0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 2000, 3000, 4000};
double[] thermocline = {27, 27, 27, 24, 20, 15, 10, 8, 7, 6, 5, 5, 2, 2};
double[] halocline = {35, 35, 35, 34.9, 34.8, 34.7, 34.4, 34.4, 34.4, 34.45, 34.5, 34.5, 34.5, 34.5};
double[] pycnoclineDiscreet = new double[12];
for (int i = 0; i < 12; i++) {
pycnoclineDiscreet[i] = calculateDensity(halocline[i], thermocline[i]);
}
for (int i = 0; i < 11; i=i+1) {
double[] interpdVals = interp2dens(pycnoclineDiscreet[i], pycnoclineDiscreet[i + 1], depth[i], depth[i + 1], 10);
for (int j = 0; j < 10; j++) {
pycnocline[i*10 + j] = interpdVals[j];
}
}
double[] temp = interp2dens(pycnoclineDiscreet[10],pycnoclineDiscreet[11],depth[10],depth[11],102);
for(int i=0;i<102;i++){
pycnocline[i+110]=temp[i];
}
double[] depthsFull=new double[212];
for(int k=0;k<212;k++){
depthsFull[k] = k*10;
}
double[][] pycnoclineAndDepth = {pycnocline, depthsFull};
return pycnoclineAndDepth;
}
public static double[] interp2dens(double val1, double val2, double depth1, double depth2, int numPointsWanted) {
int size = numPointsWanted;
double[] interpdVal = new double[size];
//y = mx+b
//slope = y2-y1/(x2-x1)
//intercept b = y-mx
double slope = (val2 - val1) / (depth2 - depth1);
double intercept = val2 - slope * depth2;
double distanceStep = Math.abs(depth2 - depth1) / numPointsWanted;
interpdVal[0]=val1;
interpdVal[size-1]=val2;
for(int i =1;i<size-1;i++){
interpdVal[i]=slope*(depth1+distanceStep*i)+intercept;
}
return interpdVal;
}
public static double calculateDensity(double salinity, double temp) {
/** rhos = density in kg/m^3 as a function of temperature and salinity
*S = salinity in g/kg
*rhos = rho + AS + BS^(3/2) + CS^2
*A = 8.24493E-1 - 4.0899E-3*T + 7.6438E-5*T^2 -8.2467E-7*T^3 + 5.3675E-9*T^4
*B = -5.724E-3 + 1.0227E-4*T - 1.6546E-6*T^2
*C = 4.8314E-4
* rho = density in kg/m^3 as a function of temperature
*T = temperature in C
*rho = 1000(1 - (T+288.9414)/(508929.2*(T+68.12963))*(T-3.9863)^2)
*/
double density = 0;
double rho = 0;
double eqn1 = 0;
double eqn2 = 0;
double eqn3 = 0;
double rhos = 0;
rho = 1000 * (1 - ((temp + 288.9419) / (508929.2 * (temp + 68.12963))) * (Math.pow((temp - 3.9863), 2)));
eqn1 = 0.824493 - temp * 0.0040899 + Math.pow(temp, 2) * Math.pow(10, -5) * 7.6438 - (Math.pow(10, -7) * 8.2467 * Math.pow(temp, 3)) + 5.3675 * Math.pow(10, -9) * Math.pow(temp, 4);
eqn2 = -5.724 * Math.pow(10, -3) + 1.0227 * Math.pow(10, -4) * temp - 1.6546 * Math.pow(10, -6) * Math.pow(temp, 2);
eqn3 = 4.8314 * Math.pow(10, -4);
rhos = rho + eqn1 * salinity + eqn2 * Math.pow(salinity, (3 / 2)) + eqn3 * Math.pow(salinity, 2);
density = rhos - 1000;
density = (double) 1 + (Math.floor(density * 100) / 100) / 1000;
return density;
}
}