I am trying to make a program that has a window which displays a polygon and two buttons. The polygon starts at 3 points (a triangle) and allows the user to press a "+" and "-" button to add or subtract sides of the polygon. Here is my code:
In TestPolygonBox:
package testpolygonbox
import javax.swing.*;
import java.awt.*;
public class TestPolygonBox extends JFrame {
public TestPolygonBox(){
setLayout(new BorderLayout(5,5));
add(new PolygonBox ());
}
public static void main(String[] args) {
TestPolygonBox frame = new TestPolygonBox();
frame.setTitle("Polygon Box");
frame.setSize(400,420);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
next there is the control class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class PolygonBox extends JPanel {
private JButton jbtEnlarge = new JButton("+");
private JButton jbtShrink = new JButton("-");
private PolygonPanel polygonPanel = new PolygonPanel();
public PolygonBox(){
JPanel panel = new JPanel();
panel.add(jbtEnlarge);
panel.add(jbtShrink);
setLayout(new BorderLayout());
this.add(polygonPanel,BorderLayout.CENTER);
this.add(panel, BorderLayout.SOUTH);
jbtEnlarge.addActionListener(new EnlargeListener());
jbtShrink.addActionListener(new ShrinkListener());
}
class EnlargeListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e){
polygonPanel.enlarge();
}
}
class ShrinkListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e){
polygonPanel.shrink();
}
}
}
class PolygonPanel extends JPanel{
private int polygonSides = 3;
/** Add side to the polygon*/
public void enlarge(){
polygonSides++;
repaint();
}
public void shrink(){
polygonSides--;
repaint();
}
#Override /** Draw requested Shape*/
protected void paintComponent(Graphics g){
int frameWidth = getWidth() / 2;
int frameHeight = getHeight() / 2;
int radius = (int)(Math.min(getWidth(),getHeight())* 0.4);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
g.setColor(Color.BLUE);
Polygon polygon = new Polygon();
polygon.addPoint(xCenter + radius, yCenter);
polygon.addPoint((int)(xCenter + radius * Math.cos(2 * Math.PI/polygonSides)),
(int)(yCenter - radius * Math.sin(2 * Math.PI / polygonSides)));
for (int polygonPoint = 2; polygonPoint <= polygonSides; polygonPoint++){
polygon.addPoint((int)(xCenter + radius * Math.cos(polygonPoint * 2 * Math.PI/polygonSides)),
(int)(yCenter - radius * Math.sin(polygonPoint * 2 * Math.PI / polygonSides)));
}
g.fillPolygon(polygon);
}
}
When i try to run this program i get the error:
Exception in thread "main" java.lang.IllegalArgumentException: adding a window to a container
at java.awt.Container.checkNotAWindow(Container.java:483)
at java.awt.Container.addImpl(Container.java:1084)
at java.awt.Container.add(Container.java:998)
at javax.swing.JFrame.addImpl(JFrame.java:562)
at java.awt.Container.add(Container.java:410)
at testpolygonbox.TestPolygonBox.(TestPolygonBox.java:21)
at testpolygonbox.TestPolygonBox.main(TestPolygonBox.java:24)
Java Result: 1
if anyone could tell me where i am adding a window to a container please? Im not sure what im doing wrong.
PolygonBox is a JFrame window but you're adding it to another JFrame. Change the class so it extends JPanel instead.
public class PolygonBox extends JPanel {
There are 4 severe problems with your code.
You are not executing the Swing code in the Event dispatch thread (EDT). Your main has to look like this:
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
initializeGUI();
}
}
}
This is very important even the Swing Hello World - the simplest Swing program - does it. If you want to know why this has to be like this read this concurrency tutorial. In short, If you don't do this properly once your program gets bigger and more complex you'll see a lot of inconsistent, non-reproducible exceptions and bugs that occur in the Swing classes and you can't track down.
Second, as stated by Reimeus, you can't put a frame inside another frame, PolygonBox has to be a JPanel.
Third you are not populating the panels correctly. Look at this code:
JPanel panel = new JPanel();
panel.add(jbtEnlarge);
panel.add(jbtShrink);
This code looks weird, and it is. The question that pops up when you see it is: "Where are those buttons added?" Well in the panel of course, but where in the panel? That depends of the layout that the panel is using. And what is the layout of that panel? Well since you didn't explicitly specify one it is using the default layout for JPanels: the BorderLayout. With the border layout panel.add(component); is equivalent to panel.add(component, BorderLayout.CENTER);. However you can't put two components in the CENTER, you can put a panel with a lot of components or whatever, but you can't directly put two components, that is why that code doesn't work. Then how do you populate the panel? Pick a layout learn to use it and then add the components in a proper way. For example I'll do this with the GridLayout.
JPanel panel = new JPanel(new GridLayout(1, 2));
panel.add(_enlargeButton);
panel.add(_shrinkButton);
With the Grid layout because I said that I want a grid 1x2, those adds are correct because the panel knows now that he has to put the components in each cell of the grid.
Lastly you will see that there are painting problems: the polygons are not erased, they are painted one over the other, adn you might see the image of the buttons in the panel... This is because you have to call super.paintComponent(g) in your paintComponent method. Thing is that code paints a polygon, yes, but you also have to do all the other work the component was doing for painting itself, so the first thing you have to do is call the original code you are overriding and then paint your component. For an extensive tutorial of why this works like this see Painting in AWT and Swing.
With all that, changing some variables names to follow the Java conventions and reformatting the code for a better organization. This is your fixed program:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestPolygonBox extends JFrame
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() { //Error 1: executing Swing code outside the EDT fixed
public void run()
{
TestPolygonBox frame = new TestPolygonBox();
frame.setVisible(true);
}
});
}
public TestPolygonBox()
{
super("Polygon Box"); //this is JFrame("title") since we are extending a frame
setLayout(new BorderLayout());//this line doesn't actually do nothing since the frame already usesk BorderLayout by default
add(buildPolygonBoxPanel());
setSize(400, 420);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
private JPanel buildPolygonBoxPanel() //Error 2: PolygonBox has to be a panel, also I made it into a mthod returning the panel instead of a whole class since it was unnecessary.
{
JPanel polygonBox = new JPanel(new BorderLayout());
_polygonPanel = new PolygonPanel();
JPanel buttonsPanel = new JPanel(new GridLayout(1, 2)); //Error 4 adding the buttons properly to a panel.
_enlargeButton = new JButton("+1");
_enlargeButton.addActionListener(new EnlargeListener());
_shrinkButton = new JButton("-1");
_shrinkButton.addActionListener(new ShrinkListener());
buttonsPanel.add(_enlargeButton);
buttonsPanel.add(_shrinkButton);
polygonBox.add(_polygonPanel, BorderLayout.CENTER);
polygonBox.add(buttonsPanel, BorderLayout.SOUTH);
return polygonBox;
}
class EnlargeListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
_polygonPanel.enlarge();
}
}
class ShrinkListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
_polygonPanel.shrink();
}
}
private JButton _enlargeButton;
private JButton _shrinkButton;
private PolygonPanel _polygonPanel;
}
class PolygonPanel extends JPanel
{
private int _polygonSides = 3;
/** Add side to the polygon */
public void enlarge()
{
_polygonSides++;
repaint();
}
public void shrink()
{
if (_polygonSides > 3) _polygonSides--;
repaint();
}
#Override
/** Draw requested Shape*/
protected void paintComponent(Graphics g)
{
super.paintComponent(g); //Error 4 fixed, call super.paintComponent
int radius = (int) (Math.min(getWidth(), getHeight()) * 0.4);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
g.setColor(Color.BLUE);
Polygon polygon = new Polygon();
polygon.addPoint(xCenter + radius, yCenter);
polygon.addPoint((int) (xCenter + radius * Math.cos(2 * Math.PI / _polygonSides)), (int) (yCenter - radius * Math.sin(2 * Math.PI / _polygonSides)));
for (int polygonPoint = 2; polygonPoint <= _polygonSides; polygonPoint++)
{
polygon.addPoint((int) (xCenter + radius * Math.cos(polygonPoint * 2 * Math.PI / _polygonSides)), (int) (yCenter - radius * Math.sin(polygonPoint * 2 * Math.PI / _polygonSides)));
}
g.fillPolygon(polygon);
}
}
Just copy and execute it and it looks like this:
Related
I have made a simple class to practice layouts. most of it is working fine, but my JLabel is not appearing until after I click the button. I had the same info as a JTextField and JTextArea in earlier versions, but really prefer the appearance of the JLabel, but even in the other iterations, it would only appear if I clicked on or tried to select the text from the window. I've tried setting the text variable to visible after generating it, after adding it to the under panel, and setting the whole under panel to visible in addition to the setVisible(true) called for the whole object, and none of those worked.
Here is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class LayoutPractice extends JFrame implements ActionListener {
//Set up variables
private JPanel graphic;
private JPanel under;
private JButton button;
private JLabel text;
private int clicks;
/**
* Constructor, sets up GUI
*
*/
public LayoutPractice(){
//Default JFrame setup
super("Layout Practice");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the graphic panel
this.graphic = new JPanel();
graphic.setPreferredSize(new Dimension(500, 500));
//Set up the components that go under the graphic
this.clicks = 0;
this.button = new JButton("Click for dialog");
this.text = new JLabel("No Clicks");
//Set up the under panel, add in the button and label
this.under = new JPanel();
under.setLayout(new FlowLayout());
under.add(button);
under.add(text);
//Tell it to react to the button being pressed
button.addActionListener(this);
//Set the panels onto the JFrame
getContentPane().add(graphic, BorderLayout.CENTER);
getContentPane().add(under, BorderLayout.PAGE_END);
//Pack and set the JFrame to visible
pack();
setVisible(true);
}
/**
* Paints the image displayed on graphic
*
* #param A Graphics object to be worked on
*
*/
public void paint(Graphics g) {
//Assigns which panel to paint
graphic.paint(g);
//Set a color to pains
g.setColor(Color.BLUE);
//Use variables for a pattern
int x = 0;
int y = 0;
//Loop for a pattern
while (x <= 230) {
//Generates a filled rectangle of the correct size
g.fillRect(x, y, (500-(2 * x)), (500-(2 * y)));
//Alternates color
if (g.getColor() == Color.BLUE) {
g.setColor(Color.ORANGE.darker());
}
else {
g.setColor(Color.BLUE);
}
//Increase variables to reduce rectangle size
x += 20;
y += 20;
}
}
/**
* Tells the GUI what to do when the button is pressed
*
* #param An ActionEvent, specifically the buton being pressed
*/
public void actionPerformed(ActionEvent event) {
//Increase the clicks variable
clicks++;
//Change/update the JLabel
text.setText("Clicks: " + clicks);
//Open a dialog using available tools
JOptionPane.showMessageDialog(new JFrame(),
("Clicks: " + clicks),
"Click Count",
JOptionPane.INFORMATION_MESSAGE);
}
/**
* Very simple main, makes a new LayoutPractice
*
* #param args
*/
public static void main(String[] args) {
new LayoutPractice();
}
}
The quick fix is to just call super.paint(g) at the beginning of your overriden paint method, so that the frame ensures its clearing/cleaning/layout correctly.
The best fix takes the following into account :
When overriding a method, add the #Override annotation, so that your IDE will warn you if you incorrectly override .
When overriding a painting method , call its super implementation to ensure that things get correctly cleaned by the parent component.
For custom painting, better use a JComponent (usually a JPanel).
For custom painting, override paintComponent(Graphics) (and call super.paintComponent), don't use paint(Graphics).
You don't need to extend JFrame, just create one JFrame and use it.
In the below example, a custom JPanel class has been added for custom painting, and the application doesn't extend JFrame anymore :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class LayoutPractice implements ActionListener {
//Set up variables
private final JPanel graphic;
private final JPanel under;
private final JButton button;
private final JLabel text;
private int clicks;
/**
* Constructor, sets up GUI
*
*/
public LayoutPractice() {
//Default JFrame setup
JFrame frame = new JFrame("Layout Practice");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the graphic panel
graphic = new GraphicPanel();
graphic.setPreferredSize(new Dimension(500, 500));
//Set up the components that go under the graphic
clicks = 0;
button = new JButton("Click for dialog");
text = new JLabel("No Clicks");
//Set up the under panel, add in the button and label
under = new JPanel();
under.setLayout(new FlowLayout());
under.add(button);
under.add(text);
//Tell it to react to the button being pressed
button.addActionListener(this);
JPanel mainPanel = new JPanel(new BorderLayout());
//Set the panels onto the JFrame
mainPanel.add(graphic, BorderLayout.CENTER);
mainPanel.add(under, BorderLayout.PAGE_END);
frame.setContentPane(mainPanel);
//Pack and set the JFrame to visible
frame.pack();
frame.setVisible(true);
}
/**
* Tells the GUI what to do when the button is pressed
*
* #param An ActionEvent, specifically the buton being pressed
*/
public void actionPerformed(final ActionEvent event) {
//Increase the clicks variable
clicks++;
//Change/update the JLabel
text.setText("Clicks: " + clicks);
//Open a dialog using available tools
JOptionPane.showMessageDialog(new JFrame(),
("Clicks: " + clicks),
"Click Count",
JOptionPane.INFORMATION_MESSAGE);
}
/**
* Very simple main, makes a new LayoutPractice
*
* #param args
*/
public static void main(final String[] args) {
new LayoutPractice();
}
private class GraphicPanel extends JPanel {
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
//Set a color to pains
g.setColor(Color.BLUE);
//Use variables for a pattern
int x = 0;
int y = 0;
//Loop for a pattern
while (x <= 230) {
//Generates a filled rectangle of the correct size
g.fillRect(x, y, (500 - (2 * x)), (500 - (2 * y)));
//Alternates color
if (g.getColor() == Color.BLUE) {
g.setColor(Color.ORANGE.darker());
} else {
g.setColor(Color.BLUE);
}
//Increase variables to reduce rectangle size
x += 20;
y += 20;
}
}
}
}
Have problem with adding class extended from JComponent to MyPanel class.
After choose from JComboBox list and press Start/Restart, the right panel doesnt update. Mean after adding to the Panel MyComponent class where is all drawing stuff. If someone can take a look and tell me when i am doing the mistakes or if its the different way to do that, please help me :)!
Run app class :
import javax.swing.JFrame;
public class RunApp {
public static void main(String[] args) {
JFrame mainFrame = new MyPanel();
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
mainFrame.setVisible(true);
}
}
MyPanel class :
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyPanel extends JFrame {
private JPanel leftPanel = null;
private JPanel rightPanel = null;
private JPanel mainPanel = null;
private JButton start = null;
private JButton restart = null;
private JComboBox<String> menuBox = new JComboBox<>();
private Toolkit kit = Toolkit.getDefaultToolkit();
private Dimension screenSize = kit.getScreenSize();
private int screenHeight = screenSize.height;
private int screenWidth = screenSize.width;
public MyPanel() {
mainPanel = new JPanel();
mainPanel.setBackground(Color.orange);
mainPanel.setPreferredSize(getPreferredSize());
leftPanel = new JPanel();
leftPanel.setBackground(Color.blue);
leftPanel.setPreferredSize(new Dimension(screenWidth/6, screenHeight));
menuBox.addItem("Gaussian Wave - non Dispersive");
menuBox.addItem("Gaussian Wave - Dispersive");
start = new JButton("Start");
restart = new JButton("Restart");
leftPanel.add(menuBox);
leftPanel.add(start);
leftPanel.add(restart);
rightPanel = new JPanel();
rightPanel.setBackground(Color.red);
rightPanel.setPreferredSize(new Dimension(screenWidth -( screenWidth/5 ), screenHeight));
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(menuBox.getItemAt(menuBox.getSelectedIndex()).equals("Gaussian Wave - non Dispersive")) {
rightPanel.add(new MyComponent());
rightPanel.revalidate();
rightPanel.repaint();
} else if(menuBox.getItemAt(menuBox.getSelectedIndex()).equals("Gaussian Wave - Dispersive")) {
rightPanel.add(new MyComponent());
rightPanel.revalidate();
rightPanel.repaint();
}
}
});
restart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rightPanel.removeAll();
rightPanel.revalidate();
rightPanel.repaint();
rightPanel.setBackground(Color.RED);
}
});
mainPanel.add(leftPanel);
mainPanel.add(rightPanel);
add(mainPanel);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(screenWidth, screenHeight);
}
}
Like i say the MyComponent class is not working when i add it in this way. Normally when i add this class directly like :
public MyPanel() {
add(new MyComponent);
}
Working perfect, but i want to add it after choose from the list to the divided screen. Thx !
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.GeneralPath;
import java.util.function.Function;
import javax.swing.JComponent;
public class MyComponent extends JComponent {
private int wx, wy;
private double xMin, xMax, yMin, yMax, xInc, time;
private Function<Double, Double>gaussFunction;
private Function<Double, Double>dispersiveGaussFunction;
public MyComponent() {
init();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
makeGraphics(g2);
}
private void makeGraphics(Graphics2D g2) {
GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
if(time < 5) {
countGaussianWave(this.time);
double x = xMin;
double y = gaussFunction.apply(x);
path.moveTo(
mapX(x), mapY(y)
);
x+=xInc;
while(x < xMax) {
y = gaussFunction.apply(x);
path.lineTo(
mapX(x), mapY(y)
);
x+=xInc;
}
g2.draw(path);
repaint();
this.time+=0.001;
if(this.time > 3.5) {
this.time = -3.5;
System.out.println(time);
}
}
}
private void init() {
wx = 700;
wy = 700;
xMin = -1;
xMax = 1;
yMin = -1;
yMax = 1;
xInc = 0.01;
setTime(time);
}
private double mapX(double x) {
double fx = wx / 2;
double sx = wx / (xMax - xMin);
return x * fx + sx;
}
private double mapY(double y) {
double fy = wy / 2;
double sy = wy / (yMax - yMin);
return -y * sy + fy;
}
public void countGaussianWave(double time) {
double lambda = 1;
this.gaussFunction = x -> {
return x = Math.exp(-(Math.pow((x-time ), 2)))
* (Math.cos((2*Math.PI*(x-time))/lambda)
+ Math.sin((2*Math.PI*(x-time))/lambda)); // need complex
};
}
// public void countDispersiveGaussianWave(double time) {
//
// double lambda = 1;
// double k = (2 * Math.PI) / lambda;
//
// this.dispersiveGaussFunction = x -> {
// return x = (1/(Math.sqrt(1 + 2 * time)) *
// Math.exp(-(Math.pow((1 / Math.pow(1 + 4 * time, 2)) * (x - k * time), 2)))
// * Math.exp(Math.pow(, b));
// );
// };
// }
public double getTime() {
return time;
}
public void setTime(double time) {
this.time = time;
}
}
app ss
Don't use "==" when comparing objects.
Instead you should be using the equals(...) method.
When you add (or remove) a component to a visible GUI the basic code is:
panel.add(...);
panel.revalidate();
panel.repaint();
Basically you need to make sure the layout manager is invoked otherwise the component has a size of (0, 0) so there is nothing to paint.
Also, you need to override the getPreferredSize() method to return the size of the component. Otherwise the size is (0, 0) so there is nothing to paint.
Edit
I'm guessing your components isn't showing because of your poor GUI design and hardcoding of values. A FlowLayout is a terrible layout to use for ALL the components added to the frame. You need to make an effort to logically organize the components on panels with appropriate layout managers. When lots of components are added the components will wrap to a second row. However, the preferred size does not automatically change so you may not see all the components. So
rightPanel.setPreferredSize(new Dimension(...) );
Don't hardcode a preferred size. Each component is responsible for determining its own size which is why you override the getPreferredSize() method. Then the layout manages can do their job properly. So get rid of all the setPreferrededSize() statements.
The default layout for a Jframe is a BorderLayout. I would stick with that layout. There is no need for your mainPanel as the content pane for the frame is already a JPanel.
So I would rename your "leftPanel" and maybe call it "topPanel". Then you can just add the fixed components to that panel and add that panel to the frame:
JPanel topPanel = new JPanel();
topPanel.add( comboBox );
topPanel.add( startButton );
topPanel.add( restartButton );
frame.add(topPanel, BorderLayout.PAGE_START);
So these components will appear at the top of the frame.
Now there is also no need for the "rightPanel". Instead you can just add your component directly to the frame.
frame.add( new MyComponent(), BorderLayout.CENTER);
frame.revalidate();
frame.repaint();
This component will now appear in the center of the frame and take up all the extra space available in the frame.
To start, you can simplify the start buttons ActionListener logic like so
if(combobox.getSelectedItem().equals("Text")
{
doSomething();
}
Also, after adding or removing new components, you need to call Revalidate & Repaint
If you're trying to paint in one of the components, make sure to #Override it and call it's super method super.paintComponent(g);
You also have 2 action listeners assigned to the same button. I suggest removing one of those
Along with #Camickr's answer of Overriding the getPreferredSize() method, you should change the restart button's ActionListener to this
rightPanel.removeAll();
rightPanel.revalidate();
rightPanel.repaint();
rightPanel.setBackground(Color.RED);
You can Override the getPreferredSize(); method like so:
#Override
public Dimension getPreferredSize()
{
int width = Toolkit.getDefaultToolkit().getScreenSize().width / 2;
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
return new Dimension(width, height);
}
I'm trying to resize a window dynamically using a Timer object, but not succeeding... I set the preferred size of the panel in the constructor, which sets the size of the window nicely, though only once. The preferred size changes after the program is initialized, but the window size stays the same. Why? Because the constructor is initialized only once and therefore isn't affected by the size change? If so, how could I get around this to resize the window in real-time?
I know this won't solve the problem in the exercise given in the beginning comments, so please ignore that :-)
/*
* Exercise 18.15
*
* "(Enlarge and shrink an image) Write an applet that will display a sequence of
* image files in different sizes. Initially, the viewing area for this image has
* a width of 300 and a height of 300. Your program should continuously shrink the
* viewing area by 1 in width and 1 in height until it reaches a width of 50 and
* a height of 50. At that point, the viewing area should continuously enlarge by
* 1 in width and 1 in height until it reaches a width of 300 and a height of 300.
* The viewing area should shrink and enlarge (alternately) to create animation
* for the single image."
*
* Created: 2014.01.07
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ex_18_15 extends JApplet {
// Main method
public static void main(String[] args) {
JFrame frame = new JFrame();
Ex_18_15 applet = new Ex_18_15();
applet.isStandalone = true;
frame.add(applet);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
// Data fields
private boolean isStandalone = false;
private Image image = new ImageIcon("greenguy.png").getImage();
private int xCoordinate = 360;
private int yCoordinate = 300;
private Timer timer = new Timer(20, new TimerListener());
private DrawPanel panel = new DrawPanel();
// Constructor
public Ex_18_15() {
panel.setPreferredSize(new Dimension(xCoordinate, yCoordinate));
add(panel);
timer.start();
}
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
}
class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(yCoordinate <= 50) {
yCoordinate++;
xCoordinate++;
}
else if(yCoordinate >= 300) {
yCoordinate--;
xCoordinate--;
}
panel.setPreferredSize(new Dimension(xCoordinate, yCoordinate));
repaint();
}
}
}
You need to re-pack your JFrame to resize it. For instance at the end of your ActionListener:
Window win = SwingUtilities.getWindowAncestor(panel);
win.pack();
A question for you though: Why in heaven's name is your class extending JApplet and not JPanel? Or if it needs to be an applet, why are you stuffing it into a JFrame?
Edit
Regarding your comment:
Wouldn't it usually be extending JFrame not JPanel? I'm stuffing it into a JFrame to allow it to run as an application as well as an applet. That's how 'Introduction to Java Programming' tells me how to do it :p Adding your code at the end of the actionPerformed method didn't do anything for me ;o
Most of your GUI code should be geared towards creating JPanels, not JFrames or JApplets. You can then place your JPanels where needed and desired without difficulty. Your book has serious issues and should not be trusted if it is telling you this.
Edit 2
Works for me:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class ShrinkingGui extends JPanel {
private static final int INIT_W = 400;
private static final int INIT_H = INIT_W;
private static final int TIMER_DELAY = 20;
private int prefW = INIT_W;
private int prefH = INIT_H;
public ShrinkingGui() {
new Timer(TIMER_DELAY, new TimerListener()).start();;
}
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (prefW > 0 && prefH > 0) {
prefW--;
prefH--;
Window win = SwingUtilities.getWindowAncestor(ShrinkingGui.this);
win.pack();
} else {
((Timer)e.getSource()).stop();
}
}
}
private static void createAndShowGUI() {
ShrinkingGui paintEg = new ShrinkingGui();
JFrame frame = new JFrame("Shrinking Gui");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(paintEg);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I'm a beginner in Java, and I'm trying to create an application that draws a rectangle where ever the cursor is located. I've already done everything, but I can't get the mouseMoved(MouseEvent) method to repaint the JPanel. Without the repaint, the rectangle is only drawn once and that's it. With the repaint, it compiles fine, but when I run it, every time the mouse is moved, I get this big "Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException" error.
So, can anyone please help me out on this?
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
public class Game extends JPanel implements MouseMotionListener
{
public static void main(String[] args) {
new Game().game();
}
JPanel panel;
JButton button2;
JButton button;
public void game() {
JPanel panel = new Game();
button = new JButton("Ok");
panel.setLayout(new FlowLayout());
panel.add(button);
button2 = new JButton("Cancel");
JFrame frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setResizable(false);
frame.add(panel);
frame.setVisible(true);
panel.addMouseMotionListener(this);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
PointerInfo a = MouseInfo.getPointerInfo();
Point b = a.getLocation();
int x = (int) b.getX();
int y = (int) b.getY();
g.fillRect(x,y,100,100);
}
public void mouseMoved(MouseEvent evt) {
panel.repaint; //This is the line of code that I need help with. Thanks!
}
public void mouseDragged(MouseEvent evt) {}
}
Hopefully the comments in the code example, be able to tell what you doing wrong in your code :-), otherwise there is always a reason to put forth your doubts...
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Game extends JPanel {
/*
* Now x and y are instance variables,
* whose values you can change at each
* MouseMove Event, and call repaint()
* to see the effects
*/
private int x;
private int y;
private MouseAdapter mouseActions =
new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent me) {
/*
* Now as the Mouse moves, we simply
* updating the instance variables,
* i.e. x and y to the new values
* of the Mouse Location and calling
* repaint() to draw the rectangle.
* Since this class (Game) extends JPanel,
* hence all the functions of the JPanel
* belongs to this class, hence like
* as we call any other method of this
* class, without using the object,
* we can call repaint, likewise.
*/
x = me.getX();
y = me.getY();
repaint();
}
};
/*
* This JPanel panel is unnecessary in
* this case, since the class itself
* extends JPanel, hence you can use
* this (keyword) to access the instance
*/
//JPanel panel;
// Not needed for this case.
//JButton button2;
//JButton button;
public void game() {
JFrame frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setResizable(false);
addMouseMotionListener(mouseActions);
/*
* Here this means the instance
* of the current class
*/
frame.add(this);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
/*
* While overriding methods of the
* super class, try to keep the
* ACCESS SPECIFIER, as close to
* the original thingy as possible
* In this case, it's protected
* and not public
*/
#Override
protected void paintComponent(Graphics g) {
/*
* Do not perform calculation in this method
* atleast.
*/
super.paintComponent(g);
g.fillRect(x, y, 100, 100);
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new Game().game();
}
};
EventQueue.invokeLater(runnable);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
}
Change this :
public void game() {
JPanel panel = new Game();
to this :
public void game() {
panel = new Game();
You are just creating a local variable in the first case. To fix this you need to instantiate the class variable.
I have tried to draw a diagram in Swing with a large number (thousands) of JLabels inside a large number of nestled JPanels, all with different layouts.
I made the whole diagram zoomable, zooming one step i use setPrefferedSize() on all of the components in the diagram.
It is also interactive, by clicking at one of the JLabels, the program zooms in on that JLabel. Each JLabel has a tooltip and changes color when hovered over.
The problem is that when the diagram is too large, the zoom is far to slow. There is a for-loop that has to go trough all of the JComponents in the diagram and change their prefered size. Then I have to call .revalidate() on the JComponents parent.
So my questions are:
Instead of using the nestled JPanels with different layouts for the structure, should I only use one JPanel with null Layout and use setBounds() on all of the JLabels to position and zoom in on them? Will it be lighter for the computer?
Should I use another GUI? It has to support the following:
Draw rectangular labels with tooltips, can add a mouseListener.
Zoomable, can scale the whole diagram
Can draw text in the diagram.
Scrollable, can add the diagram to a JScrollPane (important when zooming in)
You could forget the individual JLabels and just draw your text items in the paintComponent method of the main JPanel.
You can use drawString to draw the items of text.
Also, you can keep a "zoom level" value and draw the strings at different positions/sizes depending on that zoom level.
If I were you I'd knock up a quick example of this to draw a few thousand strings, and see if that's fast enough on your target spec client machine.
EDIT: Something like this:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
/**
* TestPaintComponentZooming
*
* #author davidf
*/
public class TestPaintComponentZooming {
private JFrame f;
private JPanel panel;
private double zoom = 1.0;
public static void main(String[] args) {
TestPaintComponentZooming t = new TestPaintComponentZooming();
t.init();
}
public TestPaintComponentZooming() {
}
public void init() {
JButton b = new JButton();
b.setBackground(Color.red);
b.setBorder(new LineBorder(Color.black, 2));
b.setPreferredSize(new Dimension(600, 10));
panel = new _JPanel();
panel.add(b);
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel, "Center");
f.add(getCheckBoxPanel(), "South");
f.setLocation(200, 200);
f.setSize(400, 400);
f.validate();
f.setVisible(true);
}
private JPanel getCheckBoxPanel() {
JButton zoomIn = new JButton("Zoom in");
zoomIn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
zoomAndRepaint(0.1);
}
});
JButton zoomOut = new JButton("Zoom out");
zoomOut.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
zoomAndRepaint(-0.1);
}
});
JPanel panel2 = new JPanel();
panel2.add(zoomIn);
panel2.add(zoomOut);
return panel2;
}
/**
* zoomAndRepaint
*/
protected void zoomAndRepaint(double d) {
zoom += d;
f.repaint();
}
private class _JPanel extends JPanel {
public _JPanel() {
super(null);
}
/**
* #see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(new Font("Arial", Font.PLAIN, (int) (zoom * 10 + 2)));
for (int x=0; x < 100; x++) {
for (int y=0; y < 100; y++) {
g.drawString("Hello " + x + "," + y, (int) (x * 60 * zoom), (int) (y * 10 * zoom));
}
}
}
}
}