Having trouble repainting circles on button click - java

I am having trouble making my window repaint when clicking a button. What's supposed to happen when I click the button is more circles should be drawn on the frame (it should technically be the number of circles drawn last time * 2). For some reason, it's not working and I can't really figure out why it wont repaint. Here's my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Roaches {
public static void main(String[] args) {
//Create the frame
JFrame frame = new JFrame();
//Set the frame's size
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Roaches!");
//Create the button
JButton button = new JButton("Multiply Roaches!");
button.addActionListener(new Roach());
JPanel buttonPanel = new JPanel();
//Add the button to the panel
buttonPanel.add(button);
//Add the panel to the frame
frame.add(buttonPanel, BorderLayout.SOUTH);
//Add the roach panel to the frame
frame.add(new Roach());
//Make frame visible
frame.setVisible(true);
}
}
class Roach extends JComponent implements ActionListener {
//Keep track of how many roaches to draw
private int roachCount = 1;
protected void paintComponent(Graphics g) {
Graphics2D gr = (Graphics2D)g;
for(int i = 0; i < roachCount; i++) {
int x = 5 * i;
int y = 5 * 1;
gr.drawOval(x, y, 50, 50);
}
System.out.println("REPAINT"); //For testing
}
public void actionPerformed(ActionEvent e) {
roachCount = roachCount * 2;
repaint();
System.out.println("CLICK!!!!"); //For testing
}
}
I don't know if I have the concept of events or repainting wrong, so any help/pointers would be appreciated. Thanks!

You don't want to create a new Roach() the second time. Just create one roach and keep a reference to it and use the reference everywhere.
public static void main(String[] args) {
//Create the frame
JFrame frame = new JFrame();
//Set the frame's size
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Roaches!");
//Create the button
JButton button = new JButton("Multiply Roaches!");
Roach roach = new Roach();
button.addActionListener(roach);
JPanel buttonPanel = new JPanel();
//Add the button to the panel
buttonPanel.add(button);
//Add the panel to the frame
frame.add(buttonPanel, BorderLayout.SOUTH);
//Add the roach panel to the frame
frame.add(roach);
//Make frame visible
frame.setVisible(true);
}

The issue is that you're creating two instances of Roach. One that you add as an action listener to your JButton and the other that you add to the frame to draw. Because they are different instances, the Roach that is drawn never receives an action and thus always has a count of 1.
This is actually a good example of why I dislike the practice of implementing listener interfaces on gui objects.

Related

Java Swing: Components automatically change position when a certain component updates

