Reordering labels - java

I'm creating labels dynamically from an array in a FlowLayout JPanel, storing them in a JLabel array for future reference. They are displayed from left to right as intended.
I want to move one of the labels to the beginning (leftmost) of the panel.
I don't mind if the whole array shifts or just two labels swap places:
apple orange pear cherry melon
|
cherry apple orange pear melon
or
cherry orange pear apple melon
I've swapped array entries, then revalidate() and repaint(), but nothing happens.
Is there an easy way to move swing components around without removing all and then re-adding them to the panel or copying all the properties from one label to the other (I have others defined, not just the text)?
Here is a stripped down version of my code:
import javax.swing.*;
public class Test extends JPanel {
public Test () {
String entries[] = { "apple", "orange", "pear", "cherry", "melon" };
JLabel[] lbls = new JLabel[entries.length];
for (int i = 0; i < entries.length; ++i) {
lbls[i] = new JLabel();
lbls[i].setText(entries[i]);
add(lbls[i]);
}
// swap array entries
JLabel tmplbl = new JLabel();
tmplbl = lbls[3];
lbls[3] = lbls[0];
lbls[0] = tmplbl;
revalidate();
repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setContentPane(new Test());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}
});
}
}

I've swapped array entries
Swapping entries in an array does nothing. The Array has nothing to do with the panel.
So you need to adjust the components on the panel.
I want to move one of the labels to the beginning (leftmost) of the panel.
Well that is a different requirement than "swapping". It is also easier.
You can add a component to a panel and specify its position in the panel, so adding a component to the beginning is easy because its position will always be zero.
So to move the 3rd component to the beginning the code would be something like:
Component component = panel.getComponent(2);
panel.add(component, 0);
panel.revalidate();
panel.repaint();
If you really want a swap, then the code would be similar. You would get the component at both locations and then add the one component back to the lower location first and the add the other component back to the higher location.

There are a couple of things to fix before fixing your error:
Here are 2 errors in this line: public class Test extends JPanel {
Class name, do you know how many people call their classes Test? A LOT! Make it more descriptive, like SwapLabelsTest.
extends JPanel, you're not changing the behavior of the JPanel so there's no need to extend it in this case, just create a new instance of JPanel.
Don't put everything in the constructor, it's better to have an initialize() method or something like that (createAndShowGUI() in the code below) to handle GUI construction. It may seem like the easiest way, but separating that part will come handy later on when the project becomes bigger.
Move your variables to a bigger scope, for easier handling, unless those variables are local to the method, this will improve performance and readability.
Include a component that detects events, such as a JButton so that your swapping execution will happen when that event is triggered (a button click).
Your swapping logic seems a little bit odd, you have created new JLabels there and are trying to swap them, but it's better to have a MVC kind of pattern here, so that you swap the values in the array and then just update the UI after with those changes.
You may be asking, but how do I do that? Well like this:
String tmpString = entries[3];
entries[3] = entries[1];
entries[1] = tmpString;
The above code swaps the values in the entries array, all we have to do now is update each label with lbl[i].setText(entries[i]) inside of a for-loop.
So, you end up with something like this in the end:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
private JFrame frame;
private JPanel panel;
private String entries[] = { "apple", "orange", "pear", "cherry", "melon" };
private JLabel[] lbls = new JLabel[entries.length];
JButton button;
private void createAndShowGUI() {
panel = new JPanel();
for (int i = 0; i < entries.length; ++i) {
lbls[i] = new JLabel();
lbls[i].setText(entries[i]);
panel.add(lbls[i]);
}
button = new JButton("Swap 1 and 3");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String tmpString = entries[3];
entries[3] = entries[1];
entries[1] = tmpString;
reloadLabels();
}
});
frame = new JFrame("Test");
frame.add(panel);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.pack();
}
private void reloadLabels() {
for (int i = 0; i < entries.length; ++i) {
lbls[i].setText(entries[i]);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().createAndShowGUI();
}
});
}
}
Everytime you click the button, the items 1 & 3 (indexes) will be swapped and the UI will be updated (as .setText triggers an UI update).

Related

Put one JPanel onto another JPanel from a different Java class

