get access to each dynamically added jComponent - java

Before I already asked question and could get value from dynamically added jTextFields and jComboBoxes using this answer for my question.
Now in my subPanel I have 3 jComboBoxes and 4 jTextFields.
To get value of jComponent I am using this code:
Component[] children = jPanel1.getComponents();
// iterate over all subPanels...
for (Component sp : children) {
if (sp instanceof subPanel) {
Component[] spChildren = ((subPanel)sp).getComponents();
// now iterate over all JTextFields...
for (Component spChild : spChildren) {
if (spChild instanceof JTextField) {
String text = ((JTextField)spChild).getText();
System.out.println(text);
}
}
}
}
I would like to ask is it possible to access to each jComboBoxes and jTextFields separately, i.e. can I manipulate each jComponent and set them different values? How can I achieve this?
Thank you in advance.

I would like to ask is it possible to access to each jComboBoxes and jTextFields separately, i.e. can I manipulate each jComponent and set them different values? How can I achieve this?
Rather than traversing the Component hierarchy (which is fragile to Layout changes), you can keep references to your Components. The following example is a class that contains instance variables for the Child components:
public class ComponentWrapper extends JComponent{
private JComboBox combo;
private JTextArea textArea;
public ComponentWrapper(){
combo = new JComboBox();
textArea = new JTextArea();
add(combo);
add(textArea);
}
public Text getTextArea(){
return textArea;
}
public JComboBox getComboBox(){
return comboBox;
}
}
The above class extends JComponent, adds the components within the constructor, and can be added to another Container elsewhere. Note the above class is just an example for how to do this, and may need to be further adapted depending upon your requirements. Usage:
ComponentWrapper wrapper = new ComponentWrapper ();
add(wrapper);
revalidate();//if adding 'dynamically'
//later, when you want to get the text
String text = wrapper.getTextArea().getText();

Since both of those classes (JComboBox and JTextField) extend JComponent you can make an ArrayList and add them to it. i.e.
ArrayList<JComponent> components = new ArrayList<JComponent>();
JComboBox pie = new JComboBox();
components.add(pie)
//pie is now stored in components as a JComponent
When you need to reference pie you can call:
JComboBox pie = (JComboBox) components.get(0);
This can be done with any JComponent and to reference it you just simply cast it on it's way out. This method, however, can lead to some confusion. So you should either remember the order you add them, or add them in a very specific way (i.e TextFields first, then ComboBoxes).

Related

Show object members inside JScrollPane

I have a list of objects using the default list model, like so:
public static DefaultListModel<Loan> loans = new DefaultListModel<Loan>();
My Swing application looks like this:
When the Add button is clicked, it creates a Loan object and adds that object to the DefaultListModel, like so:
addLoan.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Loan loan = Calculator.createLoan(
Double.parseDouble(loanAmount.getText()),
Double.parseDouble(interestRate.getText()),
Double.parseDouble(term.getText())
);
Calculator.addALoan(loan);
}
});
With my code now, the objects actually appear in the JScrollPane fine. You can see below:
JList loanList = new JList(Calculator.loans);
loans = new javax.swing.JScrollPane(loanList);
The above code happens in my init method. This is what I see on the frontend:
I want to tab it out and show each of the members inside that box (scroll pane). A Loan object consists of amount, interest rate and term. I tried something similar to (How to dd an object to the JList and show member of the object on the list interface to the user?) but I am confused on how the renderer in the accepted answer shows up.
You are seeing the result of Swing’s default behavior. Swing is calling toString() on each Loan object.
You can override that method to return something suitable for display:
public class Loan {
// (property methods)
#Override
public String toString() {
return String.format("%12s %6s %,.1f years",
NumberFormat.getCurrencyInstance().format(
getAmount()),
NumberFormat.getPercentInstance().format(
getInterestRate() / 100),
getTerm());
}
}
Since JList uses a proportional width font on most platforms, the values from the loan objects probably won’t line up with each other. To address that, remove your JList, remove your list model, and instead subclass AbstractTableModel and use a JTable as camickr suggested.

Vaadin 14 grid with checkboxes as rendered components