I made a Java program and part of the program's function is to track the user's mouse X and Y coordinates.
The tracking works nicely but there's a small problem that bothers me.
When I move my mouse around the screen, the other components automatically change position.
Here's a MRE(Minimal Reproducible Example):
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.event.ActionListener;
public class Test {
static Timer t;
static JLabel label1;
static InnerTest inner;
static int mouseX;
static int mouseY;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
buildFrame();
runTimer();
}
});
}
public static void buildFrame() {
JFrame frame = new JFrame();
label1 = new JLabel("Test1");
JLabel label2 = new JLabel("Test Test");
JLabel label3 = new JLabel("Test Label Label");
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(label1);
panel.add(label2);
panel.add(label3);
label1.setAlignmentX(Component.CENTER_ALIGNMENT);
label2.setAlignmentX(Component.CENTER_ALIGNMENT);
label3.setAlignmentX(Component.CENTER_ALIGNMENT);
frame.getContentPane().add(panel);
frame.setSize(400, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void runTimer() {
inner = new InnerTest();
t = new Timer(20, inner);
t.start();
}
static class InnerTest implements ActionListener {
public void actionPerformed(ActionEvent e) {
mouseX = MouseInfo.getPointerInfo().getLocation().x * 100;
mouseY = MouseInfo.getPointerInfo().getLocation().y * 100;
label1.setText(" ( "+String.valueOf(mouseX)+" , "+String.valueOf(mouseY)+" )");
}
}
}
How do I keep the components still when another component is updating?
The components are added to the same panel so each is centered based on the maximum width of all three components. As the width of the top label changes the others are also adjusted.
The solution is to separate the top label from the other two labels.
One way would be:
//panel.add(label1);
panel.add(label2);
panel.add(label3);
//label1.setAlignmentX(Component.CENTER_ALIGNMENT);
label1.setHorizontalAlignment(JLabel.CENTER); // added
label2.setAlignmentX(Component.CENTER_ALIGNMENT);
label3.setAlignmentX(Component.CENTER_ALIGNMENT);
frame.add(label1, BorderLayout.PAGE_START); // added
frame.getContentPane().add(panel);

Layout manager problems while programming UI

There is a specific UI that I am trying to create for a Java program and I have been having trouble choosing the adequate Layout Managers. I would like my program to have a top panel with three elements (Two JTextFields and one JButton) and a lower JPanel that has another JPanel inside. The inner panel should always be a square, centered according to its container and adapt to the maximum height or width of its container. I have tried using a ComponentAdapter to achieve the effect of always staying a square, but the program does not seem to act the way I want it to, also the top Panel seems to get squeezed to the top
JPanel maincontainer = new JPanel();
maincontainer.setLayout(new BoxLayout(maincontainer, BoxLayout.PAGE_AXIS));
JPanel jpanel2 = new JPanel();
jpanel2.setLayout(new GridLayout(0, 3));
JTextField txt = new JTextField();
txt.setFocusable(false);
JButton btn = new JButton();
btn.setFocusable(false);
JTextField txt2 = new JTextField();
txt2.setFocusable(false);
jpanel2.add(txt);
jpanel2.add(btn);
jpanel2.add(txt2);
maincontainer.add(jpanel2);
JPanel masterPane = new JPanel(new GridBagLayout());
JPanel centerPane = new JPanel();
masterPane.add(centerPane);
masterPane.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
if(masterPane.getHeight()<masterPane.getWidth())
centerPane.setSize(masterPane.getHeight(), masterPane.getHeight());
else
centerPane.setSize(masterPane.getWidth(), masterPane.getWidth());
}
});
centerPane.setBackground(Color.blue);
masterPane.add(centerPane);
maincontainer.add(masterPane);
JFrame frame = new JFrame("");
frame.getContentPane().add(maincontainer);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setMinimumSize(new Dimension(300,300));
frame.setSize(500, 500);
I would like my program to have a top panel with three elements (Two JTextFields and one JButton) and a lower JPanel that has another JPanel inside.
The easiest way to do this is to keep using the default layout manager of the frame which is a BorderLayout. You add the panel with the text fields and buttons to the BorderLayout.PAGE_START. Then you add the panel that changes dynamically to the BorderLayout.CENTER.
The inner panel should always be a square, centered according to its container and adapt to the maximum height or width of its container
The easiest way to center a component on a panel is to use a GridBagLayout on the panel. The default GridBagConstraints will cause the component to be displayed at it preferred size centered both vertically and horizontally. So you will need a wrapper panel using the GridBagLayout to contain your center panel.
You would then want to override the getPreferredSize() method of your center panel to dynamically change as the size of the parent panel changes. This is a better approach than using a ComponentListener.
Something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class SSCCE extends JPanel
{
SSCCE()
{
setLayout( new BorderLayout() );
JPanel top = new JPanel( new GridLayout(0, 3) );
top.add( new JTextField(10) );
top.add( new JButton("Button") );
top.add( new JTextField(10) );
add(top, BorderLayout.PAGE_START);
JPanel center = new JPanel()
{
#Override
public Dimension getPreferredSize()
{
Dimension parent = getParent().getSize();
if (parent.width < parent.height)
return new Dimension(parent.width, parent.width);
else
return new Dimension(parent.height, parent.height);
}
};
center.setBackground( Color.BLUE );
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add(center, new GridBagConstraints());
add(wrapper, BorderLayout.CENTER);
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SSCCE());
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
static class DragListener extends MouseInputAdapter
{
Point location;
MouseEvent pressed;
public void mousePressed(MouseEvent me)
{
pressed = me;
}
public void mouseDragged(MouseEvent me)
{
Component component = me.getComponent();
location = component.getLocation(location);
int x = location.x - pressed.getX() + me.getX();
int y = location.y - pressed.getY() + me.getY();
component.setLocation(x, y);
}
}
}

JLabel-Array create on Button push

I want to create a JLabel of an Array on a button push. I've never done something with Arrays, so maybe it's some sort of training. I tried it, but it won't work. Expected result: Everytime I push the button, would one of the 20 JLabels be created.
Here's my class:
public class JLabelArray {
static JFrame frame;
static JButton button;
public static void main(String[] args) {
final JLabel[] label = new JLabel[20];
//
button = new JButton("push me");
button.setBounds(0, 0, 100, 30);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// This is where a new JLabel should be created
for (int i = 0; i < label.length; i++) {
label[i] = new JLabel("Label" + i);
label[i].setBounds(button.getX(), button.getY()+ 10 + i * 15, 50, 50);
frame.add(label[i]);
frame.revalidate();
frame.repaint();
}
}
});
//
frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setVisible(true);
frame.add(button);
}
}
EDIT: Updated code
EDIT2: Updated for-loop
EDIT3: I reworked the whole question
You should re-validate/repaint when you add Components after the parent container has been validated - this includes not only when you add the JButton (add this prior to setting the JFrame to visible) but also when you add the JLabels
frame.add(label[i]);
frame.revalidate();
frame.repaint();
I would also recommend not using a null layout, rather choose the appropriate LayoutManager: see https://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html . If you wish to continue using a null layout, then you should set the added Labels to different positions or they will just overlap each other.

