Java GUI paintcomponent (newbie) - java

Thanks for the help. The objects are all showing now.
But I ran into a new problem. Im trying to use a For loop to draw 10 copys of the same box with a little space in between so they don't just stack in the same position.
But for some reason they keep getting painted on top of eachother and in the center instead of starting at x = 20...
import java.awt.*;
import javax.swing.*;
public class CarWashPanel extends JPanel {
public int i;
public int x = 20;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
for (i=0; i < 10; i++){
g.fillRoundRect(x, 10, 50, 100, 55, 25);
x = x + 10;
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 150);
}
}
//
I'm trying to add graphics in my CarWashPanel class to I want to add to my GUI. I've read some tutorials and other questions but I can't figure out what i'm doing wrong.
The buttons and label that i've added to the GUI show up just fine but when I add something to my CarWashPanel it doesn't show up in the GUI.
I feel like I need to tell my GUI to add all elements from the CarWashPanel but I'm not sure how.
public class Main {
public static void main(String[] args) {
GUI g = new GUI();
}
}
import javax.swing.*;
import java.awt.*;
public class GUI extends JFrame {
private JTextField t1 = new JTextField(2);
private JLabel l1 = new JLabel("enter position");
private JButton b1 = new JButton("new customer");
private JButton b2 = new JButton("wash car");
public GUI() {
setDefaultCloseOperation (
JFrame.EXIT_ON_CLOSE );
add(l1);
add(t1);
add(b1);
add(b2);
setTitle("Carwash");
setSize(500, 200);
setVisible(true);
setLayout(new FlowLayout());
add(new CarWashPanel());
}
}
public class Carwash {
private boolean[] positions = new boolean[10];
private int washing = 10;
public void addCar(int p) {
positions[p] = true;
}
public void removeCar(int p) {
positions[p] = false;
}
public boolean[] getPositions() {
return positions;
}
public int getWashing() {
return washing;
}
}
import java.awt.*;
import javax.swing.*;
public class CarWashPanel extends JPanel {
public CarWashPanel(){
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.fillRoundRect(150, 50, 100, 100, 50, 25);
}
}

It's very often issue. You are calling setVisible before you are adding your components. Add your components on CarWashPanel, add CarWashPanel on JFrame and then call setVisible. Also, remove this line: setLayout(new FlowLayout()); - FlowLayout is default layout for JPanel (CarWashPanel in your case) and this makes it sufficient.
Your code should look something like this:
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(GUI::new);
}
public static class GUI extends JFrame {
private JTextField t1 = new JTextField(2);
private JLabel l1 = new JLabel("enter position");
private JButton b1 = new JButton("new customer");
private JButton b2 = new JButton("wash car");
CarWashPanel carWashPanel = new CarWashPanel();
public GUI() {
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
carWashPanel.add(l1);
carWashPanel.add(t1);
carWashPanel.add(b1);
carWashPanel.add(b2);
add(carWashPanel,BorderLayout.CENTER);
setTitle("Carwash");
pack();
setVisible(true);
}
}
public class Carwash {
private boolean[] positions = new boolean[10];
private int washing = 10;
public void addCar(int p) {
positions[p] = true;
}
public void removeCar(int p) {
positions[p] = false;
}
public boolean[] getPositions() {
return positions;
}
public int getWashing() {
return washing;
}
}
public static class CarWashPanel extends JPanel {
public CarWashPanel() {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.fillRoundRect(150, 50, 100, 100, 50, 25);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 200);
}
}
}
Other sidenotes:
Don't call setSize for JFrame, call pack. Rather override getPreferredSize for JPanel and return some dimensions.
Avoid extending your class with JFrame unless you want to define new methods or override existing ones.

If you don't need to add things dynamically the best thing to do is call setVisible(true) after you've added all your components.
However, if you want to add things after the frame is visible you can do so and then call the frame's revalidate() method to cause it to redraw.
Secondly I'd recommend you set the layout before you add any components.

Related

paint() doesn't work after panel and frame was added