I created a grid that has four columns. The first column shows a name, the other three columns represent different roles. Each of those three columns is filled with check boxes in order to assign a specific role to a specific name. That's as far as i have come so far.
In each column and in each row there should only be one selected checkbox allowed. So in total i do have exactly one selection per checkbox column. How do i implement this?
Edit: I realize I might have misunderstood the question entirely. If you want to have 3 columns, with each having multiple CheckBoxes where only 1 can be selected per column, then you should use a RadioButtonGroup in each column and bind each to a different Enum field of your griditem class.
Instead of showing how to do three columns with a CheckBox in each, while only one CheckBox can be selected, I will show a different way to achieve the same information about the item.
The reason for this is the solution that what you want to achieve is not easily doable, since each CheckBox is defined in a scope that does not know the other Checkboxes of the same item. Therefore you would need to implement your only-one-selected rule inside the itemclass' setters, which is not optimal. I mean, it is possible, but I'd rather change the structure to something more apt. Usually you don't want to put this kind of business logic into your bean classes.
How would I solve the problem at hand?
Create a new Enum, which will replace all 3 boolean fields in your item class. Now in your grid you will only need one column for a ComboBox to select the enum.
I chose an Enum because this matches your needs here perfectly. With an Enum, you may have several options, but you can select only one (or none).
To better show what I mean, let's use an example Class for the Grid items, Foo. Your version has 3 booleans which your three grid-CheckBoxes are bound to. Let's call them isA, isB, isC.
// your version of the griditem class
public class Foo {
private boolean isA, isB, isC = false;
// constructor, getters, setters
}
// how the columns are added in the grid (approximately) (without editor):
Grid<Foo> grid = new Grid<>();
grid.addComponentColumn((item) -> {
CheckBox checkBox = new CheckBox();
checkBox.setValue(item.isA());
checkBox.addValueChangeListener(event -> item.setA(event.getValue()); // inside setA() method you need to set isB and isC to false if the new value is true. No good!
return checkBox;
});
grid.addComponentColumn((item) -> {
CheckBox checkBox = new CheckBox();
checkBox.setValue(item.isB());
checkBox.addValueChangeListener(event -> item.setB(event.getValue()); // inside setB() method you need to set isB and isC to false if the new value is true. No good!
return checkBox;
});
grid.addComponentColumn((item) -> {
CheckBox checkBox = new CheckBox();
checkBox.setValue(item.isC());
checkBox.addValueChangeListener(event -> item.setC(event.getValue()); // inside setC() method you need to set isB and isA to false if the new value is true. No good!
return checkBox;
});
And here is how it would look after my changes
public class Foo {
private AbcEnum abcEnum = null;
// constructor, getters, setters
}
public Enum AbcEnum {
A,
B,
C;
}
// how the columns are added (without editor):
Grid<Foo> grid = new Grid<>();
grid.addComponentColumn((item) -> {
ComboBox<AbcEnum> comboBox = new ComboBox<>();
comboBox.setValue(item.getAbcEnum());
comboBox.addValueChangeListener(event -> item.setAbcEnum(item.getValue()));
return comboBox;
});
I wrote "without editor" in the comments about adding the column, because this code will add the ComboBox/CheckBox as clickable and functional components for each grid item, without needing to open the editor for an item to change the values. If you are indeed using an editor, you can add these functional inputs as editorComponents (and bind them to the editor-binder instead of using setValue and addValueChangeListener), and show only the current value in the normal columns (not editable - therefore no inputs like CheckBox or ComboBox are required)

Java: Display two different JTables which are created from the same AbstractTableModel

I have a problem in the display of two different JTables which are created by the same AbstractTableModel. I don't really think that is important to show the code of the AbstractTableModel, but if I am asked for I may present it as well.
I just call two times the same class that extends this AbstractTableModel for two arraylists that I am using to create the tables.
final SwitchTableModel model = new SwitchTableModel(user_decide);
final SwitchTableModel model1 = new SwitchTableModel(duplicates);
JTable table = new JTable(model);
JTable table1 = new JTable(model1);
JFrame frame = new JFrame ("Results");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel grid = new JPanel();
grid.add(toolbar);
grid.add(toolbar1);
grid.add(table);
grid.add(table1);
frame.add(grid);
frame.pack();
frame.setVisible(true);
I also create the toolbars which are the same, I also think that this is irrelevant, that is why I don't post the code - I would do it if you think it is needed.
The problem is that in the end I see the same JTable two times, so I suppose it has something to do with the way that I call the class.
The problem comes from an inappropriate static keyword.:
public static int [][] data;
static means that the value of that variable will be the same for all your instances. Instead, put your data inside your SwitchTableModel and don't make it static. This will solve your issues immediately.
Something like:
public class SwitchTableModel extends AbstractTableModel {
private int[][] data;
//... the rest of your current code.
}
I think both the value passed to the SwitchTableModel user_decide and duplicates are having same values. Otherwise there is no issue in the above code.
Try adding two seperate scroll panes to your panel, then add the JTables to the scroll panes. Other then that, your not clear on what exactly your display problem is, wheather the JTables are not showing up, or if the data is not different... ect

Java: change a variable depending on dynamically generated text field name?

I have an ArrayList of TrainingClass objects with a variable "priority".
I am making a settings frame, where for each element currently in the ArrayList I make a TextField where the user sets priority.
This is how it is generated
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
}
Now I would add a change listener, but...
Once I know which field has been changed, how can I access the proper element of the ArrayList mTrainingClasses?
In php, for example, I would simply make something like:
$mTrainingClasses->$changed_field->setPriority($new_value);
But, as far as I understand, I can’t do this in Java. So, how should I proceed?
Do I need to manually set the field name and listener for each element? I’m sure there is some other solution, but I have no idea at this point.
(I know I could use an ArrayList for the fields as well, such as
txtPriority.add(new JTextField(3));
But in this case, how do I know which index corresponds to the field that has been changed?
)
Have a list of Text Fields
List<JTextField> textFields = new ArrayList<JTextField>();
Change the loop like the following where you add all text fields to above list
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
textFields.add(txtPriority);
}
In your listener you can do the following
mTrainingClasses.get(textFields.indexOf((JtextField) event.getSource()));
The above will return the TrainingClass which got changed.
There are several options:
Pass the TrainingClass element to the listener which you attach to the textfield. This will require to attach the listener in your for loop where you have access to both the TrainingClass and JTextField variable
Use a Map as suggested by #Ted Hopp
Use a List as you already suggested. Trick is to store an index in the JTextField so that afterwards you know which JTextField corresponds to which element in the List. You can use JComponent#putClientProperty and JComponent#getClientProperty for this.
You can use those JComponent#putClientProperty and JComponent#getClientProperty methods to store the TrainingClass variable directly
In your loop, you can populate a Map<JTextField, TrainingClass>. Then you can use that to look up the element from the changed field.
Map<JTextField, TrainingClass> fieldMap = new HashMap<>();
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
map.put(txtPriority, tclass);
}
Alternatively, you can subclass JTextField and declare a data field that you can then reference directly in event handling.
You need some kind of mapping between the JTextField and the TrainingClass. Either make text field a property of your class or make a map that maps the two.
Map<TrainingClass, JTextField> myMap= new HashMap<TrainingClass, JTextField>();
for (TrainingClass tclass : mTrainingClasses) {
//Loop the ArrayList
JTextField txtPriority = new JTextField(3);
txtPriority.setBounds(10,10,100,20);
txtPriority.setText("" + tclass.getPriority());
getContentPane().add(txtPriority);
// map the textField to the training class
myMap.put(txtPriority, tclass);
}
When the field changes inside the listener method, you'd simply call:
public void eventListenerMethod(InputEvent e) {
JTextField fieldThatGeneratedEvent= e.getSource();
TrainingClass tClass= myMap.get(fieldThatGeneratedEvent);
}

