Working on a GUI in Eclipse using WindowBuilder and ran into a roadblock..
I've created a JWindow with a drop-down box intended to display a list of people from a people array. The structure of my classes are:
public class Person {
String name;
int age;
ArrayList<Goal> goals;
}
public class Goal {
String name;
int daysToComplete;
}
Within this JWindow GUI, the drop-down box lists out all of the Person instances. Once I select a person (let's say Bob) - I want to dynamically create labels and JTextAreas to list out Bob's attribute values, for example:
Name: Bob
Age: 20
Goals:
- Goal 1, complete in X days
- Goal 2, complete in Y days
and so on.. I don't want to statically add 3 labels (Name, Age, Goals) and their respective JTextAreas (Bob, 20, Goal 1/Goal 2), because the structure of Person will likely change in the future.
What is the best way to do this?
Thanks!
If I'm understanding you correctly, you can get what you want by creating anonymous instances of JLabel and JTextArea and placing them into an array list. I don't know the specifics of your environment, but hopefully, you can follow the idea:
ArrayList<JLabel> nameLabelList = new ArrayList<JLabel>();
ArrayList<JLabel> ageLabelList = new ArrayList<JLabel>();
ArrayList<Goal> goalList = new ArrayList<Goal>();
// Event handler method
public void personSelected(person)
{
nameLabelList.add(person.name);
ageLabelList.add(person.age);
// This assumes each person has a single goal. You can adapt the code
// for multiple goals easily
goalLabelList.add(person.goal);
}
Then, after the lists are created, all you have to do is loop through these array lists and spit them out into your UI:
for(int counter = 0; counter < nameLabelList.size; counter++)
{
myContainer.add(nameLabelList.get(counter));
myContainer.add(ageLabelList.get(counter));
myContainer.add(new JLabel(goalList.get(counter).toString()));
}
After adding the contents of the array lists, make sure that they show up in the UI:
myContainer.revalidate();
myContainter.repaint();
You will have to add new Panels to your Main Panel. So your Main Panel should provide a scrollable inner Panel to which you add new lines. If you have ever worked with HTML and Tables you will understand what I am talking about.
Once you got this attribute-show-panel (inner Panel), you can load as many attributes into it with a for loop as you want.
The code would technically be like for each goal in goalArray -> add new linePanel to attribute-show-panel.
whereas a linePanel yould store a label, abutton, etc and many line Panels would list vertically
I'm trying to create a Java GUI dynamically by taking values from a result set and using it to generate a checklist. I've created a small demo program to demonstrate what I've done:
SQL Commands
CREATE USER 'test'#'localhost' IDENTIFIED BY 'testpw';
CREATE DATABASE combotest;
USE combotest;
CREATE TABLE combotable (
id INT(5) NOT NULL PRIMARY KEY auto_increment,
type VARCHAR(50) NOT NULL);
INSERT INTO combotable (id, type) VALUES
(default, 'Label'),
(default, 'Textfield'),
(default, 'Combo'),
(default, 'Label'),
(default, 'Textfield'),
(default, 'Combo'),
(default, 'Combo');
GRANT SELECT ON combotest.* TO 'test'#'localhost';
For your convenience if you'd like to test it yourself I've put all the SQL commands above.
Now, for my Java code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.*;
import javax.swing.*;
public class resToComboDemo implements ActionListener {
//JDBC Variables
static Connection connect = null;
static Statement statement = null;
static ResultSet res = null;
#SuppressWarnings("rawtypes")
//Other Variables
JComboBox comboBox;
JButton submit;
JFrame frame;
JLabel label;
JTextField textField;
Container pane;
public static void main(String[] args) throws SQLException {
new resToComboDemo();
}
public resToComboDemo() throws SQLException {
try {
Class.forName("com.mysql.jdbc.Driver");
// Setup the connection with the DB
connect = DriverManager
.getConnection("jdbc:mysql://localhost/combotest?"
+ "user=test&password=testpw");
statement = connect.createStatement();
//Note: in this specific case I do realize that "order by id" is not necessary. I want it there, though.
res = statement.executeQuery("SELECT * FROM combotable ORDER BY id");
createStuff(res);
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Error 1: "+e, "Error!", JOptionPane.ERROR_MESSAGE);
} finally {
connect.close();
}
}
#SuppressWarnings({"rawtypes", "unchecked" })
public void createStuff (ResultSet res) throws SQLException {
frame = new JFrame("Testing dynamic gui");
Dimension sD = Toolkit.getDefaultToolkit().getScreenSize();
int width = sD.width;
int height = sD.height - 45;
frame.setSize(width,height);
pane = frame.getContentPane();
pane.setLayout(new GridLayout(0, 2));
while (res.next()) {
Object[] options = { "Pass", "Fail"};
String type = res.getString("type");
JLabel label = new JLabel("<html><small>"+type+"</small></html>");
JLabel blank = new JLabel(" ");
blank.setBackground(Color.black);
blank.setOpaque(true);
if (type.equals("Label")) {
label.setBackground(Color.black);
label.setForeground(Color.white);
label.setOpaque(true);
pane.add(label);
pane.add(blank);
} else if (type.equals("Combo")) {
pane.add(label);
comboBox = new JComboBox(options);
pane.add(comboBox);
} else if (type.equals("Textfield")) {
pane.add(label);
textField = new JTextField(20);
pane.add(textField);
}
}
JLabel blank2 = new JLabel(" ");
pane.add(blank2);
submit = new JButton("Submit");
submit.addActionListener(this);
pane.add(submit);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
Now, everything works great with creating the GUI here. However, I need to be able to treat the Combobox and Textfield components as their own separate entities. Meaning, I want to be able to get user input from each different component. Right now, if I were to request information from textfield, it just gives me the information from the last textfield. This makes perfect since, because that's how java reads it. I have no problem with that.
I just can't for the life of me figure out how to get each component's input separately. Perhaps by taking the result set and adding the results to some type of array? I've attempted this multiple times in different flavors and I can't get it to come out the way I need it to. Some of you are going to request that I show you what I've tried... but honestly, it's not worth it.
And, before anybody asks: No, I will not use FlowLayout. :)
Any help is greatly appreciated!
There are probably a few ways to achieve this based on what you want to do...
If you are only performing a batch update, you could use a Map keyed to the id of the row and mapping to the Component.
This way, when you want to save the values back to the database, you would simply iterate the Maps key values, extract the Component associated with each key and then extract the value of the Component...
I might consider making a wrapper interface which has a simple getText method and wrap the component within it, making the implementation of the wrapper responsible for extracting the text, but that's just me ;)
If you want to perform updates when a individual component is updated, you would need to swap the mapping, so that the Component would the key and the id would be mapped to it.
This would mean that when some kind of event occurred that would trigger and update (ie a ActionEvent), you could extract the source from the event and look up the id in the Map based on the Component that caused the event...
Now...frankly, I would simply use a JTable and create a custom TableModel which could model all this.
This would require you to create POJO of the table, maintaining the id, type and value within a single object. This would define a basic row in the table.
The only problem is you would need to create a (reasonably) complex TableCellEditor that could take the type and return an appropriate editor for the table. Not impossible, it's just an additional complexity beyond the normal usage of a table.
This would all the information you need is available in a single object of a single row in the table.
Take a look at How to use tables for more details
Equally, you could use a similarly idea with the Map ideas above...
You could also simply create a self contained "editor" (extending from something like JPanel), which maintain information about the id and type and from which you could extract the value and simply keep a list of these....for example...
what about interrogating the Container ( pane) which contains the components
getComponents() method and loop through the sub component and check for JComobox and JTextField do the required cast and retrieve the value
Just an idea in case you are against adding the sub-components into a kind of list
You only have a reference to the last text field or combo box that you create, since you are reusing the variables that hold them. I would put them in an ArrayList, store each new text field and combbox as you create them, then you can go back and get input from all of them after you're done.
---------- (after the OP's response to the above paragraph)
No, there is no "place to refer you" -- it's your set of requirements, it would be pretty remarkable to find code that already existed that did this exact thing. Java and Swing give you the tools, you need to put things together yourself.
You don't show your "actionPerformed" routine, but let's hypothesize about it for a minute. It is called by the framework when an action is done, and it is passed an "ActionEvent" object. Looking through its methods, we find that it has "getSource()", so it will give you a reference to the component which generated the event.
Let's further think about what we have -- a set of components in the UI, and ones which can generate events are interesting to us. We want to, in this case, retrieve something from the component that generated the event.
If we have the component (from actionEvent.getSource()) and we want to do something with it, then we can, at worst do something like the following in the actionPerformed() method:
Component sourceComponent = actionEvent.getSource();
if (sourceComponent instanceof JComboBox)
{ JComboBox sourceBox = (JComboBox) sourceComponent;
// get the value from the combo box here
}
else if (sourceComponent instanceof JTextField)
{ JTextField sourceTextField = (JTextField) sourceComponent;
// get the value from the text field here
}
// or else do nothing -- our action was not one of these.
Done this way, you don't even need to keep a list of the components -- the UI is keeping a reference to all of them, and you just use that reference when the actionEvent occurs.
Now, this is not the only or even the best or the simplest way of doing this. If you wanted to extend JComboBox and JTextField with your own classes, you could have those classes both implement an interface that defined something like getValue() or getText; then you would not need the ugly instance of operator, which can usually be done away with by better design and planning.
I'm having troubles making a second call to load a Google Map in a GWT app. The problem itself is that once the map is called, it won't fit the container size. This is a usual problem, as depicted in many previous SO questions:
Here
Here
Here
Here
Let me state that I've tried all of the above and sadly nothing seems to work. I must also say I'm using the unofficial version of GWT Maps API v3, which can be found here. Thus, this is the problem:
Now, weird enough, if I change the browser size, map displays correctly:
Thus, it looks like I need to "dispatch" the onResize event somehow...but I tried with all of the above methods and nothing seemed to work. Just for clarification this, is the part where I construct the map and add it to the container:
private void buildMapMarinesPark() {
//Visualizar datos...
LatLng center = LatLng.newInstance(52.62715,1.7734);
MapOptions opts = MapOptions.newInstance();
opts.setZoom(9);
opts.setCenter(center);
opts.setMapTypeId(MapTypeId.HYBRID);
MapTypeControlOptions controlOptions = MapTypeControlOptions.newInstance();
controlOptions.setMapTypeIds(MapTypeId.values()); // use all of them
controlOptions.setPosition(ControlPosition.TOP_RIGHT);
opts.setMapTypeControlOptions(controlOptions);
mapMarinePark = new MapWidget(opts);
mapMarinePark.setSize("100%", "100%");
// Add some controls for the zoom level
List<EuropeanMarineParkDataEntity> parksPerAnio = null;
listPolygon = new ArrayList<Polygon>();
Polygon poly = null;
for(int i=2003;i<=ANIO_MAP_PARK;i++){
parksPerAnio = this.hashAnioParks.get(""+i);
if(parksPerAnio != null){
for(EuropeanMarineParkDataEntity emp : parksPerAnio){
poly = this.createPolygon(emp);
poly.setMap(mapMarinePark);
listPolygon.add(poly);
}
}
}
((Element)DOM.getElementById("currentYear")).setPropertyObject("innerHTML", ""+(ANIO_MAP_PARK));
// Add the map to the HTML host page
final DockLayoutPanel dock = new DockLayoutPanel(Unit.PX);
dock.addNorth(mapMarinePark, 500);
RootPanel.get("mapContainerProfile2").add(dock);
RootPanel.get("timeline").setVisible(true);
RootPanel.get("mapPanel2").setVisible(true);
RootPanel.get("gadget_marinepark").setVisible(true);
mapMarinePark.triggerResize(); --> Does not work!
onLoadedMapMarinePark();
}
I guess you try to draw the map when the DOM hasn't been fully constructed and the wrong dimensions are retrieved.
Try to draw/create the map in a callback of a
Scheduler.get().scheduleDeferred() call.
Update:
Also you are mixing a DockLayoutPanel with a RootPanel.
That will cause issues. Use a RootLayoutPanel instead.
Construct the DOM normally and at the point where you normally add your map to the DockLayoutPanel call scheduleDeferred() and add the map to the panel in the callback
I m working with GWT 2.4 on an new application. I made a docklayoutpanel and I inserted a celllist on the west section of it. I need to create an event, every time a user clicks on an element of celllist on the west side of page a specific widget will load at the content of the docklayoutpanel.
Any suggestions?
Thank you
The following example should be self explanatory
// Create a cell to render each value.
TextCell textCell = new TextCell();
// Create a CellList that uses the cell.
CellList<String> cellList = new CellList<String>(textCell);
cellList.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED);
// Add a selection model to handle user selection.
final SingleSelectionModel<String> selectionModel = new SingleSelectionModel<String>();
cellList.setSelectionModel(selectionModel);
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
public void onSelectionChange(SelectionChangeEvent event) {
String selected = selectionModel.getSelectedObject();
if (selected != null) {
Window.alert("You selected: " + selected);
}
}
});
Instead of Window.alert("You selected: " + selected); you will need to change the widget shown on the eastern side of your panel.
This can be done in several ways, one of which is to expose the Dockpanel to the Selection Change Event either by declaring the Panel as a field to the class (not a local Variable in the constructor of the class) or as a final local variable in the constructor.
Another way is to do this by event handling. The eventBus methodology on the MVP design pattern is the proper way to do all thesee here for more information.
I am using LWUIT for getting a search facility for selection in the List.
Now I want to know how can I display the list with CheckBoxes?
list=new List(vector);
cform.addComponent(list);
cform.addComponent(t);
cform.show();
I don't know if there is a more simple solution then mine, but mine is highly customizable and can serve for a lot of purposes.
List l = new List;
Vector v = new Vector();
for(int i = 0; i < 10; ++i){
v.addElement(new CheckItem("itemtekst"));
}
l.setListCellRenderer(new CheckItemRenderer());
l.setModel(new CheckItemModel(v));
the code above makes it work. As you can guess you have to make a new class and override two to make it work.
CHECKITEM: this class has a string and an image. as well as setters and getters. it also has a boolean that shows if it is checked or not.
CHECKITEMRENDERER: has a label for the string and the image of the checkitem it extends Container and implements ListCellRenderer
CHECKITEMMODEL: this extends the defaultlistmodel. it has methods to get the checkeditems and setthem checked or unchecked.
to recap:
set the correct items in the vector
set the correct renderer
set the correct model
and to use it add an actionlistener or it will al be for nothing.