Having Trouble Drawing With An ArrayList - java

I am having a little bit of trouble drawing components from an ArrayList I have created. If I screw around with it, I can either get the first element or the second element and if I am lucky, neither, to show up!
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class FaceShortCode {
ArrayList<CreateCircles> faceCircles = new ArrayList<CreateCircles>();
public FaceShortCode() {
JFrame main = new JFrame();
main.setTitle("Face Frame");
main.setSize(new Dimension(600, 600));
main.setLocationRelativeTo(null);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
Container c = main.getContentPane();
// c.setLayout(null);
faceCircles.add(0, new CreateCircles(100, 50, 400, 350, Color.red));
faceCircles.add(1, new CreateCircles(200, 100, 65, 65, Color.black));
c.add(faceCircles.get(0));
c.add(faceCircles.get(1));
}
class CreateCircles extends JComponent {
double x, y, width, height;
Color myColor;
public CreateCircles(double x, double y, double width, double height,
Color myColor) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.myColor = myColor;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle = new Ellipse2D.Double(x, y, width, height);
g2.setColor(myColor);
g2.fill(circle);
}
}
static class Run {
public static void main(String[] args) {
new FaceShortCode();
}
}
}
I have tried throwing in a main.repaint() after every addition to the Container c,tried a repaint() in my painting method but nothing seems to be working. Is there any were else to put a repaint() that I am just missing?