Accessing a JTextArea in a JScrollPane

I have a JTextArea in (multiple) JScrollPane in a JTabbedPane.
I need to access the JTextArea. If I didn't have the JScrollPane, I could do:
JTextArea c = (JTextArea)jTabbedPane1.getComponentAt(i);
How would I get it when in a JScrollPane?
Cheers,
Gazler.
Sounds like you'll get into a mess of references over there ( at least that's what have happened to me in the past ) .
I would suggest you to have a middle object in charge of those dependencies for you and to move the "business" methods there.
So instead of adding components and losing the references ( or worst, duplicating the references all over the place ) you can use this object which will have the reference:
class AppMediator {
private JTextArea area;
private JTabbetPane pane;
// etc.
public void doSomethingWithText() {
this.area.getText(); // etc
}
}
See the Mediator design pattern. The focus is to move all the "view" objects from where they are ( usually as references in subclasses ) to a common intermediate object.
This line looks complex, but I THINK this would do it.
JTextArea c = (JTextArea) (((JViewportView) (((JScrollPane) jTabbedPane1.getComponentAt(i)).getViewport()))).getView();
But I think it would be more interesting to store your TextArea's in an ArrayList.
So you can do this:
List<JTextArea> listAreas = new ArrayList<JTextArea>();
...
JTextArea c = listAreas.get(i);
Create a new one is something like this:
JTextArea c = new JTextArea();
jTabbedPane1.addTab("Title", new JScrollPane(c));
listAreas.add(c);
Hope this helps.
I prefer the AppMediator approach but you could also do
scrollPane.getViewport().getView()

Categories

Resources