How to create dynamically JButton value in DesignGridLayout library? - java

i create grid layout using DesignGridLayout java library (here).
in the sampe if create 3 column layout. using this code :
layout.row().add(new JButton("Button 1")).add(new JButton("Button 2")).add(new JButton("Button 3"));
or using method that return object :
layout.row().add(button()).add(button()).add(button());
...
...
public JButton button() {
return new JButton("Button");
}
The question is, how to create dynamically JButton value? May be name,icon or anything?
I already try my own code like this :
for (int i=0; i<4; i++) {
JButton button = new JButton();
layout.row().add(button).add(button).add(button);
}
it return :
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Do not add the same component twice
My purpose for different value in each component which added in panel is, i want to create gallery that populate
different image, and i load that images using looping, like this :
for(int i=0; i<files.length; i++) {
...
ImageIcon imgSource = new ImageIcon(new File(myPath));
JLabel labelGallery = new JLabel(imgSource);
...
}
Any solution?
Thanks before :)

In your example,
layout.row().add(button).add(button).add(button);
has the effect of attempting to add the same JButton instance to the row repeatedly.
In the example cited,
layout.row().grid().add(button()).add(button());
invokes an auxiliary method, button(), to create a new instance each time it appears:
public static JButton button() {
return new JButton("Button");
}

As mentioned by #trashgod, Swing does not allow to add the same component twice to a panel.
If you want to add several components, created within a loop, to the same row, you can do it as follows:
IRow row = layout.row().grid();
for (int i = 0; i < n; i++) {
JButton button = createButton(i);
row.add(button);
}
That will create only one row with n buttons inside.

Related

No output after button click, since the variable doesn't exist?