You need to put all your logic before you setVisible() or else your frame will become visible before the logic is performed.
public FaceShortCode() {
Container c = main.getContentPane();
// c.setLayout(null);
faceCircles.add(0, new CreateCircles(100, 50, 400, 350, Color.red));
faceCircles.add(1, new CreateCircles(200, 100, 65, 65, Color.black));
c.add(faceCircles.get(0));
c.add(faceCircles.get(1));
JFrame main = new JFrame();
main.setTitle("Face Frame");
main.setSize(new Dimension(600, 600));
main.setLocationRelativeTo(null);
main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main.setVisible(true);
}
Consider making FaceShortCode extends JFrame.
public class FaceShortCode extends JFrame {
private ArrayList<CreateCircles> faceCircles = new ArrayList<CreateCircles>();
public FaceShortCode {
setLayout(new Girdlayout(1, 2));
faceCircles.add(0, new CreateCircles(100, 50, 400, 350, Color.red));
faceCircles.add(1, new CreateCircles(200, 100, 65, 65, Color.black));
add(faceCircles.get(0));
add(faceCircles.get(1))
setTitle("Face Frame");
setSize(new Dimension(600, 600));
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
}

Your design is a bit confusing. There is no need for an ArrayList. When you use components you can just add the components directly to the panel. Normally you would only use an ArrayList when you are doing custom painting by painting Objects that are not components, for example when you want to paint a Shape. For an example of this approach take a look at Custom Painting Approaches.
When using components, a component needs a size and location in order to be painted automatically by Swing. Normally you would let a layout manager determine these properties. In your case you are doing random placement of your components so you would need to use a null layout and then set the size/location of each component.
So what you need to do is change how you paint your custom component. All painting should be done at location (0, 0) of your component. Then you would set the location of the component to be the x/y value. This means the component will be painted at the x/y location relative to the panel you add the component to. Then you need to set the size of your component which would be the width/height.

Related

Change JFrame display when JTabbedPane tab is selected

I am trying to make a JFrame display a different JPanel when a specific tab is selected. I have tried adding code to make it add the new panel based on which tab index is selected.
Where am I going wrong with this? What do I need to add to make it work? Thanks.
EDIT
Here is my solved SSCCE:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
public class MainPanel {
private static JTabbedPane tabbedPane = new JTabbedPane();
private static JFrame frame = new JFrame("Testing");
public static void main(String[] args) {
EventQueue.invokeLater(MainPanel::createAndShowGUI);
}
protected static void createAndShowGUI()
{
DrawGraphics drawGraphics = new DrawGraphics();
DrawDifferentGraphics drawDifferentGraphics = new DrawDifferentGraphics();
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(tabbedPane, BorderLayout.WEST);
tabbedPane.addTab("CFG", null);
tabbedPane.addTab("CNX", null);
frame.add(drawGraphics);
tabbedPane.addChangeListener(e -> {
if (tabbedPane.getSelectedIndex() == 0) {
frame.remove(drawDifferentGraphics);
frame.add(drawGraphics);
frame.validate();
frame.repaint();
}
if (tabbedPane.getSelectedIndex() == 1) {
frame.remove(drawGraphics);
frame.add(drawDifferentGraphics);
frame.validate();
frame.repaint();
}});
frame.setLocationByPlatform(true);
frame.setSize(400, 400);
frame.setVisible(true);
}
}
class DrawGraphics extends JPanel {
private ArrayList<Shape> shapes = new ArrayList<Shape>();
public DrawGraphics() {
setLayout(new BorderLayout());
shapes.add(new Ellipse2D.Double(10, 10, 20, 20));
shapes.add(new Ellipse2D.Double(10, 30, 20, 20));
shapes.add(new Ellipse2D.Double(10, 50, 20, 20));
shapes.add(new Ellipse2D.Double(10, 70, 20, 20));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.BLUE);
shapes.forEach(g2d::fill);
g2d.dispose();
}
}
class DrawDifferentGraphics extends JPanel {
private ArrayList<Shape> shapes = new ArrayList<Shape>();
public DrawDifferentGraphics() {
setLayout(new BorderLayout());
shapes.add(new Rectangle2D.Double(10, 10, 10, 10));
shapes.add(new Rectangle2D.Double(10, 30, 10, 10));
shapes.add(new Rectangle2D.Double(10, 50, 10, 10));
shapes.add(new Rectangle2D.Double(10, 70, 10, 10));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.RED);
shapes.forEach(g2d::fill);
g2d.dispose();
}
}
I want to display the graphics on the panel next to the tabbedPane.
Read the section from the Swing tutorial on How to Write a ChangeListener.
You will be notified when a tab has been clicked. You then get the selected tab and add the panel to the frame.
So basically your if (tabbedPane.getSelectedIndex() == 0) logic would be moved to the ChangeListener.
Or instead of having a bunch of "if statement" you could have a Map of Integer/JPanel values. Then you just get the index and get the panel from the Map.
Once you add the panel to the frame you then need to revalidate() and repaint() the frame.
Edit:
Actually the above suggestion is not complete. You can't just keep adding panels to the frame. The CENTER area of the BorderLayout should only contain a single component, otherwise you can get painting problems.
This can be demonstrated by clicking on the unselected tab, and then resize the frame. The original panel will be displayed.
You need to do one of the following:
Use a CardLayout (read the tutorial if you haven't used layout before) on a penel in the CENTER of the BordreLayout. So in this case the panel using the CardLayout is the only component in the CENTER and then it manages the panel that is displayed in the CardLayout. So your ChangeListener would need to identify the card to be displayed. You could set the card identifier to be the text of the selected tab. So
Remove the current panel BEFORE adding the new panel. In this case there is only a single panel in the CENTER so painting is as expected.

Panel added to BorderLayout.CENTER does not occupy remaining space

I am currently learning to program GUI in Java, and I have a problem where the CENTER component does not occupy the remaining space in the frame. From what I've read BorderLayout will grant components in north/south their preferred height and the stretch it out to the edges, and west/east will be the opposite. The center component will then get whatever space is left. What I am trying to do is to create a simple window with a panel in the north region, and a panel in the center region. I give each their own background color so I can easily see the space they are given. However, instead of getting a window with a yellow top bar and the remaining space being occupied by the magenta panel, I get this.
The top panel is just a regular JPanel, but the center panel is a class extending JPanel which overrides paintComponent and fills the panel with a color. If I hardcode in a bigger area in the fillRect() it will actually fill the window. So I suspect there's something wrong happening when I call getHeight() and getWidth in the method. It also might be worth mentioning that the dawPanel always will paint a perfect square, if I resize the window into a rectangle longer on the Y-aksis the gap will appear on the bottom instead.
So my question is, how can I get the component added to the Borderlayout.CENTER to occupy all remaining space in the frame.contentPane()?
package oblig1;
import java.awt.*;
import javax.swing.*;
public class Oblig1
{
JFrame frame;
JPanel infoPanel;
DrawingPanel drawPanel;
public static void main(String[] args)
{
Oblig1 game = new Oblig1();
}
public Oblig1()
{
frame = new JFrame("Parachute Game");
frame.setSize(860, 640);
infoPanel = new JPanel();
drawPanel = new DrawingPanel();
infoPanel.setBackground(Color.yellow);
infoPanel.setPreferredSize(new Dimension(840, 20));
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.X_AXIS));
drawPanel.setPreferredSize(new Dimension(840, 620));
frame.getContentPane().add(infoPanel, BorderLayout.NORTH);
frame.getContentPane().add(drawPanel, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setResizable(false);
frame.setVisible(true);
}
//This class represents the panel that paints all animated parts of the game
public class DrawingPanel extends JPanel
{
public DrawingPanel()
{
setDoubleBuffered(true);
}
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.MAGENTA);
g2d.fillRect(0, 0, drawPanel.getHeight(), drawPanel.getWidth());
}
}
}
issue came from two code lines (and one code line missed
infoPanel.setPreferredSize(new Dimension(840, 20));
and
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.X_AXIS));
BoxLayout required Min, Max and PreferredSize, otherwise missed Dimensions collided with another PreferredSize, in this case (infoPanel.setPreferredSize(new Dimension(840, 20)); ) that is laid in JFrame that uses BorderLayout
remove infoPanel.setPreferredSize(new Dimension(840, 20));, or its widht must be less than PreferredSize used for JPanel
painting in Swing by default never returns PreferredSize correctly back to the container, you jave to override getPreferredSize, for BoxLayout min, max and preferred size
use JFrame.pack() instead of sizing for min,max and preferredSize directly to the JComponents or container, nor to setSize for JFrame
not true at all, to see my EDIT --> use another LayoutManager for JPanel to reduce funny issue with painting if is JFrame resized
your paintComponent missed important code line super.paintComponent(g);
e.g.
import java.awt.*;
import javax.swing.*;
public class Oblig1 {
JFrame frame;
JPanel infoPanel;
DrawingPanel drawPanel;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Oblig1();
}
});
}
public Oblig1() {
frame = new JFrame("Parachute Game");
//frame.setSize(860, 640);
infoPanel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(20, 20);
}
};
drawPanel = new DrawingPanel();
infoPanel.setBackground(Color.yellow);
//drawPanel.setPreferredSize(new Dimension(840, 20));
infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.X_AXIS));
//drawPanel.setPreferredSize(new Dimension(840, 620));
frame.getContentPane().add(infoPanel, BorderLayout.NORTH);
frame.getContentPane().add(drawPanel, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
//This class represents the panel that paints all animated parts of the game
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
setDoubleBuffered(true);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(300, 300);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
public Dimension getMaximumSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.MAGENTA);
//g2d.fillRect(0, 0, getHeight(), getWidth());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
}
EDIT
to my point, is simple wrong
use another LayoutManager for JPanel to reduce funny issue with painting if is JFrame resized
there are wrong, reversed parameters for Height and Width, wrong code line g2d.fillRect(0, 0, getHeight(), getWidth()); should be g2d.fillRect(0, 0, getWidth(), getHeight());

