I am making a chat application in Java. They can add friends and chat with them.
Here is my add friend JFrame idea:
I tried to google search Multi jpanel in one jscrollpane but I found nothing. I ended up with custom JTable. I want to create a custom JTable which have JLabels in different position in each slot. Users can just select a slot in JTable, then they can use the JButton below to chat with them.
Is it possible to do this in Java. Is yes, please share your idea. Thanks.
Here is a proposal. But it contains some flaws yet
Size propagation is fiexed (may be better client or server based sizing)
No event delegation to underlying Component
Performance, because heavyweight panel instances are created frequently inside paint loop
But here is how it can be done. The code is separated into parts.
A value class
Just a simple class to represent the date inside your panels for each cell.
class Value {
public final String text;
public final boolean flag;
public Value(String text, boolean flag) {
this.text = text;
this.flag = flag;
}
}
My individual panel class
The representation can be modelled within a gui editor like google's window builder. This panel makes use of the value and displays it accordingly.
public class MyPanel extends JPanel {
public MyPanel(Value v) {
JLabel lblNewLabel = new JLabel(v.text);
add(lblNewLabel);
JCheckBox chckbxSomeValue = new JCheckBox("some value");
chckbxSomeValue.setSelected(v.flag);
add(chckbxSomeValue);
}
}
A table cell renderer class
Just returns some panel instance showing up the desired values.
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
class MyPanelCellRenderer implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
return new MyPanel((Value)value); // maybe performance problem
}
}
A custom table model
import javax.swing.table.DefaultTableModel;
class MyTableModel extends DefaultTableModel {
public MyTableModel() {
super(new Object[][] {
new Object[] { 1, new Value("asdf", true) },
new Object[] { 2, new Value("qwer", false) } },
new String[] {"Id", "MyPanel" });
}
Class[] columnTypes = new Class[] { Integer.class, Value.class };
MyTableModel(Object[][] data, Object[] columnNames) {
super(data, columnNames);
}
public Class getColumnClass(int columnIndex) {
return columnTypes[columnIndex];
}
}
A frame class
import java.awt.BorderLayout;
public class MyFrame {
JFrame frame;
private JTable table;
public MyFrame() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
table = new JTable();
table.setModel(new MyTableModel());
table.setFillsViewportHeight(true);
table.getColumnModel()
.getColumn(1)
.setCellRenderer(new MyPanelCellRenderer());
table.setRowHeight(40); // static sizing
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(table);
}
}
Main Function
import java.awt.EventQueue;
public class MyApp {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MyFrame window = new MyFrame();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
The final result
The panel MyPanel is created using eclipse and google's window builder
This is a completely different approach using neither a table nor a list. It uses panels as items arranged as a list inside of another panel. This solution doesn't requires any further modification to delegate any events to the underlying controls. The events are processed natively. The idea comes from this post. I have separated the logic into two parts for now.
JPanelList class
This class is a JPanel and maintains a list of such. ATM they can be added using the methods addPanel and addPanels.
public class JPanelList extends JPanel {
private static final long serialVersionUID = 1L;
private JPanel mainList;
private List<JPanel> panels = new ArrayList<JPanel>();
public JPanelList() { this(new ArrayList<JPanel>()); }
public JPanelList(List<JPanel> panels) {
setLayout(new BorderLayout());
mainList = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.weighty = 1;
mainList.add(new JPanel(), gbc);
add(new JScrollPane(mainList));
addPanels(panels);
}
public void addPanels(List<JPanel> panels) {
for (JPanel panel : panels)
addPanel(panel);
}
public void addPanel(JPanel panel) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
mainList.add(panel, gbc, 0);
panels.add(panel);
validate();
repaint();
}
}
JListFrame test class with main function
public class JPanelListFrame {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
JPanelListFrame window = new JPanelListFrame();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public JPanelListFrame() {
frame = new JFrame();
frame.setBounds(100, 100, 210, 192);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanelList panelList = new JPanelList();
frame.getContentPane().add(panelList, BorderLayout.CENTER);
JButton btnAddMypanel = new JButton("Add MyPanel");
btnAddMypanel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// just add a MyPanel with a Value containing a
// "asdf" and a random boolean
panelList.addPanel(new MyPanel(new Value("asdf",
(int) (2 * Math.random()) % 2 == 0)));
}
});
panelList.add(btnAddMypanel, BorderLayout.SOUTH);
}
}
One drawback with this solution is that the selection mechanics are lost. When selecting elements is a key requirement maybe a JList might be more appropriate. Another thing is that the items are not rendered as they would be rendered inside a JList or JTable e.g. using fancy borders. This can be solved by somehow adding a border decorator before adding the panels into the mainList of the JPanelList.
Related
My problem is that a JTable does not update when I select my combobox. The program I present below should delete all data (data = null;), when LA is selected. The table does not update.
public class minimumExample extends JFrame {
private JTabbedPane tabbedPane;
private FilteredTabPanel filteredTabPanel;
public void createTabBar() {
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
filteredTabPanel = new FilteredTabPanel();
tabbedPane.addTab("Test", filteredTabPanel.createLayout());
add(tabbedPane);
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
}
private void makeLayout() {
setTitle("Test App");
setLayout(new BorderLayout());
setPreferredSize(new Dimension(1000, 500));
createTabBar();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public void start() {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
makeLayout();
}
});
}
public static void main(String[] args) throws IOException {
minimumExample ex = new minimumExample();
ex.start();
}
public class FilteredTabPanel extends JPanel {
private JPanel selectionArea;
private JLabel lCity;
private JComboBox cityBox;
private JTable filterTable;
String[] columnNames = {"Cities"};
String[][] data = {
{"NY"}, {"NY"}, {"NY"}, {"NY"}, {"LA"}, {"LA"},{"Columbia"},{"DC"},{"DC"},{"DC"},{"DC"},{"DC"},{"DC"}
};
private JScrollPane scrollPane;
public JPanel createLayout() {
JPanel panel = new JPanel(new GridLayout(0, 1));
//add panels to the layout
panel.add(addButtons());
panel.add(showTable());
repaint();
revalidate();
return panel;
}
public JPanel addButtons(){
selectionArea = new JPanel(new FlowLayout(FlowLayout.LEFT));
lCity = new JLabel("City");
String[] fillings = {"NY", "LA", "Columbia", "DC"};
cityBox = new JComboBox(fillings);
cityBox.addActionListener(new ActionListener() {
private String cityFilter;
#Override
public void actionPerformed(ActionEvent arg0) {
//2. get data
cityFilter = cityBox.getSelectedItem().toString();
if(cityFilter.equals("LA")) {
data = null;
}
showTable();
repaint();
}
});
selectionArea.add(lCity);
selectionArea.add(cityBox);
selectionArea.repaint();
return selectionArea;
}
private JScrollPane showTable() {
filterTable =new JTable(data, columnNames);
filterTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
scrollPane = new JScrollPane(filterTable);
scrollPane.repaint();
scrollPane.validate();
return scrollPane;
}
}
}
As you can see the table does not update. Any recommendations what I am doing wrong?
Instead of creating new instance of you objects by calling showTable (which never get added to the screen in any way), which is just going to completely mess up your object references, try resetting the TableModel, for example...
if ("LA".equals(cityFilter)) {
filterTable.setModel(new DefaultTableModel(null, columnNames));
}
Take a closer look at How to Use Tables for more details
I've been trying to figure out CardLayout with action listeners on button
(so like - starts on a load-up page- and on a button click switches to a different "card"
my code won't even run right now i'm not entirely sure why - most implementations I can find use ItemListeners and Combo Boxes
The basic process I've done is create a master JPanel, but my cards JPanel onto the master JPanel, but my different cards into the cards JPanel, then add the master JPanel to the frame to display...
Also, for one of my cards I only need to display a picture - I previously implemented this by just creating a new pop-up window but It would be nice to be able to switch the frame to show it... I don't know why I can't figure this out
Here's my code:
import java.awt.*;
/**
* Game
* Main class that specifies the frame and widgets of the GUI
*/
public class Game implements Runnable {
public void run(){
final String ON_OPEN = "Welcome!"; //Opening frame
final String GAME = "Play!"; // Game Frame
final String STATS = "Stats"; // Post-Game Stat Frame
final String HELP = "Help"; //tutorial frame
JPanel cards = new JPanel();
JPanel master; // a panel for the card layout
final JFrame frame = new JFrame(ON_OPEN);
frame.setLocation(500,200);
//Create the master layout for the program
master = (JPanel) frame.getContentPane();
master.setLayout(new BorderLayout()); // creating master layout
//Create panel for all the cards in CardLayout
final CardLayout cLay = new CardLayout();
cards.setLayout(cLay);
// all the cards
final JPanel help = new JPanel();
final JPanel stats = new JPanel();
final JPanel game = new JPanel (new BorderLayout());
final JPanel open = new JPanel (new FlowLayout());
// setting up ON_OPEN layout - uses JPanel open
final ImageIcon img = new ImageIcon("Instructions.png", "My Instructions..."); // the image I want shown under HELP card
final JButton info = new JButton("Help");
info.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// cLay.show(help, HELP); // WHAT I NORMALLY SHOULD BE DOING, RATHER JUST MAKE A NEW FRAME FOR THIS THOUGH
// frame.pack();
final JFrame infoFrame = new JFrame("Tutorial");
infoFrame.setLocation(500,50);
JLabel tutorialImg = new JLabel(img);
// int w = img.getIconWidth();
// int h = img.getIconHeight();
//infoFrame.setSize(w, h);
infoFrame.pack();
infoFrame.add(tutorialImg);
infoFrame.setVisible(true);
}
});
open.add(info); // the open-tutorial button
//Add them to the cards JPanel
cards.add(open, ON_OPEN);
cards.add(help, HELP);
cards.add(stats, STATS);
cards.add(game, GAME);
//Add the cards panel to the Master layout panel
master.add(cards);
// This code is all commented out because I'm not sure what I'm doing here...
// frame.add(cards);
// cLay.show(cards, ON_OPEN);
// frame.add(open, BorderLayout.CENTER);
// Main playing area - I want this to be shown in the GAME card...
GridLayout tileGrid = new GridLayout(4,4);
final JPanel grid = new JPanel(tileGrid);
// game.add(grid, BorderLayout.CENTER);
// grid.setLayout(tileGrid);
// frame.add(grid, BorderLayout.CENTER);
// Input - holds typing box
// final JPanel status_panel = new JPanel();
// frame.add(cards, BorderLayout.CENTER);
// frame.add(open, BorderLayout.CENTER);
final JTextField typingArea = new JTextField();
typingArea.setFocusTraversalKeysEnabled(false);
typingArea.setEditable(true);
typingArea.setFocusable(true);
typingArea.requestFocus();
frame.add(typingArea, BorderLayout.SOUTH);
typingArea.addKeyListener(new KeyAdapter() {
public void keyPressed (KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) { // enter key is pressed
String userWord = typingArea.getText().toLowerCase();
typingArea.setText("");
}
}
});
final JLabel status = new JLabel("Running...");
// status_panel.add(status);
// Reset button
final JPanel control_panel = new JPanel();
frame.add(control_panel, BorderLayout.NORTH);
]
// Put the frame on the screen
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Game());
}
}
Problems:
Your code doesn't compile for us since we don't have the JLetterField class.
You're trying to add the JFrame's contentPane back on itself, and causes an exception and doesn't make sense.
Edit 1:
Your latest code shows you putting everything into one very large run() method but in doing so, you loose much and gain nothing.
I suggest getting rid of the Runnable interface, there's no need for it, and creating a true OOP compliant class, one with private fields and public and private methods.
Your actionPerformed method shows no attempt at changing the CardLayout's displayed card.
I suggest that you get rid of the code to show a new window and try to place card swapping code there.
Make your CardLayout and the card-displaying JPanel fields of the class so that other methods can access them and call their methods.
Edit 2:
For example the following code shows the swapping of cards using 3 JButtons. One to get the previous card, one to get the next card, and one to show how to get a specific card (here the 2nd):
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class CardLayoutEg extends JPanel {
private static final String[] CARD_LABELS = { "one", "two", "three", "four",
"five", "six", "seven", "eight", "nine", "ten" };
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private CardLayout cardlayout = new CardLayout();
private JPanel cardHolder = new JPanel(cardlayout);
private Action[] actions = { new ShowPreviousAction(), new ShowNextAction(),
new ShowTwoCardAction() };
public CardLayoutEg() {
for (String cardLabelText : CARD_LABELS) {
JLabel cardLabel = new JLabel(cardLabelText, SwingConstants.CENTER);
cardHolder.add(cardLabel, cardLabelText);
}
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0));
for (Action action : actions) {
btnPanel.add(new JButton(action));
}
setLayout(new BorderLayout());
add(cardHolder, BorderLayout.CENTER);
add(btnPanel, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private class ShowPreviousAction extends AbstractAction {
public ShowPreviousAction() {
super("Previous");
}
#Override
public void actionPerformed(ActionEvent e) {
cardlayout.previous(cardHolder);
}
}
private class ShowNextAction extends AbstractAction {
public ShowNextAction() {
super("Next");
}
#Override
public void actionPerformed(ActionEvent e) {
cardlayout.next(cardHolder);
}
}
private class ShowTwoCardAction extends AbstractAction {
public ShowTwoCardAction() {
super("Show Two");
}
#Override
public void actionPerformed(ActionEvent e) {
cardlayout.show(cardHolder, CARD_LABELS[1]);
}
}
private static void createAndShowGui() {
CardLayoutEg mainPanel = new CardLayoutEg();
JFrame frame = new JFrame("CardLayout Example");
frame.setDefaultCloseOperation(JFrame.EXIT_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();
}
});
}
}
I have a problem with JTable/JScrollPane. My data table is not refreshing/updating. I am using DefultTableModel and according to the code everything is fine and I don't have any errors. Also I have a table with paging and that's why I am using action listeners and buttons "prev" and "next". I am passing from other function to function that is coded in class where is JTable. Problem is that I fill arrays which contains data for table but table won't update/refresh it. Here is my code. Thanks advance.
BIG EDIT Old code was removed. I added new codes that will help you guys/girls to understand problem that I have. Hope that this will help. Regards.
First here is class that show gui:
import javax.swing.*;
public class Glavni {
public static void main(String[] args) {
// TODO Auto-generated method stub
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui Scanner = new gui();
Scanner.setVisible(true);
}
});
}
}
Second here is class that pass String to gui class that contains jtable
public class passDatatoTable {
public void passData(){
String str1,str2,str3,str4;
gui SendStringsToGUI = new gui();
for (int i =0;i<=10;i++){
str1="Column 1 of row: "+i;
str2="Column 2 of row: "+i;
str3="Column 3 of row: "+i;
str4="Column 4 of row: "+i;
SendStringsToGUI.WriteMonitorData(str1, str2, str3, str4);
}
}
}
Next here is declaration of gui (contructor):
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class gui extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
String[][] data = new String[100][4];
String[] columnNames = new String[]{
"IP", "PC_NAME", "ttl", "db"
};
DefaultTableModel model = new DefaultTableModel(data,columnNames);
JTable table = new JTable(model);
JScrollPane scrollPane = new JScrollPane(table);
int i=0;
public void WriteMonitorData (String IP, String PC_NAME, String ttl, String gw)
{
System.out.println(IP);//just for testing (check if data was passed)
model.setValueAt(IP, i, 0);
model.setValueAt(PC_NAME, i, 1);
model.setValueAt(ttl, i, 2);
model.setValueAt(gw, i, 3);
i++;
model.fireTableDataChanged();
table.repaint();
scrollPane.repaint();
}
gui(){
JButton addData= new JButton("Add Data");
JButton next = new JButton("next");
JButton prev = new JButton("prev");
addData.addActionListener(this);
next.addActionListener(this);
prev.addActionListener(this);
JPanel panel = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel();
buttonPanel.add(addData);
buttonPanel.add(prev);
buttonPanel.add(next);
panel.add(buttonPanel, BorderLayout.SOUTH);
panel.add(table.getTableHeader(), BorderLayout.PAGE_START);
panel.add(scrollPane, BorderLayout.CENTER);
getContentPane().add(panel);
}
Here is actionListeners:
public void actionPerformed(ActionEvent e) {
if ("Add Data".equals(e.getActionCommand())){
passDatatoTable passSomeData = new passDatatoTable();
passSomeData.passData();
}
if ("next".equals(e.getActionCommand())) {
Rectangle rect = scrollPane.getVisibleRect();
JScrollBar bar = scrollPane.getVerticalScrollBar();
int blockIncr = scrollPane.getViewport().getViewRect().height;
bar.setValue(bar.getValue() + blockIncr);
scrollPane.scrollRectToVisible(rect);
}
if ("prev".equals(e.getActionCommand())) {
Rectangle rect = scrollPane.getVisibleRect();
JScrollBar bar = scrollPane.getVerticalScrollBar();
int blockIncr = scrollPane.getViewport().getViewRect().height;
bar.setValue(bar.getValue() - blockIncr);
scrollPane.scrollRectToVisible(rect);
}
}
Your first snippet shows this:
JTable table = new JTable(model);
but your gui() constructor shows:
JTable table = new JTable(data, columnNames);
You initiate the table twice. Once using the TableModel (JTable(TableModel tm)) the next using JTable(int rows,int cols) this is not good, initiate the JTable once in the constructor:
gui() {
DefaultTableModel model = new DefaultTableModel(data,columnNames);
JTable table = new JTable(model);
JScrollPane scrollPane = new JScrollPane(table);
JButton next = new JButton("next");
JButton prev = new JButton("prev");
next.addActionListener(this);
prev.addActionListener(this);
JPanel panel = new JPanel(new BorderLayout());
JPanel buttonPanel = new JPanel();
buttonPanel.add(prev);
buttonPanel.add(next);
panel.add(buttonPanel, BorderLayout.SOUTH);
panel.add(table.getTableHeader(), BorderLayout.PAGE_START);
panel.add(scrollPane, BorderLayout.CENTER);
getContentPane().add(panel);
}
UPDATE:
Here is an example that has a thread which will start 2.5 secinds after the UI is visible and change a value of the JTable:
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class Test extends JFrame {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().createAndShowUI();
}
});
}
private void createAndShowUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
frame.pack();
frame.setVisible(true);
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2500);
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
model.setValueAt("hello", 0, 0);
}
});
}
}).start();
}
static DefaultTableModel model;
private void initComponents(JFrame frame) {
String data[][] = {
{"1", "2", "3"},
{"4", "5", "6"},
{"7", "8", "9"},
{"10", "11", "12"}
};
String col[] = {"Col 1", "Col 2", "Col 3"};
model = new DefaultTableModel(data, col);
JTable table = new JTable(model);
frame.getContentPane().add(new JScrollPane(table));
}
}
From what I understand from the comments and the question, you have first created a DefaultTableModel by passing the data as arrays in the constructor
String[][] data = new String[100][4];
String[] columnNames = new String[]{
"IP", "PC_NAME", "ttl", "db"};
DefaultTableModel model = new DefaultTableModel(data,columnNames);
and you try to modify the table afterwards by adjusting those arrays. That will never ever have any effect, as the DefaultTableModel does not use those arrays. This can be seen in the source code of that class
public DefaultTableModel(Object[][] data, Object[] columnNames) {
setDataVector(data, columnNames);
}
which in the end comes down to
protected static Vector convertToVector(Object[][] anArray) {
if (anArray == null) {
return null;
}
Vector<Vector> v = new Vector<Vector>(anArray.length);
for (Object[] o : anArray) {
v.addElement(convertToVector(o));
}
return v;
}
So all the elements of the array are copied into an internal Vector and the array is no longer used.
Solution: do not update the arrays but update the DefaultTableModel. That class provides all the API you need to add/remove data to/from it.
We have a controller where we have a pre-declared JList and JLabel that we are adding to a JPanel. Outside of the initial layout/adding code I can update a JLabel (e.g. change its text) but I can't change the selection of the JList (e.g. jlist.setSelection(index) ) where it will update the UI. Code Below:
public class Test {
private JPanel myPanel;
private JList myList;
private JLabel myLabel;
public Test() {
//Some init code here...
this.myPanel = new JPanel();
this.myPanel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
String[] values = {"Value1", "Value2", "Value3", "Value4"}; //etc. etc.
this.myList = new JList(values);
this.myPanel.add(this.myList, gbc); //Add to panel
this.myLabel = new JLabel("Label1");
this.myPanel.add(this.myLabel, gbc); //Add to panel
//Add some code here to add it to a frame or something to display
}
public void updateLabel(String workingNewLabel) {
//The following works...
this.myLabel.setText(workingNewLabel);
// as in the label component in the JPanel will
//now be updated to the new value of workingNewLabel
}
public void changeSelectionInListToSomeIndex(int nonWorkingNewIndex) {
//The following does NOT update the JList in the panel...
//the selection remains whatever it was last set to.
this.myList.setSelectedIndex(nonWorkingNewIndex);
}
}
I've been able to get around this by iterating through all the components in myPanel looking for the JList component then set it to to myList eg.
//Code that goes after the line this.myPanel.add(this.myList, gbc);
for(Component component : this.myPanel.getComponents() ) {
//Iterate through it all until...
if (component.getClass() == this.myList.getClass()) {
this.myList = (JList) component; //cast the component as JList
}
}
Why do I need to do this for a JList but not for the JLabel? This is a workaround but it seems extremely hack-ish.
Thanks in advance!
-Daniel
#JB's right. Here's a working sscce:
/** #see http://stackoverflow.com/q/9540263/230513 */
public class Test {
private static Test test = new Test();
private JPanel myPanel;
private JList myList;
private JLabel myLabel;
public Test() {
myPanel = new JPanel();
myPanel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
String[] values = {"Value1", "Value2", "Value3", "Value4"};
myList = new JList(values);
myPanel.add(this.myList, gbc);
myLabel = new JLabel("Label1");
myPanel.add(this.myLabel, gbc);
myPanel.add(new JButton(new AbstractAction("Select Value3") {
#Override
public void actionPerformed(ActionEvent e) {
test.updateList(2);
}
}));
}
public void updateLabel(String label) {
myLabel.setText(label);
}
public void updateList(int index) {
myList.setSelectedIndex(index);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(test.myPanel);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
});
}
}
I'm trying to do as the title says.
I've got an efficient way for posting several of the same swing objects to a frame by storing them in an array and adding them using a for loop like so:
JLabel[] contrllabels= new JLabel[8];
contrllabels[0] = new JLabel("SCF Type: ");
contrllabels[1] = new JLabel("Units: ");
contrllabels[2] = new JLabel("Spherical Harmonics: ");
contrllabels[3] = new JLabel("Molecular Charge: ");
contrllabels[4] = new JLabel("PP: ");
contrllabels[5] = new JLabel("DFT Type: ");
contrllabels[6] = new JLabel("Max Iterations: ");
contrllabels[7] = new JLabel("Mult: ");
for(int i = 0;i<contrllabels.length;i++){
c.gridy = i;
frame.add(contrllabels[i],c);
}
But what if there are several swing objects of different types? I've got several combo boxes and textfields which I'd like to be added to the frame in a similar manner. I use gridbaglayout so if I don't use a for loop, I end up with lots of unnecessary code due to giving the constraints new values every time I want a different object added.
Is there such thing as an array of references which point to these different objects which I can then iterate through to add to the frame? Something like
JTextField tf = new JTextField(5);
JComboBox cb = new JComboBox("example");
Swing[] array = {tf,cb}
for(int i = 0;i<array.length;i++){
c.gridy = i;
frame.add(array[i],c);
}
I know such an array doesn't exist, but is there some way of implementing such a thing? It would greatly reduce the number of lines in my code and make it less confusing.
Thank you
You could use an array or collection of a common super type such as an array or ArrayList of JComponent. I'm curious if you're using a parallel array of GridBagConstraints to go with each component that is being added -- ugh. I often use arrays of components but usually if they are like components such as JLabel/JTextField pairs or a cluster of JRadioButtons.
As an aside, for my money, I try to avoid GridBagLayout as much as possible and instead nest containers that use the more coder-friendly layouts.
For instance this small GUI was made with a combination of FlowLayout, BoxLayout, BorderLayout and GridLayout:
The large JPanel that holds the whole GUI uses BorderLayout, The JTextArea in the center is placed BorderLayout.CENTER, the Provider JLabel and JTextField at the top are in a FlowLayout JPanel that is placed overall BorderLayout.NORTH, the bottom buttons are in a JPanel that uses GridLayout(1, 0, 5, 0) which is held in another JPanel that uses FlowLayout which is placed in the GUI BorderLayout.SOUTH, and the stuff on the right are in a BoxLayout using JPanel.
For example:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
#SuppressWarnings("serial")
public class GetLetterTextGui extends JPanel {
private static final int TA_ROWS = 20;
private static final int TA_COLS = 35;
private static final int PROVIDER_FIELD_COLS = 10;
private static final String GUI_TITLE = "Get Letter Text";
private JList letterList;
private JTextArea textarea = new JTextArea(TA_ROWS, TA_COLS);
private JTextField providerField = new JTextField(PROVIDER_FIELD_COLS);
private JCheckBox addValedictionChkBox = new JCheckBox("Add Valediction", true);
public GetLetterTextGui() {
letterList = new JList(new String[]{"Fe", "Fi", "Fo", "Fum"});
providerField.setText("John Smith, MD");
textarea.setWrapStyleWord(true);
textarea.setLineWrap(true);
JPanel northPanel = new JPanel();
northPanel.add(new JLabel("Provider:"));
northPanel.add(providerField);
JPanel southPanel = new JPanel();
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 5, 0));
btnPanel.add(new JButton("Copy to Clipboard"));
btnPanel.add(new JButton("Clear"));
btnPanel.add(new JButton(new ExitAction()));
southPanel.add(btnPanel);
JPanel eastPanel = new JPanel();
eastPanel.setLayout(new BoxLayout(eastPanel, BoxLayout.PAGE_AXIS));
eastPanel.add(new JScrollPane(letterList));
eastPanel.add(new JPanel() {{add(addValedictionChkBox);}});
int eb = 0;
setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
setLayout(new BorderLayout(eb, eb));
add(northPanel, BorderLayout.PAGE_START);
add(eastPanel, BorderLayout.LINE_END);
add(new JScrollPane(textarea), BorderLayout.CENTER);
add(southPanel, BorderLayout.PAGE_END);
}
private class ExitAction extends AbstractAction {
private final Object MNEMONIC = new Integer(KeyEvent.VK_X);
public ExitAction() {
super("Exit");
putValue(MNEMONIC_KEY, MNEMONIC);
}
#Override
public void actionPerformed(ActionEvent evt) {
Window win = SwingUtilities.getWindowAncestor(GetLetterTextGui.this);
win.dispose();
}
}
private static void createAndShowGui() {
GetLetterTextGui mainPanel = new GetLetterTextGui();
JFrame frame = new JFrame(GUI_TITLE);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
}
All GUI components are JComponent objects. ArrayList<JComponent> can hold references to all of them. Give it a try:
JFrame frame = new JFrame("test");
ArrayList<JComponent> cps = new ArrayList<JComponent>();
cps.add(new JLabel("Hello"));
cps.add(new JPanel());
cps.add(new JButton("OK"));
for (JComponent widget : cps) {
frame.add(widget);
}