I'm developing Java Swing application. My application has two Java classes. Inside class1.java, I include JFrame, JButton and JPanel (panel1). When I click the button I want to hide panel1 and should be shown panel2 of class2.java. I tried this method in button actionPerformed method of class1.java. But it was not working.
class2 pnl = new class2();
this.remove(panel1);
this.add(pnl);
this.validate();
this.repaint();
Analysis
You simply want the JComponents to be displayed on the JFrame. We can achieve this by using a single JPanel, but adding and removing the JComponents from it, during the JButton's action listener.
Without looking at your actual code, it is better to make a manageable way to reach code and instantiated Objects. The code listed below, creates a nice and manageable way to do so.
Achieving This
The entire class is listed below with comments for explanations.
package swing;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MultiPaneledFrame {
JFrame frame = new JFrame();
JPanel window = new JPanel();
// As you can see, we create an array containing all your JComponents.
// We have two of these, to simulate multiple JPanel's.
List<JComponent> window1Contents = new ArrayList<JComponent>();
List<JComponent> window2Contents = new ArrayList<JComponent>();
// NOTE: The above Lists can instead be stuck in their own class like asked for,
// and instantiated on Class invocation.
JButton goto2 = new JButton("Goto Panel 2");
JButton goto1 = new JButton("Goto Panel 1");
int panelToShow = 0; // 0 - First "panel".
// 1 - Second "panel".
// Main method of class. Change 'Multi_Paneled_Frame' to the name of your Class.
public MultiPaneledFrame() {
// Execute anything else you want here, before we start the frame.
window1Contents.add(goto2);
window2Contents.add(goto1);
// Here is where I personally am setting the coordinates of the JButton's on the JPanel.
goto2.setPreferredSize(new Dimension(200, 100));
goto1.setPreferredSize(new Dimension(200, 100));
//goto2.setBounds(5, 5, 150, 30); < Used for 'null' layout.
//goto1.setBounds(5, 5, 150, 30); < Used for 'null' layout.
goto2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addComponents(panelToShow = 1);
}
});
goto1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addComponents(panelToShow = 0);
}
});
initialiseFrame();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MultiPaneledFrame();
}
});
}
private void initialiseFrame() {
frame.setSize(600, 400); // Change it accordingly.
// Optional
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
// Needed
frame.setVisible(true);
frame.add(window);
window.setLayout(new BorderLayout()); // Assuming your using a BorderLayout.
//window.setLayout(null); < Uses 'null' layout.
addComponents(panelToShow);
// I always like to make sure that everything is on the frame nicely.
frame.repaint();
frame.validate();
}
private void addComponents(int panelNo) {
if (panelNo == 0) {
for (JComponent component : window1Contents) {
window.removeAll(); // We're removing everything that it contains and replacing it...
window.revalidate();
window.add(component, BorderLayout.CENTER);
//window.add(component); < Uses 'null' layout.
// Since we are using the first panel, we are adding
// everything from the first list of components to the window...
}
} else {
for (JComponent component : window2Contents) {
window.removeAll(); // We're removing everything that it contains and replacing it...
window.revalidate();
window.add(component, BorderLayout.CENTER);
//window.add(component); < Uses 'null' layout.
// Since we are using the second panel, we are adding
// everything from the second list of components to the window...
}
}
// Refreshes the frame.
frame.repaint();
frame.validate();
}
}
Conclusion
Although there are countless ways to achieve something like this, the way I have given, is semi-efficient, and very flexible. Feel free to edit the code, or drop a question if you have any concerns, and I will be happy to respond.
PS: This code was tested and works on a Macbook Air running OS X 10.11 and Java Version 8 Update 65.
CardLayout should be your solution. In this tutorial they show how to switch from panel to another one by selecting a value in ComboBox.
A little bit of explanation for the CarLayout:
The CardLayout lets you place different panel on top of each other but shows only one at the time. With your code, you select the one you want to display.
Initialisation:
this.setLayout(new CardLayout());
class1 pnl1 = new class1();
class2 pnl2 = new class2();
this.add(pnl1, "PANEL1");
this.add(pnl2, "PANEL2");
On your button actionPerformed:
CardLayout cl = (CardLayout)(this.getLayout());
cl.show(this, "PANEL2");

Cast Component[] -> JButton

