Having troubles rendering rectangle on button press - java

I am trying to add a Rectangle in the panel by button press and remove it with another one. It should work but it doesn't render anything, and I absolutely don't know why.
Can someone explain what I am doing wrong and give me some nice tips what I can improve with my code?
public class GUI extends JPanel {
public static boolean isRecVisible = false;
public static void main(String[] args) {
createGui();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
g2d.drawRect(10, 10, 200, 200);
}
public static void createGui() {
int frameWidth = 550;
int frameHeight = 400;
int buttonWidth1 = 250;
int buttonHeight1 = 30;
int buttonWidth2 = 500;
int buttonHeight2 = 30;
int displayWidth = frameWidth - 20;
int displayHeight = frameHeight - 105;
GUI drawRec = new GUI();
JFrame f = new JFrame("Rectangle");
JPanel p = new JPanel();
JPanel display = new JPanel();
JButton addRec = new JButton("Add Rectangle");
JButton removeRec = new JButton("Remove Rectangle");
JButton colorRec = new JButton("Color Rectangle");
f.add(p);
p.add(addRec);
p.add(removeRec);
p.add(colorRec);
p.add(display);
display.setBackground(Color.LIGHT_GRAY);
f.setSize(frameWidth, frameHeight);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
display.setBounds(frameWidth / 2 - displayWidth / 2, 10, displayWidth, displayHeight);
addRec.setBounds(frameWidth / 2 - buttonWidth1 / 2 - 250 / 2, frameHeight - 85, buttonWidth1, buttonHeight1);
removeRec.setBounds(frameWidth / 2 - buttonWidth1 / 2 + 250 / 2, frameHeight - 85, buttonWidth1, buttonHeight1);
colorRec.setBounds(frameWidth / 2 - buttonWidth2 / 2, frameHeight - 60, buttonWidth2, buttonHeight2);
addRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isRecVisible == false) {
isRecVisible = true;
display.add(drawRec);
display.repaint();
System.out.println("Rectangle has been drawn!");
} else {
System.out.println("Rectangle has already been drawn!");
return;
}
}
});
removeRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isRecVisible == true) {
isRecVisible = false;
System.out.println("Rectangle has been removed!");
} else {
System.out.println("Rectangle has already been removed");
return;
}
}
});
}
}

display.add(drawRec);
display.repaint();
When you add (or remove) components to a visible frame then the basic logic is:
display.add(...);
display.revalidate();
display.repaint(); // sometimes needed
The revalidate() is the key method because it invokes the layout manager so the size/location of the component can be set.
However, that still won't fix the problem because your custom panel doesn't have a preferred size, so there is nothing to paint for your component.
You need to override the getPreferredSize() method of your custom panel to return the preferred size of your custom component. So in your case you might set the preferred size to be (220, 220) so the rectangle is centered in the panel.
Read the section from the Swing tutorial on Custom Painting for more information and complete working examples.
Note: the tutorial example will also show you how to better structure your code to make sure the GUI is created on the Event Dispatch Thread.

Rather than adding or removing components, it would make more sense here to add the custom painted panel on construction, and use the isRecVisible boolean as a flag to test for drawing the rectangle.
Something like shown here:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI extends JPanel {
public static boolean isRecVisible = false;
public static void main(String[] args) {
createGui();
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
if (isRecVisible) {
g2d.drawRect(10, 10, 200, 200);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600,400);
}
public static void createGui() {
int frameWidth = 550;
int frameHeight = 400;
GUI drawRec = new GUI();
drawRec.setBackground(Color.LIGHT_GRAY);
JFrame f = new JFrame("Rectangle");
JPanel p = new JPanel();
JButton addRec = new JButton("Add Rectangle");
JButton removeRec = new JButton("Remove Rectangle");
JButton colorRec = new JButton("Color Rectangle");
f.add(p, BorderLayout.PAGE_START);
p.add(addRec);
p.add(removeRec);
p.add(colorRec);
f.add(drawRec);
f.setSize(frameWidth, frameHeight);
f.setLocationRelativeTo(null);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
addRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
isRecVisible = true;
drawRec.repaint();
}
});
removeRec.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
isRecVisible = false;
drawRec.repaint();
}
});
}
}

