I'm trying to create a program that displays a table with data on it, called a Time Log program. I'm having trouble expanding a Jtable to fill a JPanel, since it's currently looking like this:
You can see the huge white gap below the table, but I want the rows to be evenly distributed among the space.
Below is my code.
This is a class I call TimeLogApplication, which has main method and creates a JFrame.
public static void main(String[] args) {
/* Create a new frame */
JFrame guiFrame = new JFrame("Time Log Program");
/* Set size of the frame */
guiFrame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
/* Add the panel */
guiFrame.add(new TimeLogPanel());
/* Exit normally on closing the window */
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
/* Show the frame */
guiFrame.setVisible(true);
}
The JFrame calls TimeLogPanel class that creates a JPanel. This panel has a Border Layout. The table is added to the center panel, which also has Border Layout.
public TimeLogPanel() {
// Set layout, add table panel
setLayout(new BorderLayout(20, 20));
add(tablePanel);
// Create table panel and set layout
tablePanel = new JPanel();
tablePanel.setLayout(new BorderLayout());
// Create a new table with row and column number
table = new JTable(rowNum, colNum);
table.setShowGrid(true);
table.setGridColor(Color.BLACK);
// Set a raised border
table.setBorder(new EtchedBorder(EtchedBorder.RAISED));
// Add table to table panel
tablePanel.add(table);
}
I have tried to get the panel height but since it is not created until the frame is created, the panel height is zero.
Thanks in advance for any suggestions!
UPDATE:
I tried out the suggestions and it worked beautifully (adding ComponentListener to the JPanel that holds the JTable). This is what I did:
tablePanel.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent e) {
tablePanelSize = tablePanel.getHeight();
table.setRowHeight(tablePanel.getHeight()/rowNum); //RowNum is the number of rows
}
// Below methods are not used but must be there, as a requirement when adding a listener
#Override
public void componentMoved(ComponentEvent e) {
}
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public void componentHidden(ComponentEvent e) {
}
});
I think you have to add a ComponentListener (componentResized event), calculate the row height and then call JTable.setRowHeight.
Related
I have created a simple game in Swing which has a screen. Clicking on a cell results in color change of two adjacent cells. This is achieved by this code:
public class SelfGrid extends BattleGrid {
#Override
protected JPanel getCell()
{
JPanel panel = new JPanel();
panel.setBackground(Color.black);
panel.setBorder(BorderFactory.createLineBorder(Color.blue, 1));
panel.setPreferredSize(new Dimension(20, 20));
panel.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e)
{
panel.setSize(new Dimension(20,80));
panel.setBackground(Color.orange);
}
}
});
return panel;
}
}
public abstract class Battle extends JPanel {
public BattleGrid() {
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JPanel grid = new JPanel();
grid.setLayout(new GridLayout(0,10));
JPanel panel = new JPanel();
panel = getCell();
grid.add(panel);
}
}
this.add(grid);
}
protected abstract JPanel getCell();
}
When I use setVisible(boolean) method to toggle between two screens and the original screen is brought back, only the cells that were clicked on remain colored. In other words, the dimension of each JPanel is restored to 20,20. I was told that this is because setVisible() method actually repaints components on the screen. How can I bring back the original screen without any changes being made to it contents? Thank you.
Instead of making programatic color changes directly to the UI elements, create a two dimensional array that represents the colors of the cells, and modify that. Then, repaint the cells based off the values in the array each time visibility changes or a cell is clicked.
I have a simple Java Swing program defined below:
import javax.swing.*;
import java.awt.*;
public class Test implements Runnable {
public static void main(String[] args) {
Test main = new Test();
SwingUtilities.invokeLater(main);
}
#Override
public void run() {
// Application window.
JFrame mainFrame = new JFrame();
// Set up window.
mainFrame.setPreferredSize(new Dimension(600,700));
mainFrame.setFocusable(true);
mainFrame.requestFocus();
JScrollPane scrollPane = new JScrollPane();
scrollPane.setPreferredSize(new Dimension(600,700));
JPanel scrollPanel = new JPanel();
scrollPanel.setLayout(new GridLayout(0,1));
// Add buttons.
for (int i = 0; i < 40; i ++) {
JButton button = new JButton("Button " + i);
button.setPreferredSize(new Dimension(600,100));
scrollPanel.add(button);
}
scrollPane.setViewportView(scrollPanel);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel mainPanel = new JPanel();
mainPanel.add(scrollPane);
// Fill up window.
mainFrame.getContentPane().setLayout(new BorderLayout());
mainFrame.getContentPane().add(mainPanel, BorderLayout.CENTER);
SwingUtilities.updateComponentTreeUI(mainFrame);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.pack();
mainFrame.setVisible(true);
}
}
The program consists of a simple JScrollPane with multiple buttons inside of it. Only vertical scrolling is enabled. It works fine.
However, the problem is, when I am holding down the 'shift' key, vertical scrolling does not work when I am using the mouse wheel to scroll. Vertical scrolling only works when I drag the scrollbar or let go of the 'shift' key.
Usually, in a JScrollPane, when the 'shift' key is held down, and the mouse wheel is used, it scrolls horizontally instead of vertically. However, I have disabled horizontal scrolling.
How would I go about enabling vertical scrolling using the mouse wheel if the user is holding down 'shift'?
I don't think there is a simple way to do this because normally you would only need the shift scroll when scrolling in more than one direction.
You could try adding a key listener to your JFrame and setting the wheel on your mouse to scroll vertically whenever the shift key is pressed. I tried it with this code and it worked just fine:
frame.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
if(arg0.isShiftDown()) {
frame.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent arg0) {
// TODO Auto-generated method stub
pane.getVerticalScrollBar().setValue(pane.getVerticalScrollBar().getValue()+arg0.getWheelRotation());
}
});
}
if(!arg0.isShiftDown()) {
frame.removeMouseWheelListener(frame.getMouseWheelListeners()[0]);
}
}
}
I have figured out a cleaner and simpler answer to my question. Here is the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class CustomScrollPane extends JScrollPane {
public CustomScrollPane(Component component) {
super(component);
initialize();
}
private void initialize() {
// Set up the scroll bar.
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
// Disable scrolling so we can do it manually.
setWheelScrollingEnabled(false);
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
// Manually scroll.
getVerticalScrollBar().setValue(getVerticalScrollBar().getValue() + (e.getWheelRotation()));
}
});
}
}
The solution involves disabling JScrollPane from scrolling. It detects when the mousewheel is being rotated, and manually scrolls. This way, it will scroll when the mousewheel is rotated; it does not matter if 'shift' is being held down or not. The only drawback that I can see is that getVerticalScrollBar().setUnitIncrement() will not change the scrolling speed. The scrolling speed has to be changed manually, perhaps with a constant multiplier.
I have a JPanel holding a JButton and JScrollPane (in turn holding a JTable) and am currently running into two issues which I believe are related:
The JButton listener's actionPerformed() method is not invoked upon click. The only way in which I can get it to be invoked is by calling doClick() on the JButton. The JButton color changes upon hover but no click animation is shown when the mouse is pressed.
Secondly, if a cell is clicked within the JTable, the cell located 2 rows down in the same column registers the click instead. This offset does not occur when clicking in the column headers (i.e. to adjust cell widths), only when within the cell area.
Left-hand panel. Click position circled
public class InventoryPanel extends JPanel {
// Parent Business object reference for communication and JFrame
private Business parent;
private AddItemPanel addItemPanel;
// Inventory table items
private DefaultTableModel inventoryModel;
private JTable inventoryTable;
private JScrollPane inventoryScrollPane;
private JLabel updateLbl;
private JButton addItemBtn;
// Columns for inventory table
private static final String[] INVENTORY_COLUMNS = {"Item","Stock","Restocking Level","Edit"};
public InventoryPanel(Business parent) {
this.parent = parent;
initGUI();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
//doStuff
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(new PrintStream(System.out));
}
}
}
}).start();
}
// INITIALISES GUI
public void initGUI() {
this.setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel titleLabel = new JLabel("<html><B>Inventory</B></html>");
this.add(titleLabel);
// Create empty inventory table
inventoryModel = new DefaultTableModel(new Object[3][4],INVENTORY_COLUMNS);
inventoryTable = new JTable(inventoryModel);
inventoryScrollPane = new JScrollPane(inventoryTable);
// Create button to allow items to be added
addItemBtn = new JButton("Add item");
addItemBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("ADD ITEM PRESSED");
}
});
updateLbl = new JLabel("Loading inventory...");
this.add(addItemBtn);
this.add(inventoryScrollPane);
this.add(updateLbl);
}
I've tried removing the table from the panel to see if that solves the JButton issue and visa-versa, but no luck. I've also tried changing the project JDK but no luck there either.
There are other JPanels adjacent to the troublesome one in a JFrame which work perfectly fine. Any ideas?
Edit: I can create a working instance of the InventoryPanel alone in a frame in another project, as mentioned in the comments. However the exact same code (no calls being made to other objects/methods) in the current project now produces ClassCastExceptions. After some googling this seems to be due to non-EDT threads updating the GUI.
However there is no use of the Business class, and all GUI operations are performed using the SwingUtilities.invokeLater() method like so:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("test");
frame.add(new InventoryPanel());
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
Note: the no-argument constructor InventoryPanel() just calls initGUI().
Thanks for the help so far...still very confused by this.
I am designing a swing standalone application. In my application I have a form in which I am adding some components one by one dynamically when click on add button. My requirement is when I am adding components top to bottom, them jframe and Main jpanel must increase its height when the added component height exceeds.
Add a ContainerListener to your JFrame. In the componentAdded method, pack the JFrame. Something like this:
JFrame frame = getJFrame();
frame.addContainerListener(new ContainerListener() {
#Override
public void componentAdded(ContainerEvent e) {
frame.pack();
}
#Override
public void componentRemoved(ContainerEvent e) {
}
});
I am a new, terribly green user of Swing. I managed to create a table class using examples from java.sun tutorials, and I managed to load data dynamically into it. I want to be able to react to a click on a row by displaying a dialog box. How do I add the event Handler that will identify the selected row number?
The main function code:
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
MainWindow window = new MainWindow();
window.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
createAndShowGUI();
//...
}
}
}
}
and
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Data Table");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up data of the content pane.
TableClass mainTable = new TableClass(fh.getColNames(), fh.getTableContent());
mainTable.setOpaque(true);
frame.setContentPane(mainTable);
//Display the window.
frame.pack();
frame.setVisible(true);
}
Thank you
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 1) {
int selectedRowIndex = table.getSelectedRow();
//show your dialog with the selected row's contents
}
}
});
A couple of points.
The answer from Bozhidar Batsov is correct. However, you may not necessarily have a reference to the table (if for example your mouse listener is in another class or something). Point is, the table can be found from the MouseEvent's getSource() method. You can safely cast it to a JTable.
Also, table.getSelectedRow() may return a -1 if no row has actually been selected on the table yet. That would happen if the user, for example, clicks in the "whitespace" of the table (the area outside of the grid, but still in the text area). Just be sure to test for a -1 in your code.