Can't get panels and buttons to display across methods

I'm working on a calculator/phone input keypad, and am having all sorts of trouble getting it to actually display the buttons. I got it to work when everything was in the main method, but that wouldn't allow me to implement ActionListener, which I need in order for the buttons to work. Here's the code I have right now:
import java.awt.*; //Import everything
import java.util.*;
import java.awt.event.*;
import javax.swing.*; //Seriously, everything, just makes this easier
public class Keypad extends JPanel implements ActionListener {
public static void main(String[] args) {
//Create the frame that holds everything else
JFrame frame = new JFrame ("Almost Functional Keypad");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
//Create the keypad
frame.getContentPane().add(new Keypad());
//Stuff to do so it won't break
frame.setPreferredSize(new Dimension(350,400));
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
}
public Keypad(){
//Create the array of JPanels and labels
JButton[] keyList = new JButton[12];
JLabel[] keyLabel = new JLabel[12];
//Array with the key labels
String[] keyLabelText = {"1","2","3","4","5","6","7","8","9","*","0","#"};
//We need a counter
int i;
//Create the panels
for (i=0; i<12; i++){
keyList[i]= new JButton();
keyList[i].setPreferredSize (new Dimension(100, 75));
keyLabel[i] = new JLabel (keyLabelText[i]);
keyList[i].add (keyLabel[i]);
keyList[i].addActionListener(this);
}
//Text field that only the buttons can modify
JTextArea displayText = new JTextArea();
displayText.setPreferredSize(new Dimension(320, 20));
//Create the "clear" button
JButton clear = new JButton();
clear.setPreferredSize(new Dimension(300, 40));
JLabel clearLabel = new JLabel ("Clear");
clear.add(clearLabel);
clear.addActionListener(this);
// Set up primary panel
JPanel primary = new JPanel();
primary.add(displayText);
for (i=0; i<12; i++){
primary.add (keyList[i]);
}
primary.add(clear);
}
public void actionPerformed (ActionEvent event){
//Stuff goes here eventually
}
}
I'm pretty sure that it has something to do with calling Keypad() in main, but I have no idea how to call it and get things to display. I have to create the panels in the constructor, otherwise I can't add ActionListener, but now I don't know how to get them into frame.
Thanks for any help, and I'm hoping it's just something small and stupid I'm forgetting.
Changes i made to your code were changing the constructor to a method returning a JPanel and some very minor changes. You should notice that now the function returns a JPanel which fixes your problem.
Check this
import java.awt.*; //Import everything
import java.util.*;
import java.awt.event.*;
import javax.swing.*; //Seriously, everything, just makes this easier
public class Keypad extends JPanel implements ActionListener {
public static void main(String[] args) {
//Create the frame that holds everything else
JFrame frame = new JFrame ("Almost Functional Keypad");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
//Create the keypad
frame.getContentPane().add(getKeypad());
//Stuff to do so it won't break
frame.setPreferredSize(new Dimension(350,400));
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
}
public static JPanel getKeypad(){
//Create the array of JPanels and labels
Keypad obj = new Keypad();
JButton[] keyList = new JButton[12];
JLabel[] keyLabel = new JLabel[12];
//Array with the key labels
String[] keyLabelText = {"1","2","3","4","5","6","7","8","9","*","0","#"};
//We need a counter
int i;
//Create the panels
for (i=0; i<12; i++){
keyList[i]= new JButton();
keyList[i].setPreferredSize (new Dimension(100, 75));
keyLabel[i] = new JLabel (keyLabelText[i]);
keyList[i].add (keyLabel[i]);
keyList[i].addActionListener(obj);
}
//Text field that only the buttons can modify
JTextArea displayText = new JTextArea();
displayText.setPreferredSize(new Dimension(320, 20));
//Create the "clear" button
JButton clear = new JButton();
clear.setPreferredSize(new Dimension(300, 40));
JLabel clearLabel = new JLabel ("Clear");
clear.add(clearLabel);
clear.addActionListener(obj);
// Set up primary panel
JPanel primary = new JPanel();
primary.add(displayText);
for (i=0; i<12; i++){
primary.add (keyList[i]);
}
primary.add(clear);
return primary;
}
public void actionPerformed (ActionEvent event){
//Stuff goes here eventually
// use it like this
//if(event.getSource()==whateveryouwanttotest){
//
//}
}
}