Related

Why can I not move the array initializer?

Whenever I try to move the array lifegrid, the complier gives errors and something goes wrong with the window. Not sure what the array has to do with that as I am not yet using it to change the displayed information.
I want to be able to call my code in the class LifeRunningCode but because my array is defined in MyPanel it is not visible in CreateAndShowGUI. I need it to be visible to both methods. Can I place it just before the MAIN method?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SwingPaintDemo3 {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
static void createAndShowGUI() {
System.out.println("Created GUI on EDT? " + SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
JPanel subPanel = new JPanel();
JButton seed = new JButton("SEED");
subPanel.add(seed);
seed.setPreferredSize(new Dimension(70, 50));
seed.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("Seed");
}
});
JButton start = new JButton("START");
subPanel.add(start);
start.setPreferredSize(new Dimension(70, 50));
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae1) {
System.out.println("Start");
//lifegrid = LifeRunningCode.changeValues(lifegrid);
}
});
start.setPreferredSize(new Dimension(70, 50));
JButton stop = new JButton("STOP");
subPanel.add(stop);
stop.setPreferredSize(new Dimension(70, 50));
JButton reset = new JButton("RESET");
subPanel.add(reset);
reset.setPreferredSize(new Dimension(70, 50));
f.add(subPanel, BorderLayout.EAST);
f.setVisible(true);
}
}
class MyPanel extends JPanel {
int lifegrid[][][] = new int[62][42][2];
int squareX = 1280;
int squareY = 800;
int gridX = 0;
int gridY = 0;
public MyPanel() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
squareX = e.getX();
squareY = e.getY();
if ((squareX > 20 & squareX < 920) & (squareY > 50 & squareY < 650)) {
gridX = (squareX - 20) / 15 + 1;
gridY = (squareY - 50) / 15 + 1;
squareX = (squareX - 20) / 15 * 15 + 20;
squareY = (squareY - 50) / 15 * 15 + 50;
lifegrid[gridX][gridY][0] = 1;
System.out.println(gridX + " " + gridY);
repaint(squareX, squareY, 15, 15);
}
else {}
}
});
}
public Dimension getPreferredSize() {
return new Dimension(1280, 800);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(squareX, squareY, 13, 13);
g.setColor(Color.BLACK);
g.drawRect(squareX, squareY, 13, 13);
}
}
All the UI objects are localized in createAndShowGUI method.
If you need to access the array inside an instance of MyPanel class you should:
create a getter for this array in MyPanel
store an instance of MyPanel inside main class SwingPaintDemo3 and refer it as necessary:
// MyPanel
class MyPanel extends JPanel {
// ...
public int[][][] getLifegrid() {
return lifegrid;
}
}
// SwingPaintDemo3
public class SwingPaintDemo3 {
static MyPanel myPanel = new MyPanel();
static void createAndShowGUI() {
System.out.println("Created GUI on EDT? " + SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(myPanel);
// ...
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae1) {
System.out.println("Start");
LifeRunningCode.changeValues(myPanel.getLifegrid());
}
});
// ...
}
}

Repaint won't happen until the for loop has escaped, with only two frames