Why isn't a painting drawn to the screen?

I have this simple code which is supposed to have three panels and draws and oval at the top left corner of each panel
public class main1 extends JPanel {
public main1() {
// TODO Auto-generated constructor stub
this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
JPanel1 panel1 = new JPanel1(Color.YELLOW);
panel1.setBackground(Color.black);
JPanel1 panel2 = new JPanel1(Color.red);
panel2.setBackground(Color.blue);
JPanel1 panel3 = new JPanel1(Color.pink);
panel3.setBackground(Color.green);
this.add(panel1);
this.add(panel2);
this.add(panel3);
}
class JPanel1 extends JPanel{
Color c;
public JPanel1(Color c) {
this.c = c;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println(this.getBounds().x);
g.setColor(c);
g.drawOval(this.getBounds().x, this.getBounds().y, 200, 200);
}
}
public static void main(String args[]) {
JFrame f = new JFrame("Two Panels");
f.setContentPane(new main1());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setVisible(true);
}
}
however, it only seems to draw the first oval of the first panel and ignores the rest.
can somebody explain. What am I doing wrong?
Do not use getBounds() as it gives the component location relative to its parent. Use panel's coordinates and its width and height instead. In your example you are painting outside the boundaries of the panels. For example use this to draw an oval:
g.drawOval(0, 0, getWidth(), getHeight());
Some side notes:
Do not call setSize(), override panel's getPreferredSize() and pack() the frame. For example:
public Dimension getPreferredSize(){return new Dimension(400, 400);}
Then, add frame.pack(); before making the frame visible.
See Java Naming Conventions.
See Performing Custom Painting and Painting in AWT and Swing for more information.

Event handling in Java not working as expected