Working with JFrames

hi I'm basically give up. Ok so this is what I am trying to do. So far I have written code to create a JFrame which contains a text field and combo box. The code is supposed to calculate the area of an input in the text field depending on what shape is selected from the combo box and output the result on the JFrame!
Here's is what the output should look like
And here is my code so far. it's a bit messed up but any help would be much appreciated. Thanks in advance
import javax.swing. *;
import java.awt.event. *;
import java.awt.FlowLayout;
import java.lang.Math;
public class AreaFrame3 extends JFrame
{
double Area;
double input;
public static void main(String[]args)
{
//Create array containing shapes
String[] shapes ={"(no shape selected)","Circle","Equilateral Triangle","Square"};
//Use combobox to create drop down menu
JComboBox comboBox=new JComboBox(shapes);
JLabel label1 = new JLabel("Select shape:");
JPanel panel1 = new JPanel(new FlowLayout()); //set frame layout
JLabel label2 = new JLabel("(select shape first)");
JTextField text = new JTextField(10); //create text field
text.setEnabled(false);
panel1.add(label1);
panel1.add(comboBox);
panel1.add(label2);
panel1.add(text);
JFrame frame=new JFrame("Area Calculator Window");//create a JFrame to put combobox
frame.setLayout(new FlowLayout()); //set layout
frame.add(panel1);
frame.add(text);
//JButton button = new JButton("GO"); //create GO button
//frame.add(button);
//set default close operation for JFrame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
//set JFrame ssize
frame.setSize(400,250);
//make JFrame visible. So we can see it
frame.setVisible(true);
// public void actionPerformed(ActionEvent e)
//{
}
public void AreaCalc()
{
JButton button = new JButton("GO"); //create GO button
frame.add(button);
button.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e)
{
int input = double.parseDouble(text.getText());
if(e.getSource() == button)
{
String shape = (String).comboBox.getSelectedItem();
if(shape == "(no shape selected)")
{
text.setEnabled(false);
}
else{
text.setEnabled(true);
}
if(input > 1 && shape == "Circle")
{
// label2.getText() = "Enter the radius of the circle: ";
Area = (Math.PI * (input * input));
}
}
else{}
}
}
);
}
}
I try to understand what you did here:
panel1.add(label1);
panel1.add(comboBox);
panel1.add(label2);
panel1.add(text); // <---
JFrame frame=new JFrame("Area Calculator Window");//create a JFrame to put combobox
frame.setLayout(new FlowLayout()); //set layout
frame.add(panel1);
frame.add(text); // <---
Especially frame.add(text); and panel1.add(text);. Don't add text to JFrame. Use JPanel.
Further,
public class AreaFrame3 extends Frame
Use public class AreaFrame3 extends JFrame so you don't need create additional JFrame:
JFrame frame=new JFrame("Area Calculator Window");
Something like:
super.setLayout(new FlowLayout()); //set layout
super.add(panel1);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.pack();
//set JFrame ssize
super.setSize(400,250);
//make JFrame visible. So we can see it
super.setVisible(true);
At last Ill give you some tamplate to start with (that will help you):
public class FrameExmpl extends JFrame{
private static final long serialVersionUID = 1L;
private JTabbedPane tabbedPane;
private JPanel topPanel;
private JTextField txtf_loadDS_;
public static int valueInt = 0; // responsible for Task status updating
public static Boolean isFinish = false;
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException{
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
FrameExmpl UI_L = new FrameExmpl();
UI_L.buildGUI();
UI_L.setVisible(true);
}
public void buildGUI(){
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
}
});
setSize(435, 225);
setLocation(285, 105);
setResizable(false);
topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);
txtf_loadDS_ = new JTextField();
txtf_loadDS_.setBounds(22, 22, 382, 25);
topPanel.add(txtf_loadDS_);
finishBuildGUI();
}
public void finishBuildGUI(){
tabbedPane = new JTabbedPane();
topPanel.add(tabbedPane, BorderLayout.CENTER);
}
}
There are multiple issues with this application such as extending from Frame rather than JFrame & attempting to assign an int from Double.parseDouble. I would recommend that you start again building a small but working application and incrementally add functionality, this way errors are easier to fix.

Categories

Resources