I'm trying to do some basic Java, and I've got my frame with a shape in it. It's using a JComponent class to draw the shape, with the animation being triggered on a button click at the top.
the component code is just this added to the jpanel
public void paintComponent(Graphics g){
Dimension dim = getSize();
g.setColor(Color.GREEN);
g.fillOval(margin, 150, 100, 100);
super.paintComponent(g);
}
The animation is just done inside a for loop, that just edits the left margin so that the circle moves to the right;
int getMarg = cc.getMargin();
for(int i = 1;i < 20;i++){
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
But it doesn't seem to move until the end of the loop, moving 20 pixels at a time. I previously had a sleep function but it seemed pointless when it wouldn't animate.
Any insight? Cheers.
The whole code for anyone interested, messy and largely just to get the styling:
class Main extends JFrame{
public JPanel panel = new JPanel();
JButton button1 = new JButton("Move Right");
CreateComps cc = new CreateComps();
Main(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initUI();
}
void initUI(){
setSize(800,800);
setBackground(Color.GRAY);
setLayout(new BoxLayout(this.getContentPane(), BoxLayout.Y_AXIS));
JPanel topBar = new JPanel();
topBar.setPreferredSize(new Dimension(800,30));
topBar.setMaximumSize(new Dimension(800,30));
topBar.setLayout(new BorderLayout());
topBar.add(button1, BorderLayout.WEST);
topBar.setBackground(Color.GRAY);
JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
container.setBackground(Color.DARK_GRAY);
panel.setPreferredSize(new Dimension(600,500));
panel.setMinimumSize(new Dimension(600,500));
panel.setBackground(Color.WHITE);
panel.setLayout(new BorderLayout());
panel.add(cc, BorderLayout.CENTER);
add(topBar);
add(container);
container.add(panel);
Listener listen = new Listener();
button1.addActionListener(listen);
setVisible(true);
}
public void reValidate(){
panel.revalidate();
panel.repaint();
}
public static void main (String[] args){
Main main = new Main();
}
class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Listening..");
if(e.getSource().equals(button1)){
int getMarg = cc.getMargin();
for(int i = 1;i < 20;i++){
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
}
}
}
}
}
class CreateComps extends JComponent{
int margin = 10;
public void setMargin(int marg){
margin = marg;
}
public int getMargin(){
return margin;
}
#Override
public Dimension getPreferredSize(){
return new Dimension(new Dimension(200,200));
}
#Override
public Dimension getMaximumSize(){
return new Dimension(new Dimension(200,200));
}
#Override
public Dimension getMinimumSize(){
return new Dimension(new Dimension(200,200));
}
public void paintComponent(Graphics g){
Dimension dim = getSize();
g.setColor(Color.GREEN);
g.fillOval(margin, 150, 100, 100);
super.paintComponent(g);
}
}
Without pauses, you're stacking calls to revalidate and will see nothing else than the result of the last call.
The sleep function you previously had, probably was called on the Event Dispatch Thread, which is not good at all, since you're blocking the entire event dispatching and GUI updates.
Consider either using sleep calls on another Thread :
#Override
public void actionPerformed(final ActionEvent e) {
System.out.println("Listening..");
if (e.getSource().equals(button1)) {
new Thread() {
#Override
public void run() {
int getMarg = cc.getMargin();
for (int i = 1; i < 20; i++) {
getMarg = cc.getMargin();
cc.setMargin(getMarg + 1);
reValidate();
System.out.println(i);
try {
Thread.sleep(50);
} catch (Throwable e) {
}
}
}
}.start();
}
}
Or you may also simply use a Timer, which is great for this job.

JTextField horizontal scrolling java swing