I am making kind of my paint that creates shapes, it worked fine until I added layers(panel and frame), now the shapes aren't visible on button press I assume it is under the layers?i tried using paintComponent and revalidate etc and still couldn't get it to appear
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main2 extends JPanel implements ActionListener {
private static Square mySquare;
private static Circle myCircle;
private static Color myColor;
public boolean SquareCheck;
public boolean CircleCheck;
JButton buttonSquare;
JButton buttonCircle;
JFrame frame;
JPanel panel;
public void paint(Graphics g) {
if (SquareCheck) {
g.setColor(myColor);
g.fillRect(mySquare.x, mySquare.y, mySquare.width, mySquare.length);
} else if (CircleCheck) {
g.setColor(myColor);
g.fillOval(myCircle.x, myCircle.y, myCircle.width, myCircle.length);
}
}
public void start() {
frame = new JFrame();
panel = new JPanel();
//setLayout(new BorderLayout());
buttonSquare = new JButton("■");
buttonCircle = new JButton("●");
buttonSquare.setPreferredSize(new Dimension(200, 20));
buttonCircle.setPreferredSize(new Dimension(200, 20));
buttonCircle.addActionListener(this);
buttonSquare.addActionListener(this);
//add(buttonCircle, BorderLayout.NORTH);
//add(buttonSquare, BorderLayout.SOUTH);
JToggleButton red = new JToggleButton();
panel.add(buttonCircle);
panel.add(buttonSquare);
frame.add(panel);
frame.setSize(500, 500);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonSquare) {
SquareCheck = true;
} else if (e.getSource() == buttonCircle) {
CircleCheck = true;
}
repaint();
}
public static void main(String[] args) {
mySquare = new Square(30, 50, 50, 50);
myCircle = new Circle(60, 100, 50, 50);
myColor = Color.red;
Main2 x = new Main2();
x.start();
}
}
Basiclly the buttons changes the boolean then the repaint is called and based on the boolean it draws either a cirlce or a square,the code worked before adding frame and panel
Your paint method is a method of the Main2 class, but you never add a Main2 instance to the JFrame or to any component that goes into the JFrame, and so the Main2 instance will never be displayed, and the Swing painting manager will never call its paint method.
For starters, get rid of this variable, panel = new JPanel(); and every place you use panel, substitute this. This way you'll be working with a correct Main2 instance and adding it to the GUI.
Other issues:
You need to call the super's equivalent painting method in your override on its first line
Override paintComponent, not paint, and yes call super.paintComponenet(g); in this override
You will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.
For safety's sake, add the #Override annotation above any method that you think may be overriding a parent method (such as paint), to make sure that you are doing it correctly.
For example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
#SuppressWarnings("serial")
public class Main2 extends JPanel implements ActionListener {
private static Square mySquare;
private static Circle myCircle;
private static Color myColor;
private JToggleButton buttonSquare;
private JToggleButton buttonCircle;
JFrame frame;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buttonSquare.isSelected()) {
g.setColor(myColor);
g.fillRect(mySquare.x, mySquare.y, mySquare.width, mySquare.length);
}
if (buttonCircle.isSelected()) {
g.setColor(myColor);
g.fillOval(myCircle.x, myCircle.y, myCircle.width, myCircle.length);
}
}
public Main2() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buttonSquare = new JToggleButton("■");
buttonCircle = new JToggleButton("●");
buttonCircle.addActionListener(this);
buttonSquare.addActionListener(this);
this.add(buttonCircle);
this.add(buttonSquare);
frame.add(this);
frame.setSize(500, 500);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
mySquare = new Square(30, 50, 50, 50);
myCircle = new Circle(60, 100, 50, 50);
myColor = Color.red;
new Main2();
}
}
class MyShape {
public int x, y, width, length;
public MyShape(int x, int y, int width, int length) {
this.x = x;
this.y = y;
this.width = width;
this.length = length;
}
}

JScrollPane not show content after scroll in swing