I wanted a frame with one TextArea and one Button.
When I press the button, the TextArea should show a food menu of 5 Pizzas, well it shows nothing at all, except for the console which shows
"Exception in thread "AWT-EventQueue-0"
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at de.kvwl.pizza.MainFrame.actionPerformed(MainFrame.java:54)"
In the method windowsStart() the objects exists and are adjustable.
In the actionPerformed()Method the objects are … kind of invisible, not existing?
public void windowStart()
{
MainFrame mFrame = new MainFrame();
PizzaReader2 test = new PizzaReader2();
pPizza = test.csvRead();
System.out.println("\n\n\n" + pPizza.get(0) + "\n\n\n");
f = new JFrame("Textfield");
b = new JButton("Menu");
jt = new JTextArea(10,10);
JPanel pTextArea = new JPanel();
b.addActionListener(mFrame);
pTextArea.add(jt);
pTextArea.add(b);
f.add(pTextArea);
f.setSize(300, 300);
f.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
//jt.setText("TestText");
System.out.println("\n\n\n" + pPizza.get(0) + "\n\n\n");
String s = e.getActionCommand();
if (s.equals("Menu"))
{
System.out.println("Button gedrückt");
//jt.setText("");
for (int i = 0; i < pPizza.size(); i++)
{
jt.append(pPizza.get(i)+"\n");
}
The TextArea should get the value of the ArrayList
Your exception occurs in : at de.kvwl.pizza.MainFrame.actionPerformed(MainFrame.java:54)
This action is linked during windowStart with b.addActionListener(mFrame);.
But What I see is that you pass another instance of MainFrame called mFrame as parameter (as an ActionListener). This mFrame never load the list with
pPizza = test.csvRead();
So in short, you have two instance MainFrame:
one created and use to call windowStart
one created in windowsStart and use to execute actionPerformed.
This last one never load the list of data. Explaining why your list is populated in windowStart but not in actionPerformed, you are actually using two distinct instance MainFrame with two list pPizza.
You can correct this by removing this second instance and use this, the first instance as an ActionListener
b.addActionListener(this);

How to link JcheckBoxes to JtextFields when they are being dynamically generated?

i want my app to create jcheckboxes from an input that always changes.
I want to create a jtextfield near every checkbox, that will be set enabled, only when his checkbox is pressed.
I managed to create this code:
//Create checkboxes with textfileds
for (int i = 0; i < activeProjects.length; i++) {
projectPanels[i] = new JCheckBox(activeProjects[i]);
projectPanels[i].setSelected(false);
projectPanels[i].setComponentOrientation (ComponentOrientation.RIGHT_TO_LEFT);
projectPanels[i].setAlignmentX(RIGHT_ALIGNMENT);
projectPanels[i].addItemListener(this);
projectStorageNum[i] = new JTextField("");
// projectStorageNum[i].setEnabled(false);
projectStorageNum[i].setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
projectStorageNum[i].setMaximumSize(new Dimension(200,30));
projectStorageNum[i].setMinimumSize(new Dimension(200,30));
projectStorageNum[i].setPreferredSize(new Dimension(200,30));
projectStorageNum[i].setAlignmentX(RIGHT_ALIGNMENT);
tmppnl = new JPanel();
tmppnl.add(projectStorageNum[i]);
tmppnl.add(projectPanels[i]);
checkBoxPanel.add(tmppnl);
}
and this is my state change listener:
public void itemStateChanged(ItemEvent e) {
Object source = e.getItemSelectable();
JCheckBox myBox= (JCheckBox)source;
String bName = myBox.getText();
if (e.getStateChange() == ItemEvent.SELECTED)
{
// enable matching text field.
// add bName to projects list.
}
else
{
//disable matching textfield
// remove bName from list
}
when I access the checkboxes in a dynamic way I don't have access to the second array of textfields.
is there any way to link them , or any other idea ?
thanks
Dave.
One thing you could do is use the setName and getName methods of Component to save the index of the JCheckBox.
projectPanels[i].setName(Integer.toString(i));
Then, in your state change listener.
int i = Integer.valueOf(e.getName());
This gives you the index of the JTextField.
You could use a Map (Hashmap) the checkbox would be the key and the textField the value returned when you do the key loopup.

How to avoid implementing component over and over

For example, I have 20 different JTextField and lets say I need to set text to all "random", so I don't want to do like:
field1.setText("Random");
field2.setText("Random");
field3.setText("Random");
.
.
.
field20.setText("Random");
Is there a way to avoid this?
This is quite a basic question. You can do this :
Stream.of(field1,field2,...,field3).forEach(f -> f.setText("Random"));
or without Java 8 :
JTextField fields = Arrays.asList(field1,field2,...,field3);
for (JTextField field : fields)
field.setText("Random");
Or, if you don't need to keep a reference to your fields :
for (int i=0 ; i<20 ; i++) {
JTextField field = new JTextField("Random");
// place your field in the UI
}
Try something like:
JTextField[] textFields = new JTextField[20];
for (int i =0; i< fields.length; i++) {
//init here text fields
textFields[i].setText("Random");
}
Create the JTextFields in an array or a Collection and then iterate over the contents of the array.
For instance:
List<JTextField> myTextFields = new ArrayList<JTextField>();
for (int i = 0; i < 20; i++) {
myTextFields.add(new JTextField()); //Instantiate the textfields
//Do whatever other initialization, if you can do it in a loop (like position, etc.)
myTextFields.get(i).setText("Random"); //Set text on all of them.
}
Just to also cover the possibility which was not mentioned so far:
You could (although you really should not in most cases) inherit from JTextField and do your business e.g. in the constructor. Or you can use a factory-method.
(sample below is untested code written with one hand)
class MyTextField extends JTextField {
public MyTextField() {
setText("Random");
}
}
/* in other file */
class MyTextFildFactory {
public static JTextField createTextField() {
JTextField field = new JTextField();
field.setText("Random");
return field;
}
}

Problems with JScrollPane -- Trying to Update it on Model Changes

I'm writing a fairly complex program, so I'll try to explain it only in terms of where the problem is occurring.
In my view, I create a JScrollPane to display a list of courses that a student is registered for:
registeredPane = new JScrollPane(); registeredPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
registeredPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
c.gridx = 2;
c.gridwidth = 1;
c.gridheight = 2;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
layout.setConstraints(registeredPane, c);
Then I, in my controller, change the model to reflect the courses the currently active student is registered in by calling this function in my model:
public void updateCurrentStudentCourses() {
ArrayList<String> courseNames = new ArrayList<String>();
for (Course c: currentStudent.getRegCourses()) {
courseNames.add("" + c.getDepartment().getId() + c.getCode());
}
System.out.println(courseNames);
}
Then I, again in my controller, update the view to reflect these changes by adding the
ArrayList to the JScrollPane:
public void updateView() {
view.getNameField().setText(model.getCurrentStudent().getName());
view.getRegisteredPane().removeAll();
view.getRegisteredPane().getViewport().add(model.getCurrentStudentCourses());
view.getRegisteredPane().repaint();
}
The scrollbars disappear, but that's it. The list items (which I know are in the ArrayList) are not displayed. What am I doing wrong?
EDIT
If model.getCurrentStudentCourses() is returning an ArrayList then you can put the contents of ArrayList within JTextArea and then set the viewport of JScrollPane to be that JTextArea. You can proceed as follows:
your updateView() method should be like this:
JTextArea ta = new JTextArea(30,100);
public void updateView() {
ta.setText("");
for (String course : model.getCurrentStudentCourses())
ta.append(course+"\n");
view.getNameField().setText(model.getCurrentStudent().getName());
view.getRegisteredPane().setViewportView(ta);
}
To set the view of a scrollpane, use setViewportView(). Manipulating the child components of the scrollpane directly will cause problems (for example, you're removing the scroll bars as well).
Scrollpanes have their own layout manager which only knows about the components created by the scrollpane (the various viewports, the scrollbars, the corners). So, any component you add will not appear unless you manually set its position.

Multiple JButtons, one Eventlistener in Java

I have a 2D-Array of JButtons
JButton[][] ledBtns = new JButton[8][8];
And in a loop, I do all the init stuff. Now I want to add an EventListener to each JButton, that fires when the Button os clicked. Then I want to change the image on the Button.
for(int i = 0; i < ledBtns.length; i++){
for(int j = 0; j < ledBtns[i].length; j++){
//init stuff
ledBtns[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
changeImage();
}
});
}
}
Now 'changeImage()' will be called, but I need to know what button called it.
I can't use parameters, if I do it tells me to declare them as 'final'.
Is there any other way than writing 64 methods, that do exactly the same, and adding them manually to each of the JButtons?
The ActionEvent class has a getSource() method used to get the component that generated the event.
The easiest way to do this is to just declare two temporary final ints, and reference those.
for(int i = 0; i < ledBtns.length; i++){
for(int j = 0; j < ledBtns[i].length; j++){
//init stuff
final int finalI = i;
final int finalJ = j;
ledBtns[i][j].addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
changeImage(finalI,finalJ);
}
});
}
You can set the JButton object's "name" property and, according to mre's answer, you can call the getSource() method. So you can identity whick button is clicked
Another option is to have your class implement ActionListner (ie, implements ActionListner).
Then when you cycle through your buttons in your loop, you can just say
ledBtns[i][j].addActionListener(this).
Of course, then you have to figure out which object was the source of the event (usually by using if...else blocks). Now that could get unwieldy for 64 objects, but for lesser items, it isn't usually a problem.
Or, you could have the actionPerformed method call change image and pass in the button object, etc to do your work on.
What I've suggested is just another option. I'd do what makes the most sense for your code and is the cleanest and most readable.

Categories