I have a JTextField inside a JPanel . The user can type text as big as she wants in the textfield , however horizontal scrolling should appear when the text goes beyond the screen of the textfield. At this point if the user backspace the text and shrink the text within the screen of the textfield , the scrolling should disappear. So basically scrolling should appear only when its needed. How to do it ? Help please.
If you don't like the behavior of the JScrollPane + JTextField, you need to use JScrollBar#setModel(BoundedRangeModel) + JTextField#getHorizontalVisibility():
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicScrollBarUI;
public class TextFieldScrollBarTest {
private static final String TEXT = "javascript:(function(){var l=location,m=l.href.match('^(https?://)(.+)(api[^+]+|technotes[^+]+)');if(m)l.href=m[1]+'docs.oracle.com/javase/8/docs/'+decodeURIComponent(m[3]).replace(/\\+.*$/,'').replace(/\\[\\]/g,':A').replace(/, |\\(|\\)/g,'-');}());";
public JComponent makeUI() {
JScrollPane scroll = new JScrollPane(new JTextField(TEXT));
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
JTextField textField = new JTextField(TEXT);
JScrollBar scroller = new JScrollBar(Adjustable.HORIZONTAL) {
#Override public void updateUI() {
//super.updateUI();
setUI(new ArrowButtonlessScrollBarUI());
}
#Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.height = 5;
return d;
}
};
scroller.setModel(textField.getHorizontalVisibility());
Box box = Box.createVerticalBox();
box.add(scroll);
box.add(Box.createVerticalStrut(50));
box.add(textField);
box.add(Box.createVerticalStrut(2));
box.add(scroller);
box.add(Box.createVerticalGlue());
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
p.add(box, BorderLayout.NORTH);
return p;
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TextFieldScrollBarTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class ZeroSizeButton extends JButton {
private static final Dimension ZERO_SIZE = new Dimension();
#Override public Dimension getPreferredSize() {
return ZERO_SIZE;
}
}
class ArrowButtonlessScrollBarUI extends BasicScrollBarUI {
private static final Color DEFAULT_COLOR = new Color(220, 100, 100);
private static final Color DRAGGING_COLOR = new Color(200, 100, 100);
private static final Color ROLLOVER_COLOR = new Color(255, 120, 100);
#Override protected JButton createDecreaseButton(int orientation) {
return new ZeroSizeButton();
}
#Override protected JButton createIncreaseButton(int orientation) {
return new ZeroSizeButton();
}
#Override protected void paintTrack(Graphics g, JComponent c, Rectangle r) {
//Graphics2D g2 = (Graphics2D) g.create();
//g2.setPaint(new Color(100, 100, 100));
//g2.fillRect(r.x, r.y, r.width - 1, r.height - 1);
//g2.dispose();
}
#Override protected void paintThumb(Graphics g, JComponent c, Rectangle r) {
JScrollBar sb = (JScrollBar) c;
if (!sb.isEnabled()) {
return;
}
BoundedRangeModel m = sb.getModel();
int iv = m.getMaximum() - m.getMinimum() - m.getExtent() - 1; // -1: bug?
if (iv > 0) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Color color;
if (isDragging) {
color = DRAGGING_COLOR;
} else if (isThumbRollover()) {
color = ROLLOVER_COLOR;
} else {
color = DEFAULT_COLOR;
}
g2.setPaint(color);
g2.fillRect(r.x, r.y, r.width - 1, r.height - 1);
g2.dispose();
}
}
}

JPanel does not appear after click JButton

My JPanel doesn't show up when my JButton is clicked. It does shows up when I add the JPanel in my go() method. However, once I tried to execute it by click the JButton, it doesn't work. The program does goes into the loop of the actionPeformed() method of the listener though.
public class MyShape
{
JFrame frame;
JPanel panel;
JButton drawButton;
public static void main (String[] args)
{
MyShape test = new MyRandomShape();
test.go();
}
public void go()
{
drawButton = new JButton("Draw Shape!");
drawButton.addActionListener(new DrawListener());
frame = new JFrame();
frame.add(drawButton, BorderLayout.NORTH);
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class DrawListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if(empty)
{
System.out.print("IN");
panel = new DrawPanel();
frame.add(panel, BorderLayout.CENTER);
}
}
}
private class DrawPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponents(g);
int randNo = (int)(Math.random() * 3);
int width = (int)(Math.random() * getWidth());
int height = (int)(Math.random() * getHeight());
int xpos = getWidth()/2-width/2;
int ypos = getHeight()/2-height/2;
int v1 = (int)(Math.random() * 256);
int v2 = (int)(Math.random() * 256);
int v3 = (int)(Math.random() * 256);
g.setColor(new Color(v1, v2, v3));
if(randNo == 0)
{
g.fillOval(xpos, ypos, width, height);
}
else if(randNo == 1)
{
g.fillRect(xpos, ypos, width, height);
}
else
{
int startAngle = (int)(Math.random() * 360);
int arcAngle = (int)(Math.random() * 360);
g.fillArc(xpos, ypos, width, height, startAngle, arcAngle);
}
}
}
}
How do I get the JPanel to show up once the button is clicked?
You have to call parentComponent.revalidate() every time you do one or more parentComponent.add(childComponent) (or change its children in other ways, such as reorder or remove them).
In your case, your code should be
private class DrawListener implements ActionListener {
public void actionPerformed(ActionEvent event)
{
if(empty)
{
System.out.print("IN");
panel = new DrawPanel();
frame.add(panel, BorderLayout.CENTER);
frame.revalidate(); // <---------- important
}
}
}
Few changes, check this
public static void main(String[] args) {
MyShape test = new MyShape();
test.go();
}
public void go() {
drawButton = new JButton("Draw Shape!");
drawButton.addActionListener(new DrawListener());
frame = new JFrame();
frame.getContentPane().add(drawButton, BorderLayout.NORTH);
panel = new DrawPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.setSize(500, 500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class DrawListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
frame.getContentPane().remove(1);
panel = new DrawPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.repaint();
frame.validate();
}
}

