I have a JTable in a JPanel (called cur_panel) of a JTabbedPane which is fetching contents from a MySQL database. Now I want to be able to refresh the table whenever I make changes using the options in other pane of JTabbedPane. I got around this problem by destroying cur_panel and then again constructing it so that it could fetch refreshed data from the database.
Can someone suggest me a better way getting around it? Like if I could fetch only the new/modified data and withouut having destroy and then reconstruct the JPanel.
You're right - you shouldn't need to re-construct the JPanel. Indeed, you shouldn't even need to reconstruct the JTable, or possibly even the TableModel. (Also, you might want to do some reading about the Model-View-Controller design pattern)
Instead, you should be resetting the data in the TableModel. I think this should cause the JTable to be repainted. If not, you could call:
table.tableChanged(new TableModelEvent(tableModel));
Alternatively, if you feel it's better to construct a new TableModel with the new data:
table.setModel(newTableModel);
Here's an SSCCE of a JTable that is updated by something else... I've replaced the database connection with input from a JTextField, but it should give you the idea:
public class TableTest {
public static void main(String[] args) {
JFrame frame = new JFrame("Table Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField text = new JTextField("Editable Text");
final TableModel model = new DefaultTableModel(new String[]{"Text"},1);
model.setValueAt(text.getText(), 0, 0);
text.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
model.setValueAt(text.getText(), 0, 0);
}
});
JTable table = new JTable(model);
JPanel panel = new JPanel(new BorderLayout());
panel.add(text, BorderLayout.NORTH);
panel.add(new JScrollPane(table), BorderLayout.SOUTH);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
}
Related
I'm very new to Java and I'm trying to create a small program that reverses text (That part I've figured out).
Where I'm getting stuck on is my GUI, my envisioned plan for the gui is a window with a centered text field for user input then under it in the directly middle of the window a button that reverses the text from the above text box and outputs it in a text box below the button.
Right now I'm using JTextField boxes and after trying to make them look the way I want I'm getting the feeling that there's an easier way to do it, but I don't know it.
Here's my GUI class:
public class ReverseTextGUI extends ReverseRun implements ActionListener {
public static JFrame frame;
private JPanel northFlowLayoutPanel;
private JPanel centerFlowLayoutPanel;
private JPanel southFlowLayoutPanel;
private final JButton reverse = new JButton("Reverse");
private final JTextField userInput = new JTextField(50);
private final JTextField reverseOutput = new JTextField(50);
public void actionPerformed(ActionEvent e) {
reverse.addActionListener((ActionListener) reverse);
reverse.setActionCommand("Reverse");
if ("algorithm".equals(e.getActionCommand())) {
System.out.println("test");
}
}
public void initUI() {
northFlowLayoutPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
northFlowLayoutPanel.add(userInput);
userInput.setPreferredSize(new Dimension(150,100));
centerFlowLayoutPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
centerFlowLayoutPanel.add(reverse);
southFlowLayoutPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
southFlowLayoutPanel.setBorder(BorderFactory.createTitledBorder("Output text"));
southFlowLayoutPanel.add(reverseOutput);
reverseOutput.setPreferredSize(new Dimension(150,100));
JFrame frame = new JFrame("Backwardizer");
frame.setLayout(new BorderLayout()); // This is the default layout
frame.add(northFlowLayoutPanel, BorderLayout.PAGE_START);
frame.add(centerFlowLayoutPanel, BorderLayout.CENTER);
frame.add(southFlowLayoutPanel, BorderLayout.PAGE_END);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setSize(750, 500);
}
Any ideas how to either move the cursor to the start of the box (it shows up in the middle as of now) or a better way to accomplish what I'm trying to do?
For the reversing aspect, you can add the text from the first box to a string builder
StringBuilder rev = new StringBuilder(firstBox.getText());
String reversedText = rev.reverse().toString();
secondBox.setText(reversedText);
Something along those line should get the desired result if you nest it in the button action.
Any ideas how to either move the cursor to the start of the box (it shows up in the middle as of now) or a better way to accomplish what I'm trying to do?
JTextField#setCaretPosition, call this AFTER you've updated the text of the field
Make the field readonly, JTextField#setEditable and pass it false
Additionally, you could use a JList or JTextArea if you want to store multiple rows/lines of text
You should also avoid using setPreferredSize, see Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? for more details
I have been reading about the TableModelListener (http://www.cs.auckland.ac.nz/compsci230s1c/lectures/xinfeng/swingmodelview.pdf) for a while now and I am trying to implement a Listener for a JTable which uses the AbstractTableModel.
To explain the different parts of my program my class. My main class extends JFrame and implements TableModelListener so that is why I have this tableChanged method.
#Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.UPDATE)
System.out.println("It is updated");
if (e.getType() == TableModelEvent.DELETE)
System.out.println("It is deleted");
}
My Class SwitchTableModel which extends AbstractTableModel - Which is not the constructor - creates a table which displays some values which are taken from some other arrays. Part of the implementation is here Display the next row of a List in a JTable .
I use the constructor to call the SwitchTableModel class and create the JTable and the JFrame.
I have also added this row in to get when a value is updated.
public Object getValueAt(int rowIndex, int columnIndex) {
fireTableCellUpdated(rowIndex, columnIndex); ...
The thing that I would like to be able to edit my JTable and then save its data, but although it seems that I could edit it, when I write smth for example in a empty field and press enter, it doesn't keep the data. The same happens when I try to change smth in a non-empty field.
Actually, with this code, it continues printing "It is edited" for the whole time that the JFrame stays open.
Any idea what I might be doing wrong?
***** EDIT *****
My constructor is like this:
final SwitchTableModel model = new SwitchTableModel(user_decide);
JTable table = new JTable(model);
JFrame frame = new JFrame ("Results");
table.getModel().addTableModelListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add (toolbar, BorderLayout.PAGE_START);
frame.getContentPane().add (
new JScrollPane(
table,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),
BorderLayout.CENTER);
//frame.add(checkPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
I'm having some troubles with Java Swing.
I'm trying to make a frame with a control panel at the top with some buttons in it.
and below that i want a JTable to show
I've been trying but the table is not showing.
If I remove the controlPanel at the top, it sometimes shows and sometimes not.
The code that I use inside my constructor of my JTable is provided in the same application,
so it's no network error
public ServerMainFrame(GuiController gc){
this.gc = gc;
initGUI();
}
private void initGUI() {
System.out.println("initiating GUI");
createFrame();
addContentPanel();
addControls();
//openPopUpServerSettings();
addSongTable();
}
private void createFrame()
{
this.setTitle("AudioBuddy 0.1");
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(800, 600);
this.setResizable(false);
this.setLocationRelativeTo(null);
}
private void addContentPanel()
{
JPanel p = new JPanel();
p.setLayout(new FlowLayout());
p.setSize(new Dimension(800, 600));
this.setContentPane(p);
}
private void addControls()
{
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new FlowLayout());
controlPanel.setBorder(BorderFactory.createLineBorder(Color.black));
controlPanel.setSize(700,100);
// Buttons
JButton play = new JButton("Play");
JButton pause = new JButton("Pause");
JButton stop = new JButton ("Stop");
JButton next = new JButton("Next");
JButton prev = new JButton("Previous");
controlPanel.add(play);
controlPanel.add(pause);
controlPanel.add(stop);
controlPanel.add(next);
controlPanel.add(prev);
// Currently playing
JLabel playing = new JLabel("Currently playing:");
controlPanel.add(playing);
JLabel current = new JLabel("Johnny Cash - Mean as Hell");
controlPanel.add(current);
this.getContentPane().add(controlPanel);
}
private void addSongTable()
{
JTable songTable = new JTable(Server.getSongTableModel());
songTable.setVisible(true);
JPanel tablePanel = new JPanel();
tablePanel.setVisible(true);
tablePanel.add(songTable);
songTable.repaint();
this.getContentPane().add(tablePanel);
JButton btnMulticastList = new JButton("send list to clients");
btnMulticastList.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
Server.MulticastPlaylist();
}
});
getContentPane().add(btnMulticastList);
}
if I remove the controlPanel at the top, it sometimes shows and
sometimes not.
everything is hidden in Server.getSongTableModel(), nobody knows without posting an SSCCE with hardcoded value returns from
GUI has issue with Concurency in Swing
XxxModel loading data continiously with building GUi, then exception caused described problems
The code that I use inside my constructor of my JTable is provided in
the same application, so it's no network error
no idea what you talking about
have to create an empty GUI, see InitialTread
showing GUI, then to start loading data to JTable
then starting Workers Thread (Backgroung Task) from SwingWorker or (descr. Network issue) better Runnable#Thread (confortable for catching an exceptions and processing separate threads)
output from Runnable to the Swing GUI must be wrapped into invokeLater()
If you want controls at the top of your window, and the table filling the majority of the window, then I'd suggest you try using BorderLayout instead of FlowLayout. Create it like this...
private void addContentPanel()
{
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
p.setSize(new Dimension(800, 600));
this.setContentPane(p);
}
And add the components by specifying the location in the BorderLayout. In this case, the controls should be added to the top in their minimal size...
this.getContentPane().add(controlPanel,BorderLayout.NORTH);
And the table should be in the center, filling the remaining window space...
this.getContentPane().add(tablePanel,BorderLayout.CENTER);
In your case, you also have a button at the bottom...
getContentPane().add(btnMulticastList,BorderLayout.SOUTH);
For the layout you're after, BorderLayout is much more appropriate. The benefit of using BorderLayout here is that the components should be automatically resized to the size of the window, and you're explicitly stating where each component resides, so panels shouldn't not appear.
It would also be my recommendation that you find an alternative to calling getContentPane() in all your methods. Maybe consider keeping a global variable for the main panel, like this...
private mainPanel;
private void addContentPanel()
{
mainPanel = new JPanel(new BorderLayout());
mainPanel.setSize(new Dimension(800, 600));
this.setContentPane(mainPanel);
}
Then you can reference the panel directly when you want to add() components to it.
Finally, I'd also suggest using GridLayout for your controls, as it will allow you to place all your buttons in it, and they'll be the same size for consistency. Define it like this to allow 5 buttons in a horizontal alignment...
JPanel controlPanel = new JPanel(new GridLayout(5,1));
then you just add the buttons normally using controlPanel.add(button) and they'll be added to the next slot in the grid.
For more information, read about GridLayout or BorderLayout, or just see the Java Tutorial for a Visual Guide to Layout Managers to see what alternatives you have and the best one for your situation. In general, I try to avoid FlowLayout, as I find that there are other LayoutManagers that are more suitable in the majority of instances.
I need some advice on which Swing Components to choose in order to achieve the following:
I have something like a "table" structure, that every time that I click the "Add" button, another "row" should be inserted on the "table". Each row is composed of 2 JTextField. The problem I am having with the GridLayout (the layout used in the pictures below) is that if I add another row, then the heights of the text fields will be shortened and I don't want that (picture on the right), I want to preserve the same height for every row.
What I would like to happen is to have the extra row appear below the last one, so that I could use the JScrollPane and scroll to see it.
Should I use another layout rather than the GridLayout? Maybe the AbsoluteLayout or even using the Table Component?
Thanks.
I would use a JTable and set the row height to whatever you desire. For example:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class TableEg {
private static final int ROW_HEIGHT = 40;
private static final String[] TABLE_COLUMNS = {"Foo", "Bar"};
private static void createAndShowGui() {
final DefaultTableModel tableModel = new DefaultTableModel(TABLE_COLUMNS, 2);
JTable table = new JTable(tableModel );
table.setRowHeight(ROW_HEIGHT);
JScrollPane scrollpane = new JScrollPane(table);
JButton addRowBtn = new JButton(new AbstractAction("Add Row") {
#Override
public void actionPerformed(ActionEvent arg0) {
tableModel.addRow(new String[]{"", ""});
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(addRowBtn);
JFrame frame = new JFrame("TableEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(scrollpane, BorderLayout.CENTER);
frame.getContentPane().add(btnPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Let's say that you add your JTextField in a Jpanel, and you Jpanel in a JScrollPane. Now you would set a preferredSize for you JScrollPane (since you want a fixed displayable area), but you modify the height of the JPanel dinamically based in the amount of JTextField you are placing at that time.
In other words, if you have 2 JTextField in a Jpanel set with GridLayout of 2 columns; you JTextField would take the entire height of the Jpanel; but if you include 2 more they would have to make room for this two and therefore shortening.
Notice that you have 2 rows in a JPanel set to "100" height each row would use "50". if you add another row but also modify the height of the JPanel to "150" each JTextField would still take "50" pixels.
I am a newbie, and it is a possibility that i am not 100% right; if that it is the case "Sorry". I just wanted help.
Try this....
JScrollPane scroll = new JScrollPane(Your_Table);
This will make your table to scroll....
If I add a JTable to a JPanel and then add that JPanel to a JScrollPane, whenever that JTable gains focus, the scroll pane automatically scrolls to the very bottom, which is bad.
I have many reasons for doing it like this, so I'm hoping for some kind solution to stop this auto-scrolling.
OH, and here's the kicker...It only seems to happen when running the app through JNLP/WebStart, and it does NOT do it in Eclipse, which is even more frustrating.
Here's a quick example that if you launch through JLNP, click the text field, click the table, then it auto-scrolls to the bottom:
import java.awt.*;
import javax.swing.*;
public class ScrollDemo extends JPanel{
private static final long serialVersionUID = 1L;
public ScrollDemo()
{
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
JTable table = new JTable(100,6);
this.add(new JTextField());
JPanel panel = new JPanel();
panel.add(table);
JScrollPane scroll = new JScrollPane(panel);
this.add(scroll);
}
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("ScrollDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
JComponent newContentPane = new ScrollDemo();
newContentPane.setOpaque(true); // content panes must be opaque
frame.setContentPane(newContentPane);
frame.setPreferredSize(new Dimension(500, 500));
// Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I am unable to reproduce the problem, but I can offer a few observations:
The frame's add() method forwards the component to the contentPane automatically.
Instead of setting the frame's preferred size, use setPreferredScrollableViewportSize() on the table.
If the unwanted scrolling is due to updating the table's model, see this example of how to temporarily suspend scrolling.
Kudos for using the event dispatch thread.
Addendum: The nested panel having a (default) FlowLayout is superfluous.
import java.awt.*;
import javax.swing.*;
/** #see https://stackoverflow.com/questions/8319388 */
public class ScrollDemo extends JPanel {
public ScrollDemo() {
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
JTable table = new JTable(100, 6);
table.setPreferredScrollableViewportSize(new Dimension(320, 240));
this.add(new JTextField());
this.add(new JScrollPane(table));
}
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame("ScrollDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create and set up the content pane.
frame.add(new ScrollDemo());
frame.pack();
// Display the window.
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
Just in case anyone cares, the problem was that the eclipse launcher and the JNLP launcher for the app had a different KeyboardFocusManager, and the JNLP one had some quirks.
I was unaware of a custom KeyboardFocusManager being added on the JLNP side, but at least it explains things.
Thanks everyone.
I have had auto scrolling issue on JTables when I place them on an intermediary JPanel instead of placing them directly as the JScrollPane's viewport. In those cases, removing the extraneous panel and putting the table directly on the scrollpane has solved my issues.
Well, I ended up having to rework how I was adding my objects to the scroll pane to avoid this issue. Specifically, if the table is going to be taller than the viewport height, then I just have to add the table directly to the scrollpane then change how I was using the intermediary panel.
Thanks to everyone for your input!
According to this, to change the selection for your JTable you need to change the selection model. Try using the following to set the selected item to the very first one.
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.setSelectionInterval(start, end);
See if that gives the focus to the top of the JTable?
EDIT: I have not done this before, so not sure what start and end will need to be, try 0,0 or 0,1 maybe?