Good day.
I develop program which must show few shapes when user clicks the button. At least it doesn't show it. What is wrong?
Code is:
public class ShowFrame extends JFrame
{
public ShowFrame()
{
this.setTitle("Show data"); //Title
this.setSize( DEF_WIDTH, DEF_HEIGHT ); //Size of frame
this.setResizable(false);
//...
JButton testButton = new JButton("Test");
buttonPanel.add(testButton);
this.add(buttonPanel, BorderLayout.SOUTH);
testButton.addActionListener( new ActionListener() { //Add listener
public void actionPerformed(ActionEvent e) {
DrawStuff stuff = new DrawStuff(); //Create class which draws shapes
add(stuff, BorderLayout.CENTER);
System.out.println("Test Button");
}
} );
}
public static final int DEF_WIDTH = 600;
public static final int DEF_HEIGHT = 400;
private JPanel buttonPanel = new JPanel();
}
Class which draws shapes:
public class DrawStuff extends JComponent
{
public void paintComponent( Graphics g )
{
Graphics2D g2 = (Graphics2D) g;
//...
Rectangle2D rect = new Rectangle2D.Double(leftX, topY, width, height);
Line2D line = new Line2D.Double(leftX, topY, 0, 0);
//...
g2.draw(rect);
g2.draw(line);
//...
}
}
When you add/remove components on a visible GUI the code should be:
panel.add(...);
panel.revalidate();
panel.repaint();
Your design of adding a new panel every time you click a button is not a very good one.
Instead you should create a custom painting panel and override the paintComponent() method. Then when you click a button you invoke a method in your custom component to set the shape you want to draw. The paintComponent() method should be smart enought to paint the shape. Then you invoke repaint() on the panel.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Related
I am trying to build a bounce game in Java. My project has three classes ie the Main class which creates a new window(frame) where the game buttons and bounce objects are drawn.
The GameInterface class which represents the properties of the frame being drawn and the RightPanel class which I created so that I could override the paint(Graphics) method to draw my bounce object. So far this is what I have managed to draw with the code.
You can see that I have two JPanels, one that holds my buttons and the other one that accepts the drawing of a round ball on it ie RightPanel
I need help with the Button Event listeners to move the ball up and down and when user holds the button down, it needs to keep moving down until reaches the down order, sam for the up button.
The code am using is provided below.
GameInterface class
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
public class GameInterface extends JFrame {
//we need this panel declaration in the class level for reference from other methods
RightPanel rightpanel;
//define the physical properties of the window
public GameInterface(){
setSize(new Dimension(600, 600));
setResizable(false);
setTitle("Bounce Game");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBackground(Color.black);
//define a new JSplitPane and use it to add two JPanels
JPanel leftpanel= new JPanel();
//add buttons to the left panel programatically
JButton up= new JButton("Move up");
//set the event listeners for the buttons
up.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//move my ball up
//clear and redraw the ball while in a new position, use a timer or
something
}
});
JButton down = new JButton("Move down");
down.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//move my ball down
// rightpanel.getGraphics.fillColor(Color.RED);
}
});
leftpanel.add(up);
leftpanel.add(down);
//add a new RightPanel with a drawn red object
rightpanel= new RightPanel();
JSplitPane splitpane= new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftpanel,rightpanel);
this.add(splitpane);
setVisible(true);
}
}
RightPanel class
import javax.swing.*;
import java.awt.*;
public class RightPanel extends JPanel {
//define the position where the circle will be drawn
private int positionX=150;
private int positionY=150;
//I had an idea where we need a timer and then on delay we
//decrement positionX by 3 for move down but can't figure out how to clear RightPanel
private int radius=100;//as the shape is a circle
//override the paint method to draw the bounce ball on the second panel
#Override
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillOval(positionX,positionY,radius,radius);
}
}
Main class
public class Main
{
public static void main(String args[]){
new GameInterface();
}
}
How do I add logic to my code to make it move the circle up an down, Thank You.
I tried using a timer object to clear the panel and then redraw the ball in the new position of the ball but it draws a vertical bar, not clearing the original ball drawn.
Never call getGraphics() on a component.
Override paintComponent not paint
Call the super.paintComponent(g) in your override.
Give the RightPanel class setter methods that allow you to change the positionX and positionY locations for drawing,
In the button listener, call an appropriate setter method, and then call repaint() on on the RightPanel instance after changing the positions.
For example:
The key code below is here in the ActionListener where you update the position values and call repaint:
moveRightBtn.addActionListener(e -> {
// get and update the x position
int x = drawOval.getPositionX();
x += DELTA;
// call the setter method
drawOval.setPositionX(x);
// request that Java repaint the JPanel
drawOval.repaint();
});
and in the drawing JPanel's paintComponent method where you call the super's method and draw the oval:
#Override
protected void paintComponent(Graphics g) {
// this is needed to do house-keeping painting, to clear "dirty" pixels
super.paintComponent(g);
// this is needed to draw smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(OVAL_COLOR);
g2.fillOval(positionX, positionY, RADIUS, RADIUS);
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class MoveCircle extends JPanel {
private static final int DELTA = 5;
private DrawOval drawOval = new DrawOval();
public MoveCircle() {
JButton moveRightBtn = new JButton("Move Right");
moveRightBtn.addActionListener(e -> {
// get and update the x position
int x = drawOval.getPositionX();
x += DELTA;
// call the setter method
drawOval.setPositionX(x);
// request that Java repaint the JPanel
drawOval.repaint();
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(moveRightBtn);
setLayout(new BorderLayout());
add(drawOval);
add(buttonPanel, BorderLayout.LINE_START);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
MoveCircle mainPanel = new MoveCircle();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
#SuppressWarnings("serial")
class DrawOval extends JPanel {
private static final int RADIUS = 100;
private static final int PANEL_WIDTH = 600;
private static final int PANEL_HEIGHT = 450;
private static final Color OVAL_COLOR = Color.RED;
private int positionX = 0;
private int positionY = 0;
public DrawOval() {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
}
public int getPositionX() {
return positionX;
}
public void setPositionX(int positionX) {
this.positionX = positionX;
}
public int getPositionY() {
return positionY;
}
public void setPositionY(int positionY) {
this.positionY = positionY;
}
#Override
protected void paintComponent(Graphics g) {
// this is needed to do house-keeping painting, to clear "dirty" pixels
super.paintComponent(g);
// this is needed to draw smooth graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(OVAL_COLOR);
g2.fillOval(positionX, positionY, RADIUS, RADIUS);
}
}
I'm trying to make my program object oriented and I'm attempting to split it into several classes. The short version is that I want to paint different objects in 1 JFrame. So I created a class per object that I wanted to paint, defined the object in my method and then add them to my frame. The problem is that only the last component is painted in the frame. I tried adding the objects to a JPanel first but that doesn't seem to work.
trees1
public class trees1 extends JComponent{
public final ImageIcon pokemontree;
public trees1(){
ImageIcon poke = new ImageIcon("pokemontree.png");
Image image = poke.getImage(); // transform it
pokemontree = new ImageIcon(newimg); // transform it back
}
public void paintComponent (Graphics g){
Graphics2D g2 = (Graphics2D) g;
pokemontree.paintIcon(this,g2 , 100,200);
}
}
testing
// main program
public class testing {
public static void main(String[] args){
JFrame win = new JFrame();
win.setSize(600,400);
win.setTitle("Test");
win.setResizable(false);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
trees1 exo = new trees1();
Playerwalking p1 = new Playerwalking(1,2);
win.add(exo);
win.add(p1);
win.setVisible(true);
}
JFrame content pane default uses BorderLayout
The default content pane will have a BorderLayout.
For BorderLayout, the "add()" method always set to "CENTER" if you don't specify the location (NORTH/EAST/SOUTH/WEST/CENTER etc) of the components you adding.
You need to specify your "getPreferredSize()" in your component, also understand how to use the BorderLayout (or other layout manager properly).
public class Test1 extends JFrame {
public static void main(String[] args) throws Exception {
JFrame win = new JFrame();
win.setSize(600,400);
win.setTitle("Test");
win.setResizable(false);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.add(new trees1(), BorderLayout.CENTER);
win.add(new trees1(), BorderLayout.SOUTH);
win.add(new trees1(), BorderLayout.EAST);
win.pack();
win.setVisible(true);
}
}
class trees1 extends JComponent {
public final ImageIcon pokemontree;
public trees1(){
ImageIcon poke = new ImageIcon("c:\\temp\\2.png");
Image image = poke.getImage(); // transform it
pokemontree = new ImageIcon(image); // transform it back
}
#Override
public Dimension getPreferredSize() {
return new Dimension(pokemontree.getIconWidth(), pokemontree.getIconHeight());
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
pokemontree.paintIcon(this,g2 , 0,0);
}
}
this is my first question here so please apologize if I do mistakes.
Trying to better understand oop (inheritance, polymorphism, abstractization) I have imagine the following exercise : in a JPanel with BorderLayout I add a JPanel which contains two JRadioButton and an abstract panel that extends JPanel. The abstract panel can point to 2 different concrete children ... but it doesn't work as I expected. . When I select the rectangle button on the console is printed "rectangle fires event" but the paint Component of the rectangle object is not executed and when I select the oval button on the console is printed "oval fires event" and "paint oval draw oval" which means the paint Component of the oval button is executed. Please help me to understand what do I do wrong? Why the paintComponent of the rectangle is not executed?
I repeat, my aim is to practice and understand oop, not to draw ovals and rectangles.
Here is my code
public class MyGeometry extends JPanel{
private MyShape shape;
private JPanel shapeControl = new JPanel();
private JRadioButton rbtOval = new JRadioButton("Oval");
private JRadioButton rbtRectangle = new JRadioButton("Rectangle");
MyGeometry(){
setLayout(new BorderLayout());
// setting radio buttons
ButtonGroup shapeButton = new ButtonGroup();
shapeButton.add(rbtOval);
shapeButton.add(rbtRectangle);
rbtOval.setSelected(true);
shapeControl.add(rbtOval);
shapeControl.add(rbtRectangle);
//Initialize shape to avoid null pointer exception
final MyOval oval = new MyOval();
final MyRectangle rectangle = new MyRectangle();
shape = oval;
//add components to the panel
add(shapeControl, BorderLayout.NORTH);
add(shape);
//add event handling
rbtOval.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
shape = oval;
shape.repaint();
System.out.println("Oval fires event");
}
});
rbtRectangle.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
shape = rectangle;
shape.repaint();
System.out.println("Rectangle fires event");
}
});
}
public Dimension getPreferredSize(){
return new Dimension(500, 500);
}
public abstract class MyShape extends JPanel{
public abstract String dummyString();
public void paintComponent(Graphics g){
super.paintComponent(g);
}
}
public class MyOval extends MyShape{
public String dummyString(){
return "Draw oval";
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawOval(50, 50, 100, 80);
String textOval = "paint oval " + dummyString();
System.out.println(textOval);
}
}
public class MyRectangle extends MyShape{
public String dummyString(){
return "Draw rectangle";
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(50, 50, 100, 80);
String textRectangle = "paint rectangle " + dummyString();
System.out.println(textRectangle);
}
}
}
the paint Component of the rectangle object is not executed
All you are doing is setting a field on the JPanel, you are not changing the JPanel itself. I suspect what you intended was to remove the oval and add the rectangle.
Your OOP is sound; your Swing is not.
You started off with shape set to your oval. This shape was added to your JPanel. The rectangle was never added to the JPanel. Even when you set shape = rectangle, and then tell it to draw, Swing knows that your rectangle has no parent component, and thus will never be drawn. So telling it to repaint() will not call paintComponent().
I am a newb and am trying to get a line to draw with my xslider and yslider so it would create cross hairs on the canvas panel. I cannot figure this out. The idea is that when I push the "Show" button a circle is to appear centered on the crosshairs set by the sliders. I used internalframes to create the button location and canvas for the circle and sliders. I need lines connected to the sliders. I cannot change the coding as to how the sliders work in tandem, part of expectations. Please assist.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class CircleViewer2 extends JPanel
{
//Variables
Ellipse2D.Double circle;
static Color FillColor = Color.blue;
static String ShowHideName = null;
static JSlider xSlider;
static JSlider xSlider2;
static JSlider ySlider;
static JSlider ySlider2;
//Creation of the circle utilizing Ellipse2D
public CircleViewer2(int radius)
{
circle = new Ellipse2D.Double(0, 0, radius, radius);
setOpaque(false);
}
//Setting PreferredSize
public Dimension getPreferredSize()
{
Rectangle bounds = circle.getBounds();
return new Dimension(bounds.width, bounds.height);
}
//Establishing parameters for Drawing the Circle Via Paint
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FillColor);
g2.fill(circle);
}
public static void main(String[] args)
{
final JPanel center = new JPanel();
center.setLayout(null);
center.setPreferredSize(new Dimension(400,400));
ShowHideName = "Show";
final JButton ShowHideButton = new JButton(ShowHideName);
ShowHideButton.setPreferredSize(new Dimension(75, 25));
ShowHideButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (ShowHideName.equals("Show"))
{
int xCoord = xSlider.getValue();
System.out.println(xCoord);
int yCoord = ySlider.getValue();
System.out.println(yCoord);
CircleViewer2 component = new CircleViewer2(50);
component.setLocation(xCoord,yCoord);
component.setSize(component.getPreferredSize());
center.add(component);
ShowHideName = "Hide";
center.repaint();
}
else
{
ShowHideName = "Show";
center.removeAll();
center.updateUI();
}
ShowHideButton.setText(ShowHideName);
}
});
final JButton ColorButton = new JButton("Color");
ColorButton.setPreferredSize(new Dimension(75, 25));
ColorButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
FillColor = JColorChooser.showDialog(null, "Pick a Color", Color.blue);
}
});
JFrame frame = new JFrame();
JInternalFrame canvas = new JInternalFrame();
JInternalFrame buttonFrame = new JInternalFrame();
JPanel buttonPanel = new JPanel();
buttonPanel.add(ShowHideButton);
buttonPanel.add(ColorButton);
javax.swing.plaf.InternalFrameUI ifu= buttonFrame.getUI();
((javax.swing.plaf.basic.BasicInternalFrameUI)ifu).setNorthPane(null);
buttonFrame.setBounds(0, 500, 500, 200);
buttonFrame.add(buttonPanel, BorderLayout.CENTER);
buttonFrame.setVisible(true);
xSlider = new JSlider(SwingConstants.HORIZONTAL,0,380,10);
BoundedRangeModel xmodel = xSlider.getModel();
xSlider2 = new JSlider(SwingConstants.HORIZONTAL);
xSlider2.setModel(xmodel);
ySlider = new JSlider(SwingConstants.VERTICAL,0,350,10);
BoundedRangeModel ymodel = ySlider.getModel();
ySlider.setInverted(true);
ySlider2 = new JSlider(SwingConstants.VERTICAL);
ySlider2.setModel(ymodel);
ySlider2.setInverted(true);
canvas.add(center, BorderLayout.CENTER);
canvas.add(xSlider, BorderLayout.SOUTH);
canvas.add(xSlider2, BorderLayout.NORTH);
canvas.add(ySlider, BorderLayout.EAST);
canvas.add(ySlider2, BorderLayout.WEST);
canvas.setBounds(0, 0, 500, 550);
canvas.setVisible(true);
javax.swing.plaf.InternalFrameUI ifu2 = canvas.getUI();
((javax.swing.plaf.basic.BasicInternalFrameUI)ifu2).setNorthPane(null);
frame.add(canvas, BorderLayout.NORTH);
frame.add(buttonFrame, BorderLayout.SOUTH);
frame.setBounds(0, 0, 500, 530);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
This is not a good implementation of the problem. You can inherit the JFrame and make the code clearer, it's the common method to play with Swing components. Any way, Add a change Listener to your sliders and change the location of the Center Panel according to the Sliders Value.
Something like that:
xSlider.addChangeListener( e -> {
center.setLocation(new Point(xSlider.getValue(), (int) center.getLocation().getY());
});
and so on.
This is a good place to start with Java/Swing GUI best practices
Your code looks way more complex than it needs to be, and I would try to simplify it greatly. Some suggestions:
Get rid of all the JInternalFrames and use JPanels instead.
Create a JPanel, say called drawingPanel, that has its paintComponent(Graphics g) overridden and perhaps its getPreferredSize() overridden, place this JPanel BorderLayout.CENTER in your main GUI.
In the paintComponent method, have the logic to draw the circles and the crosshairs based on fields of your class.
When a JSlider moves, have its ChangeListener change the state of the corresponding field, and then have it call repaint() on the drawing panel so that it will draw the changes.
When the show button is pressed, have it change the state of a boolean variable and then call repaint().
Have the drawingPanel use the boolean in its paintComponent method to decide whether or not to draw the filled circle.
Have the drawingPanel draw the lines in its paintComponent method based on the value returned by the JSliders. Again you're calling repaint in the JSlider's listeners, so the lines will move.
Do not add and remove components on button clicks since this adds unnecessary complexity which makes things much harder to code.
Don't move the center component or any component. Just use the fixed drawingPanel JPanel and move the location of the circle and the lines that it draws.
Since this is homework, I'm going to avoid posting code as the coding should be up to you. Much luck!
I'm relatively new to java and I'm just trying to code a GUI that draws a box when a button is pressed. My problem is, my program draws the box before the button is pressed and I don't know how to fix this.
This is my controller class:
import java.awt.*;
import javax.swing.*;
import java.awt.event.* ;
public class TestController extends JFrame {
private JButton enterButton;
private JPanel buttonHolder;
private Container contentPane;
private TestView view;
public TestController() {
contentPane = getContentPane();
enterButton = new JButton("Enter");
buttonHolder = new JPanel();
buttonHolder.setPreferredSize(new Dimension (600, 100));
contentPane.add(buttonHolder, BorderLayout.SOUTH);
buttonHolder.add(enterButton);
view= new TestView();
view.setPreferredSize(new Dimension (125, 125));
contentPane.add(view, BorderLayout.CENTER);
enterButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
view.repaint();
}
});
}
public static void main(String[] args) {
TestController bc = new TestController() ;
bc.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;
bc.pack();
bc.setVisible(true) ;
}
}
This is my view class:
import java.awt.* ;
import java.awt.geom.*;
import javax.swing.* ;
public class TestView extends JPanel {
public TestView() {}
public void paintComponent( Graphics g ){
super.paintComponent( g );
Graphics2D g2= ( Graphics2D ) g;
Rectangle2D rect= new Rectangle2D.Double(0, 0, 30, 30);
g2.setPaint( Color.CYAN );
g2.fill( rect );
}
}
You have many ways to do it.
You can for example set the visibility of TestView to false until the button is pressed.
view.setVisible(false);
and in your button's action listener:
view.setVisible(true);
Why did you have your problem:
Every Visual object you create is visible by default.
When you added your object to the board, it was drawn because of that.
This call showed your object: contentPane.add(view, BorderLayout.CENTER);
First of all, let go of the illusion that you control the paint process in Swing, you don't. Swing uses a passive repaint process which is controlled by the RepaintManager, it is this objects responsibility to decide what and when something should be repainted.
paintComponent is called as part of the repaint chain on your behalf by the RepaintManager and may be called for any number of reasons (many outside of your direct control).
Your code is doing exactly what you told it to.
If you want to change the state of the components painting, then you should probably use some kind of state variable to tell the paintComponent if it should paint the rectangle or not...
private boolean paintRect = false;
public void paintComponent( Graphics g ){
super.paintComponent( g );
if (paintRect) {
Graphics2D g2= ( Graphics2D ) g;
Rectangle2D rect= new Rectangle2D.Double(0, 0, 30, 30);
g2.setPaint( Color.CYAN );
g2.fill( rect );
}
}
You would then need to provide some kind of access to the state variable in the TreeView class.
public void setPaintRect(boolean paint) {
paintRect = paint;
repaint();
}
Now, in your actionPerformed method, you would simply need to set the state...
enterButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
view.setPaintRect(true); // or what ever you want.
}
});
On a side note. Your TreeView should be overriding getPreferredSize and returning a suitable size hint for other layout managers. You've gotten away with it by using BorderLayout and manually setting the size of the frame, but TreeView's default size is 0x0.
Take a look at
Performing Custom Painting
Painting in AWT and Swing
For more details