Java repainting when pushing a button

I've hit a wall (in my brain) trying to update my board on button presses. Am I right in thinking that the GameBoard class is the one that needs to be repaint()ed?
GameBoard.java
public class GameBoard extends Panel {
static Compass compass = new Compass();
private static final long serialVersionUID = 1;
Graphics2D g2d;
static final Dimension WINDOW_SIZE = new Dimension(1150, 800);
public void boardMaker() throws Exception {
JFrame frame = new JFrame("Display image");
JPanel panel = new JPanel();
/* unimportant stuff
.....
*/
//
DieRoll roll = new DieRoll("Roll Dies");
roll.setC(compass);
roll.setG2D(g2d);
//
Button button = new Button("new");
button.setGameBoard(this);
JPanel buttonPanel = new JPanel();
buttonPanel.add(button);
buttonPanel.add(roll);
buttonPanel.setPreferredSize(new Dimension(200,100));
frame.getContentPane().add(buttonPanel, BorderLayout.NORTH);
//
frame.getContentPane().add(panel);
frame.setVisible(true);
}
public void paint(Graphics g) {
// not important I think
}
}
Button.java
public class Button extends JButton implements ActionListener {
private static final long serialVersionUID = 1L;
JPanel panel = new JPanel();
JFrame frame = new JFrame();
Compass c = new Compass();
GameBoard gb = new GameBoard();
Button(String text) {
this.setText(text);
this.addActionListener(this);
}
void setGameBoard(GameBoard gb) {
this.gb = gb;
}
#Override
public void actionPerformed(ActionEvent e) {
gb.g2d.setColor(Color.black);
gb.g2d.fillRect(100, 100, 100, 200);
gb.repaint();
}
}
This gives a null pointer exception. So any idea how to repaint my GameBoard? I'm not mad if I've to rewrite everything because of stupidity! ;)
Thanks
You have the wrong idea about how to draw in Java. Components like Panels draw themselves, and all drawing takes place on the UI thread.
Check out this tutorial: docs.oracle.com/javase/tutorial/2d/index.html
The article Painting in AWT and Swing may offer some perspective on application-triggered painting. The example below illustrates the principle. Note that setForeground() calls repaint() automatically because the foreground color is a bound property, but you can always call it yourself.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class SwingPaint {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
final GamePanel gp = new GamePanel();
f.add(gp);
f.add(new JButton(new AbstractAction("Update") {
#Override
public void actionPerformed(ActionEvent e) {
gp.update();
}
}), BorderLayout.SOUTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
private static class GamePanel extends JPanel {
private static final Random r = new Random();
public GamePanel() {
this.setForeground(new Color(r.nextInt()));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(320, 240);
}
public void update() {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = this.getSize();
int d = Math.min(size.width, size.height) - 10;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
}

Categories

Resources