I'm programming Java with NetBeans. I have a strange problem. In a class Mouse Listener, I do getComponents of a panel, and it returns a array of components that I put(?) in one array JButton (whit cast) so..
JButton[] b= (JButton[])Main.p1.getComponents();
IDE doesn't show an error.
I do, for example:
b[0].setBackground(Color.yellow);
and in output I see an error.
But if I do
JButton b= (JButton) Main.p1.getComponents()[0];
b.setBackground(...);
And all works but I don't understand why. Can somebody explain please?
2015-01-18
Thank to all for the answer.
I want take Components of a Panel, not only for change background's color, but also for the Ordinates; I need all Buttons in a array to compare all at the same time .. Why I cant convert 'Components' in 'Buttons'? Panel has a pointer to Buttons in Ram Memory..
Patryk Dobrowolski thank's you, i try this: (i use class Button extends JButton)
Button[] button = new Button[Main.p1.getComponents().length];
for (int i = 0; i < Main.p1.getComponents().length; i++) {
button[i] = (Button) Main.p1.getComponents()[i];
}
but in output I see this error:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: javax.swing.JLabel cannot be cast to Button
why JLabel?
Hovercraft Full Of Eels, thank's you for your answer, I could create an array and insert the pointer of all Button into it. But I said: JPannel has a Array where insert pointer.. I can use it. Why not?
Thank's you very much!
Alessandro Amedei
In the first case, you are lying to the compiler. By stating
JButton[] b= (JButton[])Main.p1.getComponents();
you are telling the compiler that you know that this is an array of JButtons, when in fact it really is an array of Components. Therefore you get no compile-time errors, but you do get one at runtime.
In the other case it works, since then you are telling the compiler that the Component you want to work with is a JButton, which is true.
It's because array of JComponent is not the same as array of JButton. You need to cast one by one in the loop if you want to finally get array of buttons:
JButton[] buttons = new JButton[components.length];
for (int i = 0; i < components.length; i++) {
buttons[i] = (JButton) components[i];
}
I am concerned that you are trying to extract your selected JButton by iterating through an array of components and thereby basing your program logic on the GUI component structure, which is a very fragile way to do things. If you change components around, or later decide to nest a container that holds your JButtons into another container (i.e., into another JPanel), you would have to take care to update this listener and all similar listeners, again making your code quite fragile.
Rather than do this, a better solution might be to add a listener to the individual components that need them. For instance, if you want to change the background of a JButton to yellow if a mouse is hovering over it, consider adding a ChangeListener to each JButton, extracting the involved button via the ChangeEvent's getSource() method and then testing the button's model to see if it is in a roll-over state. For example:
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class SetButtonBackground extends JPanel {
public static final Color ROLLOVER_COLOR = Color.yellow;
public SetButtonBackground() {
int rows = 5;
int cols = rows;
setLayout(new GridLayout(rows, cols, 5, 5));
ButtonChangeListener btnChngListener = new ButtonChangeListener();
for (int i = 0; i < rows * cols; i++) {
String text = "Button " + (i + 1);
JButton button = new JButton(text);
button.addChangeListener(btnChngListener);
add(button);
}
}
private class ButtonChangeListener implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
JButton btn = (JButton) e.getSource();
ButtonModel model = btn.getModel();
if (model.isRollover()) {
if (!btn.getBackground().equals(ROLLOVER_COLOR)) {
btn.setBackground(ROLLOVER_COLOR);
}
} else if (btn.getBackground().equals(ROLLOVER_COLOR)) {
btn.setBackground(null); // change back to default state
}
}
}
private static void createAndShowGui() {
SetButtonBackground mainPanel = new SetButtonBackground();
JFrame frame = new JFrame("Set Button Background");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Java JComponent paint --Almost working

I almost have the repaint() Jcomponent working. I had it working and then tried to make an absolute positioning and now it doesn't work.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
public class DDHGenericFrame {
private static final long serialVersionUID = 1L;
DDHGenericPanel d = new DDHGenericPanel();
//DDHCircleOne o = new DDHCircleOne();
public DDHGenericFrame() {
initUI();
}
public final void initUI() {
JFrame frame = new JFrame("AbsoluteLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Draw Circle");
frame.setBackground(Color.green);
frame.setLayout(null);
frame.setSize(300,425);
frame.add(d);
// frame.add(o);//didn't work neither
frame.setVisible(true);
}
public static void main(String[] args) {
DDHGenericFrame ex = new DDHGenericFrame();
}
}
Class 2: (This is the JComponent that I am trying to set)
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
public class DDHTestDraw extends JComponent {
public DDHTestDraw () {
settPreferredSize();
}
public void settPreferredSize() {
Dimension d = new Dimension(25,25);
setPreferredSize(d);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Text",20,20);
}
}
I added my component to the Container and then added the container to the JPanel and then the JPanel is added to the JFrame. I would think that this should work. I have a set preferred size. I had it working once and now it doesn't work.
I want to be able to make a component that is a circle. I want that circle to be able to be drawn any where on a Jframe, and then I want that circle to be able to move based on a a certain length of time. I am going to make a game that has circles dropping from the top and then falling to the bottom. When I had it working I did have the circle being written to the JPanel which is a complex piece of code. But I have had to go back to the old simple method of writing a single graphical word.
When you use null layout, you are completely responsible for making sure that components added have proper location and size (not preferredSize) set.
You should almost never use null layout.
Wouldn't this sort of thing work better by creating a logical class to represent the Circle, not a component? Then your drawing JPanel could hold a collection of logical circles, and the drawing JPanel could be responsible for drawing each Circle in its paintComponent method.
Edit
Your comments/my replies:
when you say never use an absolute layout, the company that I worked for always used a absolute layout only.
There are times when it is useful, but not for creating a typical component-filled GUI. Otherwise the GUI becomes very hard to modify and maintain.
When you mean a logical class you mean a class that just creates one circle.
Yes, and that holds all the necessary properties of that circle such as its Color, location, movement, etc..
Then the Jpanel would draw each circle.
Yes. I would imagine the drawing JPanel having an ArrayList<MyCircle> of them, and the paintComponent method iterating througgh this List.
when you say Size this is a property in JComponent.
I think that it is a property of Component, JComponent's parent. If you use null layout, then all components must have their size and location specified. Otherwise the component defaults to a location of [0, 0] and a size of [0, 0].
Edit 2
public Dimension Size(int a, int b) {
Dimension d = new Dimension();
d.width = a;
d.height = b;
return d;
}
This is the code that I used for the preferred size. I am at a lost why this doesn't work.
This code has no effect on either the size or the preferredSize properties of Component/JComponent. It doesn't surprise me that it will not help you. You would either have to override getSize() or getPreferredSize() or explicitly call setSize(...) or getPreferredSize(...) to change the state of the properties.
I am going to try it with a different layout manager and see but I would see the difference between one layout manager or another.
I'm not sure how to interpret this.
Edit 3
You state:
I worked at one company and we used absulute layouts all of the time. How would an absolute layout not work as good as, say BorderLayout(). To me the BorderLayout() are harder to implement. Or is it that you use a Jframe() with a BorderLayout, and then insert a Jpanel into an already existing position that is already also a BorderLayout(). I always have trouble getting my buttions and positions correct in a layout that is something different than a BorderLayout(). Can you post an example that would be easier to use than
I'm guessing you want an example of where use of layout managers is easier than using absolute positioning.
Let's take the example of a very simple calculator, one with buttons for numeric input and simple operations. Again this example is very basic, and is non-functional, but serves to illustrate the use of layouts. I could easily place my buttons in a GridLayout-using JPanel, and then place that button JPanel into a BorderLayout-using JPanel at the BorderLayout.CENTER position with the JTextField, display, placed in the same BorderLayout-using JPanel at the BorderLayout.PAGE_START position:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.*;
public class CalcEg {
private static final float BTN_FONT_SIZE = 20f;
private static final String[][] BTN_LABELS = {
{"7", "8", "9", "-"},
{"4", "5", "6", "+"},
{"1", "2", "3", "/"},
{"0", ".", " ", "="}
};
private static final int GAP = 4;
private JPanel mainPanel = new JPanel(new BorderLayout(GAP, GAP));
private JPanel buttonPanel = new JPanel();
private JTextField display = new JTextField();
public CalcEg() {
int rows = BTN_LABELS.length;
int cols = BTN_LABELS[0].length;
buttonPanel.setLayout(new GridLayout(rows, cols, GAP, GAP));
for (String[] btnLabelRow : BTN_LABELS) {
for (String btnLabel : btnLabelRow) {
if (btnLabel.trim().isEmpty()) {
buttonPanel.add(new JLabel());
} else {
JButton btn = createButton(btnLabel);
buttonPanel.add(btn);
}
}
}
display.setFont(display.getFont().deriveFont(BTN_FONT_SIZE));
display.setEditable(false);
display.setFocusable(false);
display.setBackground(Color.white);
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(buttonPanel, BorderLayout.CENTER);
mainPanel.add(display, BorderLayout.PAGE_START);
}
private JButton createButton(String btnLabel) {
JButton button = new JButton(btnLabel);
button.setFont(button.getFont().deriveFont(BTN_FONT_SIZE));
return button;
}
public JComponent getMainComponent() {
return mainPanel;
}
private static void createAndShowGui() {
CalcEg mainPanel = new CalcEg();
JFrame frame = new JFrame("CalcEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This would result in a calculator that looks like so:
Now, sure you could say that you could produce this with null layout and setbounds(...), and that's all well and good, but now say that you're not satisfied with this calculator, and now desire that it has some scientific calculation functionality. Say you now want to add buttons for square, square root, exponential, and logarithm, but not only that, say you wish to add the buttons below the display and above the numeric and basic operations buttons. If you were to do this with null layout, you would have to reposition all the components below and to the right of any new components added, and you'd have to expand the size of the JTextField, all calculations that are tedious and prone to error.
If you used layout managers, you would instead only need to add one line of code, actually an additional row to an array:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.*;
public class CalcEg {
private static final float BTN_FONT_SIZE = 20f;
private static final String[][] BTN_LABELS = {
{"sqr", "sqrt", "exp", "log"}, // ******* Line Added Here *********
{"7", "8", "9", "-"},
{"4", "5", "6", "+"},
{"1", "2", "3", "/"},
{"0", ".", " ", "="}
};
private static final int GAP = 4;
private JPanel mainPanel = new JPanel(new BorderLayout(GAP, GAP));
private JPanel buttonPanel = new JPanel();
private JTextField display = new JTextField();
public CalcEg() {
int rows = BTN_LABELS.length;
int cols = BTN_LABELS[0].length;
buttonPanel.setLayout(new GridLayout(rows, cols, GAP, GAP));
for (String[] btnLabelRow : BTN_LABELS) {
for (String btnLabel : btnLabelRow) {
if (btnLabel.trim().isEmpty()) {
buttonPanel.add(new JLabel());
} else {
JButton btn = createButton(btnLabel);
buttonPanel.add(btn);
}
}
}
display.setFont(display.getFont().deriveFont(BTN_FONT_SIZE));
display.setEditable(false);
display.setFocusable(false);
display.setBackground(Color.white);
mainPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
mainPanel.add(buttonPanel, BorderLayout.CENTER);
mainPanel.add(display, BorderLayout.PAGE_START);
}
private JButton createButton(String btnLabel) {
JButton button = new JButton(btnLabel);
button.setFont(button.getFont().deriveFont(BTN_FONT_SIZE));
return button;
}
public JComponent getMainComponent() {
return mainPanel;
}
private static void createAndShowGui() {
CalcEg mainPanel = new CalcEg();
JFrame frame = new JFrame("CalcEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
which would result in this GUI:
Again this is a very simplistic example, but the general principles apply to any GUI that holds components such as JButtons, JTextComponents, etc.
With Swing animation, the Timer class is an exceptional option.
//this class is a JPanel that implements ActionListener`
Timer t = new Timer(1000,this);//the first arg is how many times it repeats in milliseconds
//Then in the constructor...
t.start();
//the ActionPerformed function normally increments a variable then calls the repaint method.
rectangle_x++;
repaint(); //calls paintComponent
Another good idea is to cast g to a Graphics2D object- it's a lot safer and more capable.
Another way to use the Timer class:
Timer t = new Timer(510, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
rectangle_x++;
repaint();
}
})
...
t.start();
You still need to override actionPerformed() in your main class though.

CardLayout performance?

I thought that CardLayout uses a HashMap to store its pairs (panels and associated string identifiers) but looking through the CardLayout class I noticed that it actually uses a Vector. This is how I understand it: when the show method is called it loops through the contents of the vector checking with equals to find out if this is the name of the appropriate card, and if it is, it then loops through all the panels of the container to find out which one is currently visible, it hides it, and then it displays the appropriate card.
If I was making a gui app that has a lot of different panels wouldn't that be kind of slow technique to flip to the desired panel? Should I better use my own way of showing my panels like storing them to an array and manually using add/remove or setVisible instead of using CardLayout? This is actually the way I was using at the beginning before I ended up to CardLayout.
This will never be an issue: you don't normally flip between components very frequently, and when you do then scanning a list with a smallish number of compoenents (usually 3-100?) is going to take a negligible amount of time compared with other operations that will have to happen (e.g. drawing the new component). Choice of data structure is basically irrelevant from a performance perspective - you could use a linked list and nobody would notice.
Also note that a HashMap wouldn't be appropriate for a CardLayout as it needs to preserve the order of the cards so that you can use first/next/previous etc.
So basically, don't worry and don't waste your time rolling your own CardLayout clone - CardLayout works just fine.
I don't find any performance issue in CardLayout. Even if you have a 1000 child components, it still feels very fast. Either by using previous/next or using show, it goes really fast.
Try to post an SSCCE that reproduces your problem and then we may help you. Here is something to start from:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestCardLayout {
protected void initUI() {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final CardLayout layout = new CardLayout();
final JPanel panel = new JPanel(layout);
for (int i = 0; i < 1000; i++) {
panel.add(new JLabel("Label " + i), getLabelConstraint(i));
}
JButton next = new JButton("Next");
next.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
layout.next(panel);
}
});
JButton previous = new JButton("Previous");
previous.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
layout.previous(panel);
}
});
final JButton choose = new JButton("Choose");
choose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String value = JOptionPane.showInputDialog(choose, "Enter a number between 0 and 999");
try {
int i = Integer.valueOf(value);
if (i > -1 && i < 1000) {
layout.show(panel, getLabelConstraint(i));
}
} catch (NumberFormatException e1) {
e1.printStackTrace();
}
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(previous);
buttonPanel.add(next);
buttonPanel.add(choose);
frame.add(buttonPanel, BorderLayout.SOUTH);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private String getLabelConstraint(int i) {
return "ComponentConstraint" + i;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestCardLayout().initUI();
}
});
}
}