Hi I need to display a large content(its graphical data) of data in single, so I tried following code.
canvas.setPreferredSize(new Dimension(3000, 300));
canvas.setBackground(Color.blue);
JScrollPane jsp = new JScrollPane(canvas);
setPreferredSize(new Dimension(600, 500));
setLayout(new GridLayout(1, 0, 5, 0));
jsp.getHorizontalScrollBar().addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
System.out.println(e.getValue());
repaint();
}
});
add(jsp);
this is my MyCanvas class
class MyCanvas extends Canvas {
#Override
public void paint(Graphics g) {
super.paint(g);
System.out.println("paint");
g.setColor(Color.YELLOW);
for (int i = 0; i < 100; i++) {
g.drawString(""+i, i*30, 100);
// g.drawLine(10, 10, 20, 20);
}
}
}
but problem is that when I am scrolling window I cannot see full content as I expected it should print 100 numbers but not printed actually, can any one correct me?
see the result here
I recommend that you avoid mixing AWT and Swing components together (or if you absolutely must do this, then you have to make sure you understand the pitfalls and fully jump through all the necessary hoops.
Myself, I'd extend JPanel, I'd be sure that its preferredSize was where I want it, since this will determine how big it will be within the JScrollPane.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyScrollExample extends JPanel {
private static final int MAX = 100;
private MyPanel myPanel = new MyPanel(MAX);
public MyScrollExample() {
JScrollPane scrollPane = new JScrollPane(myPanel);
scrollPane.getViewport().setPreferredSize(new Dimension(600, 200));
add(scrollPane);
}
private static void createAndShowGui() {
MyScrollExample mainPanel = new MyScrollExample();
JFrame frame = new JFrame("MyScrollExample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class MyPanel extends JPanel {
private static final Color BG = Color.BLUE;
private static final Color FG = Color.YELLOW;
private static final int WIDTH_GAP = 30;
private static final int HEIGHT_GAP = 100;
private int max;
public MyPanel(int max) {
setBackground(BG);
this.max = max;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(FG);
for (int i = 0; i < max; i++) {
g.drawString("" + i, i * WIDTH_GAP, HEIGHT_GAP);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
int w = (WIDTH_GAP + 1) * max;
int h = HEIGHT_GAP * 3;
return new Dimension(w, h);
}
}

Paint component disappear after resizing window or use scrollbar

I write a program to draw 50 rectangles after clicking OK button. But I don't uderstand why it disappears after resizing window, using scrollbar or clicking on OK again.
Here's my code (I have two classes: drawingPanel and Main)
drawingPanel.java:
import javax.swing.*;
import java.awt.*;
public class drawingPanel extends JPanel
{
public boolean drawIt = false;
public int x = 140,y = 0;
public void paintIt()
{
drawIt = true;
repaint();
}
public void paintComponent(Graphics g)
{
if (drawIt == true)
{
super.paintComponent(g);
for (int i = 1; i <= 50; i++)
{
g.fillRect(x, y, 50, 50);
y += 70;
}
}
}
}
Main.java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JFrame
{
private drawingPanel draw = new drawingPanel();
private JPanel controlPanel = new JPanel();
private JButton ok = new JButton("OK");
private JScrollPane scroll = new JScrollPane(draw);
public Main()
{
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Demo");
setLayout(new BorderLayout());
controlPanel.setLayout(new FlowLayout());
controlPanel.add(ok);
ok.addActionListener(new okListener());
draw.setPreferredSize(new Dimension(100,1000));
add(controlPanel, BorderLayout.NORTH);
add(scroll,BorderLayout.CENTER);
setVisible(true);
}
private class okListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
draw.paintIt();
}
}
public static void main(String[] args)
{
new Main();
}
}
Please help me, thanks in advance.
Re-initialize x and y fields within your paint component method like so that they won't keep increasing out of view each time the JPanel is drawn:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // move this here
if (drawIt) {
x = 140; // add these guys
y = 0;
for (int i = 1; i <= 50; i++) {
// .... etc....
and things should draw OK.
Other minor issues not related to your question:
The super's painting method should always be called, so pull that out of the if block.
Rename your drawingPanel class to be DrawingPanel so that it conforms to Java conventions.
Better to change if (drawIt == true) { to the more simple if (drawIt) {
Minor quibble: paintComponent should be protected, not public.

How can I move my JButton to the right side of my JPanel?

For a programming class, I was asked to create a replica of an iCalendar app. I'm using JAVA to code it, and JFrame and JPanel to draw it. Here is a SSCCEE of my problem:
import java.awt.*;
import javax.swing.*;
public class Mainie extends JFrame {
private JButton back = new TriangleButton(true),
front = new TriangleButton(false);
public Mainie(){
super();
setSize(800, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel months = new JPanel();
final JPanel days = new JPanel();
JLabel one = new JLabel("Hallo");
one.setHorizontalAlignment(JLabel.CENTER);
back.setHorizontalAlignment(SwingConstants.LEFT);
front.setHorizontalAlignment(SwingConstants.RIGHT);
months.setLayout(new BorderLayout());
months.add(back, BorderLayout.WEST);
months.add(one, BorderLayout.CENTER);
months.add(front, BorderLayout.EAST);
days.setLayout(new GridLayout(1,1));
days.add(new JButton("Meister Camickr"));
months.setAlignmentX(CENTER_ALIGNMENT);
add(months, BorderLayout.NORTH);
add(days, BorderLayout.CENTER);
setVisible(true);
}
public static void main(String[] args){
new Mainie();
}
}
class TriangleButton extends JButton {
private Shape triangle = createTriangle();
public void paintBorder(Graphics g) {
((Graphics2D) g).draw(triangle);
}
public void paintComponent(Graphics g) {
((Graphics2D) g).fill(triangle);
}
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
public boolean contains(int x, int y) {
return triangle.contains(x, y);
}
public TriangleButton(boolean pointLeft) {
super();
if (!pointLeft)
this.triangle = createRTriangle();
}
private Shape createTriangle() {
Polygon p = new Polygon();
p.addPoint(0, 20);
p.addPoint(20, 0);
p.addPoint(20, 40);
return p;
}
private Shape createRTriangle() {
Polygon p = new Polygon();
p.addPoint(20, 20);
p.addPoint(0, 0);
p.addPoint(0, 40);
return p;
}
}
If you compile this, you can see that my JButton is too far to the left. How can I move it to the right?
The basic problem with those buttons is that the button is very wide and the visual indicator is to the left. Put a border around them and it becomes obvious.
OTOH I would use an entirely different approach, using text for the buttons and a factory method to make them look as expected.
import java.awt.*;
import javax.swing.*;
public class CustomPointerButtons {
JPanel ui;
CustomPointerButtons() {
initUI();
}
private final void initUI() {
ui = new JPanel(new BorderLayout(4, 4));
JPanel topPanel = new JPanel(new BorderLayout(4, 4));
ui.add(topPanel, BorderLayout.PAGE_START);
ui.add(new JButton("Mister Mix")); //will default to CENTER
topPanel.add(new JLabel("Blah, Blah.."));
JButton back = getMinimalButton(new String(Character.toChars(9668)));
topPanel.add(back, BorderLayout.LINE_START);
JButton forward = getMinimalButton(new String(Character.toChars(9658)));
topPanel.add(forward, BorderLayout.LINE_END);
}
public JButton getMinimalButton(String text) {
JButton b = new JButton(text);
b.setFont(b.getFont().deriveFont(40f));
b.setMargin(new Insets(0,0,0,0));
b.setContentAreaFilled(false);
b.setBorder(null);
return b;
}
public final JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Custom Pointer Buttons");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setContentPane(new CustomPointerButtons().getUI());
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
The problem is your random preferred size doesn't represent the real size of the triangle.
You can do something like:
public Dimension getPreferredSize() {
//return new Dimension(200, 100);
Rectangle bounds = triangle.getBounds();
return new Dimension(bounds.width, bounds.height);
}
You still have other problems because you will still have painting artifacts. You should always invoke super.paintComponent() when you override the method:
public void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).fill(triangle);
}
However, this still doesn't really work because the button does other painting as well. So the real problem is that you should not be trying to do custom painting of the button. What you really want to do is custom painting of an Icon. Then you can just add the Icon to the button.
You may want to check out Playing With Shapes. You can use the ShapeIcon class to create your icons. This will provide a more flexible solution since I'm guess all shapes are not represented by an ascii character.

JPanel repaint issue

I have a JFrame which contains 2 JPanel subclass and 2 JLabel in BorderLayout. One of the JPanel contains JButtons and the other is used for displaying graphics. The JLabels are in north and south, the button JPanel in the west and the display JPanel in center.
The display JPanel requires constant refresh, so i invoke its repaint() method via the action event generated by swing timer. I also override its paintComponent() method to do my drawings.
Instead of displaying what i have drawn, the "content of the JFrame" is being drawn onto the display JPanel. I am aware that i can simply "clear" the display JPanel by using g.fillRect() or super.paintComponent() before doing my drawings.
I am just curious why this happens.
i'm using jdk 1.6u27. below is my code:
package test;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class Main {
public static void main(String[] args) {
Simulation sim = new Simulation();
}
}
class Simulation extends JFrame {
public JLabel state;
private JLabel id;
private ButtonPanel control;
private Display display;
public Simulation() {
id = new JLabel("Test");
state = new JLabel("Test");
control = new ButtonPanel();
display = new Display(this);
this.setLayout(new BorderLayout());
this.add(id, BorderLayout.NORTH);
this.add(control, BorderLayout.WEST);
this.add(display, BorderLayout.CENTER);
this.add(state, BorderLayout.SOUTH);
this.setSize(500, 600);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public ButtonPanel getControl() {
return this.control;
}
}
class ButtonPanel extends JPanel implements ActionListener {
public JButton b[] = new JButton[8];
public boolean bp[] = new boolean[8];
public ButtonPanel() {
this.setLayout(new GridLayout(8, 1));
for (int i = 0; i < b.length; i++) {
b[i] = new JButton(""+i);
b[i].addActionListener(this);
bp[i] = false;
this.add(b[i]);
}
}
public void actionPerformed(ActionEvent e) {
//do something
}
}
class Display extends JPanel implements ActionListener {
private Timer tm;
private int yco;
private Simulation sim;
public Display(Simulation sim) {
tm = new Timer(100, this);
tm.start();
yco = 0;
this.sim = sim;
}
#Override
public void paintComponent(Graphics g) {
//draw something
g.drawLine(0, yco, 100, 100);
}
public void actionPerformed(ActionEvent e) {
yco ++;
this.repaint();
}
}
Without super.paintComponent(g), the result depends on your platform's default for the opacity property of the JPanel UI delegate, PanelUI. Mine happens to be true, but you can experiment on your platform, as suggested below.
Addendum: "If you do not honor the opaque property you will likely see visual artifacts."—paintComponent(). The artifact you observe will vary by platform, but it is not atypical. In effect, you are breaking the promise to draw every pixel, and you see whatever is left over in some buffer.
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Simulation sim = new Simulation();
}
});
}
}
class Simulation extends JFrame {
public JCheckBox state;
private JLabel id;
private ButtonPanel control;
private Display display;
public Simulation() {
id = new JLabel("Test");
state = new JCheckBox("Opaque");
control = new ButtonPanel();
display = new Display(this);
this.setLayout(new BorderLayout());
this.add(id, BorderLayout.NORTH);
this.add(control, BorderLayout.WEST);
this.add(display, BorderLayout.CENTER);
this.add(state, BorderLayout.SOUTH);
state.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
display.setOpaque(e.getStateChange() == ItemEvent.SELECTED);
}
});
state.setSelected(true);
this.pack();
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public ButtonPanel getControl() {
return this.control;
}
}
class ButtonPanel extends JPanel {
private static final int N = 8;
private List<JToggleButton> list = new ArrayList<JToggleButton>(N);
public ButtonPanel() {
this.setLayout(new GridLayout(0, 1));
for (int i = 0; i < N; i++) {
final JToggleButton b = new JToggleButton(String.valueOf(i));
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//System.out.println(b.isSelected());
}
});
list.add(b);
this.add(b);
}
}
}
class Display extends JPanel {
private Simulation sim;
private Timer tm;
private int yco;
public Display(Simulation sim) {
this.setPreferredSize(new Dimension(320, 320));
this.setOpaque(true);
this.sim = sim;
tm = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yco++;
repaint();
}
});
tm.start();
}
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g);
g.drawLine(0, yco, getWidth() / 2, getHeight() / 2);
}
}

Categories