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.
Related
I am trying to associate Ellipse2D objects with a cell on a JTable. Specifically if the user clicks on one Ellipse2D then it would highlight a cell on the JTable and accept input. I need to do this in a way that allows the user to associate a string with each Ellipse.
Is what I am trying to do possible and if so how can I do this?
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
public class SelfContainedExample extends JPanel {
private List<Shape> shapes = new ArrayList<>();
public static void main(String[] args)
{
EventQueue.invokeLater(() -> createAndShowGUI());
}
public SelfContainedExample()
{
//Circle of Radios
shapes.add(new Ellipse2D.Double(250, 100, 20, 20));
shapes.add(new Ellipse2D.Double(160, 100, 20, 20));
shapes.add(new Ellipse2D.Double(70, 100, 20, 20));
shapes.add(new Ellipse2D.Double(70, 160, 20, 20));
shapes.add(new Ellipse2D.Double(160, 160, 20, 20));
shapes.add(new Ellipse2D.Double(250, 160, 20, 20));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setColor(Color.BLACK);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
shapes.forEach(g2d::fill);
g2d.dispose();
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Example");
JPanel panel = new JPanel();
Object[][] data = {{"1_1", "1_2", "1_3"},
{"2_1", "2_2", "2_3"}};
Object[] columnNames = {"1", "2", "3"};
JTable jtable = new JTable(data, columnNames);
panel.setLayout(new BorderLayout());
panel.add(new SelfContainedExample(), BorderLayout.CENTER);
panel.add(jtable, BorderLayout.SOUTH);
panel.setOpaque(true);
panel.setVisible(true);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,400);
frame.setLocationByPlatform( false );
frame.setLocationRelativeTo( null );
frame.setContentPane( panel );
frame.setVisible( true );
}
}
Well maybe you start by creating a HashMap where the key is the ellipse and the value is a Point, where the Point would represent the row/column of the ellipse in the table.
//shapes.add(new Ellipse2D.Double(250, 100, 20, 20));
Ellipse2D.Double ellipse = new Ellipse2D.Double(...);
shapes.add(ellipse);
shapesMap.put(ellipse, new Point(0, 0);
the user clicks on one Ellipse2D then it would highlight a cell on the JTable and accept input.
So then you need to add a MouseListener to your panel and handle the mousePressed() event. The code would need to iterate through the List to find the ellipse that contains the point. Then you can start editing on the cell. Maybe something like:
public void mousePressed(MouseEvent e)
{
for (Ellipse2D.Double ellipse: shapes)
{
if (ellipse.contains(e.getPoint())
{
table.requestFocusInWindow();
Point p = shapesMap.get(ellipse);
table.editCellAt(p.x, p.y);
break;
}
}
}
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());
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.
I know there is a way to extend a JLabel to paint 3D borders and a way to paint round borders, but how do you get both? Here is my code
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRoundRect(0, 0, getWidth()-1, getHeight()-1, 25, 25);
g.fill3DRect(10, 10, 30, 30, true);
super.paintComponent(g);
Use LineBorder with rounded corners or a variant of the TextBubbleBorder.
You refer this code to create Round Corner JLabel:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class RoundedLineBorder extends JPanel {
public RoundedLineBorder() {
super(true);
setLayout(new BorderLayout());
JLabel label = new JLabel("Rounded Corners");
label.setHorizontalAlignment(JLabel.CENTER);
LineBorder line = new LineBorder(Color.blue, 2, true);
label.setBorder(line);
add(label, BorderLayout.CENTER);
}
public static void main(String s[]) {
JFrame frame = new JFrame("Rounded Line Border");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 200);
frame.setContentPane(new RoundedLineBorder());
frame.setVisible(true);
}
}
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);
}
}