Java (Swing): JScrollPane.setBounds() does not apply?

I'm trying to create a simple JList with a scrollbar, and therefore i need to have the JList within a JScrollPane. So far, so good. However, for some reason i can't resize/position the JScrollPane!? It sounds logic that everything inside it should stretch to 100%, so if i set the JScrollPane to be 300px wide, the elements inside will be as well. Is that correct?
While you're at it, please critisize and give me hints if i should change something or optimize it.
Anyhow, here's the code:
package train;
import java.awt.*;
import javax.swing.*;
public class GUI {
private DefaultListModel loggerContent = new DefaultListModel();
private JList logger = new JList(loggerContent);
GUI() {
JFrame mainFrame = new JFrame("title");
this.addToLog("testing testing");
this.addToLog("another test");
// Create all elements
logger = new JList(loggerContent);
JScrollPane logWrapper = new JScrollPane(logger);
logWrapper.setBounds(10, 10, 20, 50);
// Add all elements
mainFrame.add(logWrapper);
// Show everything
mainFrame.setSize(new Dimension(600, 500));
mainFrame.setVisible(true);
}
public void addToLog(String inputString) {
int size = logger.getModel().getSize();
loggerContent.add(size, inputString);
}
}
Thanks in advance,
qwerty
EDIT: Here's a screenshot of it running: http://i.stack.imgur.com/sLGgQ.png
The setVisibleRowCount() method of JList is particularly convenient for this, as suggested in the relevant tutorial. ListDemo is a good example.
Addendum:
please critisize and give me hints…
Well, since you ask: Don't invoke public methods in the constructor; make them private or invoke them after the constructor finishes. There's no need to find the last index for add(), when addElement() is available. Also, be sure to construct your GUI on the event dispatch thread .
import java.awt.*;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/5422160 */
public class ListPanel extends JPanel {
private DefaultListModel model = new DefaultListModel();
private JList list = new JList(model);
ListPanel() {
list.setVisibleRowCount(5);
}
public void append(String inputString) {
model.addElement(inputString);
}
private void init() {
for (int i = 0; i < 10; i++) {
this.append("String " + String.valueOf(i));
}
JFrame mainFrame = new JFrame("GUI");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane jsp = new JScrollPane(list);
mainFrame.add(jsp);
mainFrame.pack();
mainFrame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ListPanel().init();
}
});
}
}
The bounds & size of a component are generally ignored over that of it's preferred size and the constraints of the layout being used by the container.
To solve this problem, learn how to use layouts & apply them appropriately.
Try to put your JScrollPane inside a JPanel and add the panel to the frame.
JPanel panel = new JPanel();
panel.add (logWrapper);
mainFrame.add(panel);
Then set the bounds of the panel instead of the JScrollpane
panel.setBounds(10, 10, 20, 50);
The probles is that Swing uses layout managers to control child bounds property. Adding a JScrollpane directly to the main frame, doesn't allow you to choose right bounds properly.

Categories

Resources