I'm trying to dynamically add some components to a JPanel, but unfortunatelly they don't appear. I only see the ones added in the constuctor.
Updated version (adding a new JPanel, where all the components will be):
public class View extends JPanel {
JPanel panel = new JPanel();
JLabel label;
JLabel labels[];
JButton b1 = new JButton("OK");
public View() {
this.setLayout(new FlowLayout());
this.add(panel); // adding a new JPanel
label = new JLabel("My label");
panel.add(label); // adding label to the new panel (this one works)
}
public void showLabels() {
System.out.println("function showLabels called");
labels = new JLabel[5];
for (int i = 0; i < 5; i++) {
labels[i] = new JLabel("Label: " + i);
panel.add(labels[i]); // this one doesn't work
}
panel.add(b1); // this one doesn't work, too
this.validate(); // validating this class (parent container)
panel.validate(); // validating the panel, where all the components are
}
}
Unfortunatelly nothing changed.
Call validate() on the parent container, as shown in the Nested Layout Example. Those labels on the lower left are added dynamically. Note that calling pack() might cause the size of the GUI to change, whereas calling validate() won't. If you need to get the GUI to resize - call pack(), else call validate().
Related
I'm creating a program that features a grid of 12 JPanels. When the "add image" button is pressed, an image appears in the first JPanel in the grid and a counter is incremented by one. From then onwards, every time the "add image" is clicked again, an image would be added to the next JPanel. For some reason, the button only adds an image to the first JPanel and then stops working. Here's the code I've got so far.
public class ImageGrid extends JFrame {
static JPanel[] imageSpaces = new JPanel[12];
int imageCounter = 0;
ImageGrid() {
this.setTitle("Image Grid");
setSize(750, 750);
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel p3 = new JPanel();
p3.setLayout(new GridLayout(3, 4, 10, 5));
p3.setBackground(Color.WHITE);
p3.setOpaque(true);
p3.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
for (int j = 0; j < imageSpaces.length; j++) {
imageSpaces[j] = setImageSpace();
p3.add(imageSpaces[j]);
}
MyButtonPanel p1 = new MyButtonPanel();
add(p1, BorderLayout.SOUTH);
add(p3, BorderLayout.CENTER);
}
public JPanel setImageSpace() {
JPanel test;
test = new JPanel();
test.setOpaque(true);
test.setPreferredSize(new Dimension(100, 100));
return test;
}
class MyButtonPanel extends JPanel implements ActionListener {
final JButton addImage = new JButton("Add Image");
ImageIcon lorryPicture = new ImageIcon(ImageGrid.class.getResource("/resources/lorry.png"));
JLabel lorryImage = new JLabel(lorryPicture);
MyButtonPanel() {
add(addImage);
addImage.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == addImage) {
imageSpaces[imageCounter].add(lorryImage);
revalidate();
repaint();
imageCounter++;
}
}
}
public static void main(String[] args) {
ImageGrid test = new ImageGrid();
test.setVisible(true);
}
}
You should be revalidating and repainting the panel, (which is the containter being affected by the addition), not the frame
imageSpaces[imageCounter].add(lorryImage);
imageSpaces[imageCounter].revalidate();
imageSpaces[imageCounter].repaint();
Diclaimer: This may work as a simple fix, but also note that a component (in this case your JLabel lorryImage) can only have one parent container. The reason the above fix still works is because you don't revalidate and repaint the previous panel, the label was added to. So you may want to think about doing it correctly, and adding a new JLabel to each panel.
if (e.getSource() == addImage) {
JLabel lorryImage = new JLabel(lorryPicture);
imageSpaces[imageCounter].add(lorryImage);
imageSpaces[imageCounter].revalidate();
imageSpaces[imageCounter].repaint();
imageCounter++;
}
Disclaimer 2: You should add a check, to only add a label if the count is less than the array length, as to avoid the ArrayIndexOutOfBoundsException
Side Notes
Swing apps should be run from the Event Dispatch Thread (EDT). You can do this by wrapping the code in the main in a SwingUtilities.invokeLater(...). See more at Initial Threads
You could also just use a JLabel and call setIcon, instead of using a JPanel
I have a JTable which contains a list of item and a JPanel with some components. When I click to my button, I want all information of the selected item in the JTable will be loaded to the JPanel. At the first time, it work well but at the further times when I click to my button the Jpanel appears with empty component.
The code when clicking to my button:
jbtUpdateContract.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
Object contractID = getValueOfSelectedRow(contractTable, 0);
Contract contract = contractTransaction.getContractByID(Integer.parseInt(contractID.toString()));
//Create Jpanel to display information
jplUpdateContractForm.removeAll();
jplUpdateContractForm = createContractForm(contract);
jplUpdateContractForm.revalidate();
//Modify contract frame
jfrmUpdateContract.getContentPane().add(jplUpdateContractForm,
BorderLayout.CENTER);
jfrmUpdateContract.setSize(400, 300);
jfrmUpdateContract.setVisible(true);
jfrmUpdateContract.setResizable(false);
}
});
And my createContractForm function:
public static JPanel createContractForm(Contract contract)
{
JPanel jplContractForm = new JPanel(new SpringLayout());
JLabel jlblAppointmentDate = new JLabel("Appointment date:", JLabel.TRAILING);
jlblAppointmentDate.setName("jlblAppointmentDate");
jplContractForm.add(jlblAppointmentDate);
final JTextField jtxtAppointmentDate = new JTextField(15);
jtxtAppointmentDate.setName("jtxtAppointmentDate");
jtxtAppointmentDate.setText(contract.getAppointmentDate());
jtxtAppointmentDate.setEditable(false);
jtxtAppointmentDate.addMouseListener(appointmentDateMouseListener(jtxtAppointmentDate));
jlblAppointmentDate.setLabelFor(jtxtAppointmentDate);
jplContractForm.add(jtxtAppointmentDate);
jplContractForm.setOpaque(true);
//***Customer Cobobox
JLabel jlblCustomer= new JLabel("Customer:", JLabel.TRAILING);
jlblCustomer.setName("jlblCustomer");
jplContractForm.add(jlblCustomer);
final JComboBox jcbxCustomer = new JComboBox();
jcbxCustomer.setName("jcbxCustomer");
//Load customers from DB to combobox
Customer[] customers = customerTransaction.getAllCustomer();
for(int i = 0; i < customers.length; i++)
jcbxCustomer.addItem(customers[i]);
System.out.println("----------CUSTOMER----------" + getIndexOfCustomerComboBoxItem(jcbxCustomer, contract.getCustomerSeq()));
jcbxCustomer.setSelectedIndex(getIndexOfCustomerComboBoxItem(jcbxCustomer, contract.getCustomerSeq()));
jlblCustomer.setLabelFor(jcbxCustomer);
jplContractForm.add(jcbxCustomer);
jplContractForm.setOpaque(true);
}
Please help me to explaint why the JPanel is empty as I describes above.
Regards.
The frame is already visible, so I don't think any of these lines of code will help.
jfrmUpdateContract.setSize(400, 300);
jfrmUpdateContract.setVisible(true);
jfrmUpdateContract.setResizable(false);
The revalidate() does nothing because you haven't added the panel to the frame yet. Try the revalidate() AFTER the panel has been added to the frame.
jplUpdateContractForm = createContractForm(contract);
//jplUpdateContractForm.revalidate();
jfrmUpdateContract.getContentPane().add(jplUpdateContractForm, BorderLayout.CENTER);
jplUpdateContractForm.revalidate();
If you need more help then post a proper SSCCE.
I have a border layout and I want to add a grid layout to the center section. However, I can't declare a grid and then add my center border. How can I do this?
public Liability_Calculator(String s)
{
super(s);
setSize(325,200);
c = getContentPane();
c.setLayout(new BorderLayout());
//the top label
total = new JLabel("Total monthly liabilities ", JLabel.CENTER);
c.add(total, BorderLayout.NORTH);
//the grid
GridLayout grid = new GridLayout(2,2);
text_field1 = new JTextField(7);
//I GET AN ERROR HERE!!!!!!!
grid.add(text_field1);
//AND ERROR HERE!!!!!!!!!!!!!
c.add(grid, BorderLayout.CENTER);
setVisible(true);
}
You're trying to add a component to a layout, and that simply cannot be done. Instead use a JPanel, give it a GridLayout, and then add the component to the JPanel (acting as the "container" here).
In general, you will want to nest JPanels with each using the best layout for the GUI, here the inner JPanel using GridLayout and the outer one using BorderLayout. Then you simply add the inner JPanel to the outer one (here your contentPane) in the BorderLayout.CENTER position.
Providing code visualization derived from Hovercraft's answer:
Display class:
public class Display extends JFrame {
JPanel gridHolder = new JPanel(); // panel to store the grid
private GridLayout buttonsGrid; // space containing a buttons
private JButton myButtons[]; // grid is to be filled with these buttons
private BorderLayout mainGUILayout; // main gui layout
private Container mainGuiContainer;
public Display() {
mainGUILayout = new BorderLayout(5,5); // Border layout option
mainGuiContainer = getContentPane(); // getting content pane
mainGuiContainer.setLayout(mainFrameLayout); // setting main layout
buttonsGrid = new GridLayout(4, 1, 5, 5); // 4 buttons one over the other
myButtons = new JButton[4]; // player's hand represented with buttons
gridHolder.setLayout(buttonsGrid);
for (int x = 0; x < 4; x++) {
myButtons[x] = new JButton (" ");
gridHolder.add(myButtons[x]); }
add(gridHolder, BorderLayout.WEST);
setVisible(true); } }
MainGUILaunch class:
public class MainGUILaunch {
public static void main (String args[]) {
Display myApplication = new Display();
myApplication.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myApplication.setSize(1024, 1024);
myApplication.setVisible(true); // displaying application }
} // End of MainGUILaunch
I'm having problems adding a JPanel, which has a paintComponent in it, to a JFrame.
If this is the only thing I add to the frame, it works. But as soon as I add a layout manager and add other components to the JFrame, it no longer shows the panel with the painting!
To make this clearer ...
This is the code that works and the JPanel is successfully shown:
The panel that draws the sign (in reality i am not trying to paint hello, this is to simply the code here)
public class SignPanel2 extends JPanel {
public int hello;
public void paintComponent(Graphics comp) {
Graphics g = (Graphics) comp;
g.setColor(Color.LIGHT_GRAY);
g.fillRect(70, 250, 150, 150);
g.setColor(Color.BLACK);
if (hello > 0)
g.drawString("h",135, 270);
if (hello > 1 )
g.drawString("h e",135, 270);
if (hello > 2)
g.drawString("h e l",135, 270);
if (hello > 3)
g.drawString("h e l l",135, 270);
if (hello > 4)
g.drawString("h e l l o",135, 270);
}
}
The frame i put the panel on:
public class SignFrame extends JFrame {
// the constructor method
public SignFrame () {
super("This is the title of the Sign Frame");
setSize(300,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// make a container for the frame
Container content = getContentPane();
// call from the drawing panel
SignPanel2 signpanel = new SignPanel2();
// change class variable of SignPanel
signpanel.hello = 5;
signpanel.repaint();
// add signpanel to container
content.add(signpanel);
setContentPane(content);
setVisible(true);
}
}
The main class
public class TheSignMain {
public static void main (String[] args) {
SignFrame signframe = new SignFrame();
}
}
The above works perfectly fine and gives me a frame with the desired drawing in it.
But if I add other components to the frame and add a layout manager, it no longer shows me the painting. even if I use repaint().
I have to include a layout manager, otherwise it adds the panel with the painting, but not the other components.
This is how my frame class looks now, and this is where i'm having problems.
public class SignFrame extends JFrame {
// the constructor method
public SignFrame () {
super("This is the title of the Sign Frame");
setSize(300,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// make a container for the frame
Container content = getContentPane();
// need a layout manager to decide the arrangement of the components on the container
FlowLayout flo = new FlowLayout();
// designate the layout manager to the container
content.setLayout(flo);
// make components
JPanel buttons = new JPanel();
JButton play = new JButton("Play");
JButton pause = new JButton("Pause");
JButton stop = new JButton("Stop");
// add components to a panel
buttons.add(play);
buttons.add(pause);
buttons.add(stop);
// add panel to frame container
content.add(buttons);
// call from the drawing panel
SignPanel2 signpanel = new SignPanel2();
// change class variable of SignPanel
signpanel.hello = 5;
signpanel.repaint();
// add signpanel to container
content.add(signpanel);
setContentPane(content);
setVisible(true);
}
}
I am totally new to Java, so any help will be much appreciated.
Sorry for all that code and thanks for your help!!
Not tested, but the flow layout probably uses the preferred size of your panel, and you probably haven't overridden getPreferredSize() to return something other than a [0, 0] dimension.
Also, you should encapsulate the change of the hello variable in a setHello() method that calls repaint(). The calling code shouldn't have to deal with repaint. The panl should know when it has to be repainted, and call repaint itself.
Why my JFrame 'frame' is diplaying empty window, when it should give me 3 menu buttons and my own painted JComponent below ? What am I missing here ?
import java.awt.*;
import javax.swing.*;
public class Eyes extends JFrame {
public static void main(String[] args) {
final JFrame frame = new JFrame("Eyes");
frame.setPreferredSize(new Dimension(450, 300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel players = new JPanel(new GridLayout(1, 3));
players.add(new JButton("Eyes color"));
players.add(new JButton("Eye pupil"));
players.add(new JButton("Background color"));
JPanel eyes = new JPanel();
eyes.add(new MyComponent());
JPanel content = new JPanel();
content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
content.add(players);
content.add(eyes);
frame.getContentPane();
frame.pack();
frame.setVisible(true);
}
}
class MyComponent extends JComponent {
public MyComponent(){
}
#Override
public void paint(Graphics g) {
int height = 120;
int width = 120;
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
BasicStroke bs = new BasicStroke(3.0f);
g2d.setStroke(bs);
g2d.setColor(Color.yellow);
g2d.fillOval(200, 200, height, width);
g2d.setColor(Color.black);
g2d.drawOval(60, 60, height, width);
}
}
Your line:
frame.getContentPane();
doesnt do anything but access the content pane of the frame. Instead of getting the content pane, you should set your content pane, like this:
frame.setContentPane(content);
EDIT:
alternatively, as #trashgod points out, you could use the getContentPane method to access the default content pane and add your content component to that:
frame.getContentPane().add(content);
I think you are attempting to use nested JPanels. This is certainly a way to organize your components, but downside to is the fact that it gets difficult to manage in some cases. You could try this snippet of code below. In the program you will find:
1) An array of JLabel
2) An array of JTextField
3) Nested JPanels
At the end of the program I use the Container to add the final product of these object to my Graphics Window.
The most efficient way I can think of is to define these components at the top of my program so that I can reuse them later as I need to.
To achieve this you can try this snippet of code:
import javax.swing.*; //Required to use Swing Components
public class TestGUI extends JFrame
{
JLabel[] label; //Define this with an array
JTextField[] textField; //Define this with an array as well
private int nLabels; //Number of labels preferred
private int nTextFields; //Number of text fields preferred
public testGUI(int amt)
{
//Assuming that you want equal amounts of each,
//set these two variables to the "ant" input parameter
nLabels = amt;
nTextFields = amt;
//Set component attributes
label = new JLabel[2]; //Label compared text fields
textField = new JTextField[2]; //Use two of these for comparison
textField[0].setEnabled(false); //Disabled editing
//Do nothing with the second text field
JPanel labels = new JPanel(); //Place JLabels here
//Use this to align the labels vertically
labels.setLayout(new GridLayout(2, 1));
//Use this for loop to add the labels to this JPanel
for(int i = 0; i < nLabels; i++)
{
labels.add(label[i]);
//You can also define and apply additional properties
//to labels inside this loop. TIP: You can do this in
//any loop
}
JPanel txtFields = new JPanel(); //Place JTextFields here
//Use this to align the text fields vertically
txtFields.setLayout(new GridLayout(2, 1));
//Use this for loop to add the labels to this JPanel
for(int i = 0; i < nTextFields; i++)
{
textFields.add(textField[i]);
//You can also define and apply additional properties
//to text fields inside this loop. TIP: You can do
//this in any loop
}
//Now we have the two components, you asked for help with, set up
//Next, we will need another JPanel to add these to panels to.
//This JPanel will be added to the JFrame Container
//You probably know how to run this via the "main" method
JPanel window = new JPanel();
//Place the JPanel for the labels and text fields
//This requires a horizontal grid
window.setLayout(new GridLayout(1, 2));
//Add the the two JPanels: "labels" and "txtFields"
window.add(labels);
window.add(txtFields);
//Define the Container object to set up the GUI
Container container = getContentPane();
//Apply the "window" JPanel object to the container
container.add(window, BorderLayout.CENTER);
//Center this in the Graphics Window when displayed
}
//Any other methods and/or functions can be added as well
//If they are, they must placed in the constructor method above
}
This is the approach that I would use when trying to go at making and manipulating my Graphics Windows that I write. Sometimes I write applets, but only after making sure that I have everything functioning properly in a plain Graphics Window.
I hope this helps.
If you have any other questions, just let me know and I will answer the to the best of my ability, thanks.