I cannot get the code linked below to do exactly what I want it to do.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Gui {
static JFrame frame;
static JLabel label;
public static void main (String[] args) {
Gui gui = new Gui();
gui.go();
}
public void go () {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,300);
frame.setVisible(true);
MyDrawPanel panel = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, panel);
label = new JLabel("I'm a label");
frame.getContentPane().add(BorderLayout.WEST, label);
JButton colorButton = new JButton("Change Colors");
ColorButtonListener colorButtonListener = new ColorButtonListener();
colorButton.addActionListener(colorButtonListener);
frame.getContentPane().add(BorderLayout.SOUTH, colorButton);
JButton labelButton = new JButton("Change Label");
LabelButtonListener labelButtonListener = new LabelButtonListener();
labelButton.addActionListener(labelButtonListener);
frame.getContentPane().add(BorderLayout.EAST, labelButton);
}
}
class ColorButtonListener implements ActionListener {
JFrame frame = Gui.frame;
public void actionPerformed (ActionEvent event) {
frame.repaint();
}
}
class LabelButtonListener implements ActionListener {
JLabel label = Gui.label;
public void actionPerformed (ActionEvent event) {
if (label.getText() == "That hurt") {
label.setText("I'm a label");
} else {
label.setText("That hurt");
}
}
}
class MyDrawPanel extends JPanel {
public void paintComponent (Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
Color startColor = new Color(red, green, blue);
red = (int) (Math.random() * 256);
green = (int) (Math.random() * 256);
blue = (int) (Math.random() * 256);
Color endColor = new Color(red, green, blue);
GradientPaint gradient = new GradientPaint(70, 70, startColor, 150, 150, endColor);
g2d.setPaint(gradient);
g2d.fillOval(0, 0, this.getWidth(), this.getHeight());
}
}
There is a panel class used to draw a circle and then the panel is positioned in the center region of the frame.
A label is positioned in the west region of the frame, two buttons colorButton(positioned south) and labelButton(positioned east) should control the circle and the label respectively. 2 classes ColorButtonListener and LabelButtonListener implement the ActionListener interface and are registered with the colorButton and labelButton respectively. The color button when clicked should paint a circle with random colors and the label button when clicked should toggle between the text "I'm a label" and "That hurt".
Now, the issue I am having is with the label button. When clicking it, it not only changes the text(as expected), but also redraws the circle. This button should not be redrawing the circle. The color button works as expected.
The problem is you don't control when a repaint might occur. Instead of changing the color every time paintComponent is called, which you can't control, change the color only when you want to and reference from within the paintComponent method, for example
class MyDrawPanel extends JPanel {
private Color startColor;
private Color endColor;
// And setters or some other way
// to randomise the colors
public void paintComponent (Graphics g) {
Graphics2D g2d = (Graphics2D) g;
GradientPaint gradient = new GradientPaint(70, 70, startColor, 150, 150, endColor);
g2d.setPaint(gradient);
g2d.fillOval(0, 0, this.getWidth(), this.getHeight());
}
}
Check out Painting in AWT and Swing for more details
This is because setText() methods internally calls repaint() if new text is not same as old text. So results in color change whenever you click that button too.

how to make java JPanel and graphics2d transparent?

Well the title is quite self explanatory. I want to build two panels one on-top of each other in layers using java. I want the top layer to contain a JPanel which will contain a graphics2d object. I'd like both the JPanel and graphics2d to have transparent background (I still want the content drawn by the graphics2d visible). Does anyone have an idea how it can be done?
Call setOpaque(false) on the JPanel - that will not paint the JPanel's background.
Depending on what method you're overriding to get at the Graphics2D (JPanel's don't contain a Graphics2D object like a component - a Graphics2D object is used to paint the JPanel) - if it's paintComponent() you should read the JavaDocs for JComponent - and call super.paintComponent(g) first so that opacity is honored - and then do the rest of your painting.
Working example:
package com.stackoverflow.opaque;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class OpaqueExample extends JFrame {
private JLayeredPane layers;
private JPanel up, down;
private JButton toggleOpaque;
public OpaqueExample() {
layers = new JLayeredPane();
down = new JPanel();
down.setBackground(Color.GREEN);
down.setBounds(100, 100, 200, 200);
layers.add(down, new Integer(1));
up = new JPanel() {
public void paintComponent(Graphics og) {
super.paintComponent(og);
Graphics2D g = (Graphics2D)og;
GradientPaint gradient = new GradientPaint(0, 0, Color.BLUE, 10, 0,
Color.WHITE, true );
Polygon poly = new Polygon();
poly.addPoint(10, 10);
poly.addPoint(100, 50);
poly.addPoint(190, 10);
poly.addPoint(150, 100);
poly.addPoint(190, 190);
poly.addPoint(100, 150);
poly.addPoint(10, 190);
poly.addPoint(50, 100);
poly.addPoint(10, 10);
g.setPaint(gradient);
g.fill(poly);
g.setPaint(Color.BLACK);
g.draw(poly);
}
};
up.setBackground(Color.RED);
up.setBounds(150, 150, 200, 200);
layers.add(up, new Integer(2));
getContentPane().add(layers, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
toggleOpaque = new JButton("Toggle Opaque");
toggleOpaque.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
up.setOpaque(!up.isOpaque());
layers.repaint();
}
});
buttonPanel.add(toggleOpaque);
getContentPane().add(buttonPanel, BorderLayout.EAST);
}
public static void main(String[] args) {
JFrame f = new OpaqueExample();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}

Categories