Is there a way to align all the column in jtable at the same time?
using this:
DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
rightRenderer.setHorizontalAlignment( JLabel.RIGHT );
JTAB_TABLE.getColumnModel().getColumn(0).setCellRenderer( rightRenderer );
will let me align only one column but i need to align all.
Normally, a table contains different kinds of data, (Date, Number, Boolean, String) and it doesn't make sense to force all types of data to be right aligned.
If however you have a table with all the same type of data and you want to force the renderering of all columns to be the same, then you should probably use the same renderer. Assuming you are using the default renderer you can use:
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer)table.getDefaultRenderer(Object.class);
renderer.setHorizontalAlignment( JLabel.RIGHT );
You can do so by overriding prepareRenderer(...) in JTable. This assumes that any custom renderers are JLabels (they're JLabels by default). You'd have to guard against it otherwise.
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableDemo implements Runnable
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new TableDemo());
}
public void run()
{
JTable table = new JTable(5, 5)
{
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int col)
{
Component comp = super.prepareRenderer(renderer, row, col);
((JLabel) comp).setHorizontalAlignment(JLabel.RIGHT);
return comp;
}
};
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
JFrame frame = new JFrame();
frame.getContentPane().add(scrollPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Related
I'm creating GUI in java using swing. I use JTable that looks like this.
I need to make cells of last column("Popis") to be scrollable as there will be description so String containing this description has variable lenght for each row.
This is how I created my JTable.
this.tableModel = new DefaultTableModel(
new Object[][] { },
new String[] {"ID", "Nazov", "Naplnena kapacita / kapacita", "Popis"}
);
this.table1.setModel(this.tableModel);
I add data to table using this.tablemodel.addRow();
Is there a way to make cells of last column of my table scrollable?
It would be difficult and awkward to embed a scrolling component inside a JTable. A common approach to showing long cell values is to make the cell’s tooltip (hover text) display the full value:
table1.setModel(tableModel);
table1.getColumnModel().getColumn(3).setCellRenderer(
new DefaultTableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean selected,
boolean focused,
int row,
int column) {
Component component = super.getTableCellRendererComponent(
table, value, selected, focused, row, column);
if (component instanceof JComponent) {
((JComponent) component).setToolTipText(
Objects.toString(value, null));
}
return component;
}
});
(Objects is the java.util.Objects class.)
It can be solved by creating private class that extends AbstractCellEditor and implements TableCellEditor. Then create and customize the scrollbar. Here is example.
public class MainForm {
private JTable table1;
private JPanel panel1;
private JTextArea text = new JTextArea();
public MainForm() {
text.setEditable(false);
DefaultTableModel tableModel = new DefaultTableModel(new Object[][]{}, new String[]{"Meno", "Priezvisko", "Datum", "Popis"});
table1.setModel(tableModel);
table1.setRowHeight(40);
tableModel.addRow(new Object[]{"Jaimi", "Parker", "2022", "fdjfadufaouifasoifjasifhasiofa \nasdasdasdasdasdasdas \nasdasdasdasdasdasd\nasdasdasdasd "});
tableModel.addRow(new Object[]{"William", "Mcdonald", "2021", "fdjfadufaouasfadfdsfafifasoifjasifhasiofa"});
tableModel.addRow(new Object[]{"Matt", "Ashley", "2020", "asfasfafdsfgdafgdfgasgsdg"});
tableModel.addRow(new Object[]{"Ariana", "Burns", "2019", "fdjfadufaouifasfdsgdgagsgsdgsdgsdagsdgsdgsdgsagsdgoifjasifhasiofa"});
TableColumn column = table1.getColumn("Popis");
column.setCellEditor(new HScrollableTCE());
}
public static void main(String[] args) {
JFrame frame = new JFrame("MainForm");
frame.setContentPane(new MainForm().panel1);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private class HScrollableTCE extends AbstractCellEditor implements TableCellEditor
{
JScrollPane scroll = new JScrollPane(text, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex)
{
if (value != null) {
scroll.setHorizontalScrollBar(scroll.createHorizontalScrollBar());
scroll.getHorizontalScrollBar().setPreferredSize(new Dimension(0, 5));
scroll.setBorder(new EmptyBorder(0,0,0,0));
scroll.setToolTipText(value.toString());
text.setText(value.toString());
return scroll;
}
return null;
}
public Object getCellEditorValue()
{
return text.getText();
}
}
}
I needed a UI that collects similar pieces of data over and over again which can best be described as a batch entry.To input the batch, i needed a scrollpane that adjusts to whatever size of data the user is inputting. To do this smoothly,I relied on the jtable attached to a jscrollpane, and the row to be a jpanel. I am facing some troubles with this solution, that is
I dont know how to access the textfields in my jpanel
I dont know how to add more jpanels as rows on my jtable.
I may add that i jumped to this solution(which may not necessarily be the best) after trying to add many jpanel to one jscrollpane over and over again but this did not work well.This alternative failed as it seems adding a jpanel through the setviewportview was not designed to accept a new panel to expand the view.A variant of the same is one jpanel attached to one scrollpane, then add many jpanels on the mainpanel attached to the scrollpane but the view did not expand and the scrollpane stayed the same.I have checked out oracle's scrollpane tutorialand seen the dynamic changing of a client and how to revalidate the client but am not sure how this will apply in my case where am adding a new jpanels(unless i add them on one jpanel which i keep setting a new clients preferred size as i add a new jpanel.)
NB:
The jpanel i keep adding is not really new as i am iterating it.
Another third solution i had tried is using a flexible gridlayout which its row is a variable 'x' which the user can choose and the a number of 'x' jpanels can be added, problem was,and this is the main problem, the scrollpane was not gracefull to allow expansion so neither did it adjust to a new view.
Note that the scrollpane i need should not be attached to the JFrame but to a an inner view (like a scrollpane for North or Center only in a borderLayout UI) within the JFrame.
I know that the jpanel as a row can be replaced by a normal row where each cell on the row can act either as a jtextfield,jlabel or whatever component i need, but this approach was taken for a particular need to make the JTable look like an actual physical document.
How do I make a good scrollpane that expands dynamically to addition of jpanels ?
The code below shows two attempts of adding jpanels in the hope of expanding the scrollpane but both fail.
Solution one renders each cell as a jpanel but i cannot access the textfield on the jpanel thus i cannot get the data from the user and i cannot also add another row to create a batch.
public class JTablePanelRow extends JFrame implements ActionListener{
JButton addRow = new JButton("Add Row");
JTable table = new JTable();
JScrollPane spane = new JScrollPane();
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel panelRow = new JPanel();
JLabel lblName = new JLabel("NAME");
JTextField txName = new JTextField();
JLabel lblAge = new JLabel("AGE");
JTextField txAge = new JTextField();
TblModel tblmodel = new TblModel();
public static void main(String[] args) {
JTablePanelRow tblPane = new JTablePanelRow();
tblPane.init();
}
void init(){
panelRow.setLayout(new GridLayout(2,2,4,4));
panelRow.add(lblName);
panelRow.add(txName);
panelRow.add(lblAge);
panelRow.add(txAge);
table.setModel(tblmodel);
table.setRowHeight(50);
table.getColumn("A").setCellRenderer(new PanelRenderer(panelRow));
spane.setViewportView(table);
addRow.addActionListener(this);
mainPanel.add(addRow,"North");
mainPanel.add(spane,"Center");
this.getContentPane().add(mainPanel);
this.setVisible(true);
this.setSize(new Dimension(400,500));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==addRow){
tblmodel.addNewRow();
}
}
}
class TblModel extends AbstractTableModel{
#Override
public int getRowCount() {
return 1;
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return "";
}
public void addNewRow(){
//how to add the jpanel again
}
}
class PanelRenderer extends DefaultTableCellRenderer{
JPanel entryPn = new JPanel();
public PanelRenderer(JPanel entryPn){
this.entryPn = entryPn;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
entryPn.setPreferredSize(new Dimension(200,50));
entryPn.setBackground(Color.pink);
return entryPn;}
}
Solution two tries to add jpanels through a flexible gridlayout but the jpanels are not added or not scrollable.
public class GridLayoutTrick extends JFrame{
JPanel mainPanel = new JPanel(new BorderLayout());
JScrollPane scroll = new JScrollPane();
JPanel entryPanel = new JPanel();
JPanel rowPanel = new JPanel();
int batchNumber = 10;
JPanel northPanel = new JPanel();
public static void main(String[] args) {
GridLayoutTrick glt = new GridLayoutTrick();
glt.init();
}
void init(){
rowPanel.setBackground(Color.PINK);
rowPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK,5));
rowPanel.setPreferredSize(new Dimension(100,50));
entryPanel.setLayout(new GridLayout(batchNumber,1,4,4));
for(int i = 0;i < batchNumber;i++){
entryPanel.add(rowPanel);
}
entryPanel.setBackground(Color.BLUE);
scroll.setViewportView(entryPanel);
mainPanel.add(northPanel,"North");
mainPanel.add(scroll,"Center");
this.getContentPane().add(mainPanel);
this.setVisible(true);
this.setSize(new Dimension(400,400));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Solution two tries to add jpanels through a flexible gridlayout but the jpanels are not added or not scrollable.
for(int i = 0;i < batchNumber;i++){
entryPanel.add(rowPanel);
}
You only have 1 "rowPanel".
You need to create a separate instance of the "rowPanel" every time you want to add it the the "entryPanel".
mainPanel.add(northPanel,"North");
mainPanel.add(scroll,"Center");
Don't use magic constants. Use the fields from the API:
BorderLayout.PAGE_START
BorderLayout.CENTER
Although I really think the suggestions by MadProgrammer are a better solution.
I'm left scratching my head why you wouldn't just use the editable capabilities of the JTable alone, you're just making life more difficult for yourself by trying to use a JPanel as the cells renderer/editor
You should start by taking a closer look at How to use tables to get a better understanding of how tables are suppose to be used
The following is an overly simplified example which allows you to add any number of rows and edit the values at each cell in the table...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultTableModel model;
public TestPane() {
setLayout(new BorderLayout());
model = new DefaultTableModel(new Object[]{"Name", "Age"}, 0);
JTable table = new JTable(model);
add(new JScrollPane(table));
JButton btn = new JButton("Add");
add(btn, BorderLayout.SOUTH);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.addRow(new Object[] {"", ""});
table.editCellAt(model.getRowCount() - 1, 0);
table.getEditorComponent().requestFocusInWindow();
}
});
}
}
}
Personally, I'd set up a container object to hold the data and manage it through a custom TableModel, but that's me.
I'd also look at implementing a continuous editing model to allow the user to move easily through the table in a "continuous" editing mode
I have a JTable for which the renderer returns a JPanel composed of multiple JLabel instances. One of those JLabels can contain HTML used among other things to split the output over multiple lines using <br/> tags.
To show the multiple lines in the table, the renderer calls in the getTableCellRendererComponent method
table.setRowHeight(row, componentToReturn.getPreferredSize().height);
to dynamically update the row height, based on the contents. This only works correctly if componentToReturn indicates a correct preferred size.
It looks however that the getPreferredSize returns bogus values. The preferred height of the returned component is smaller than the sum of the heights of the labels inside the component.
Here is a little program illustrating this behaviour (without using a JTable)
import java.awt.*;
import javax.swing.*;
public class SwingLabelTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
LabelPanel renderer = new LabelPanel();
Component component = renderer.getComponent(false);
//asking for a bigger component will not
//update the preferred size of the returned component
component = renderer.getComponent(true);
}
});
}
private static class LabelPanel {
private final JPanel compositePanel;
private final JLabel titleLabel = new JLabel();
private final JLabel propertyLabel = new JLabel();
public LabelPanel() {
JPanel labelPanel = new JPanel();
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.PAGE_AXIS));
labelPanel.add(titleLabel);
labelPanel.add(propertyLabel);
compositePanel = new JPanel(new BorderLayout());
//normally it contains more components,
//but that is not needed to illustrate the problem
compositePanel.add(labelPanel, BorderLayout.CENTER);
}
public Component getComponent( boolean aMultiLineProperty ) {
titleLabel.setText("Title");
if ( aMultiLineProperty ){
propertyLabel.setText("<html>First line<br/>Property: value</html>");
} else {
propertyLabel.setText("Property: value");
}
int titleLabelHeight = titleLabel.getPreferredSize().height;
int propertyLabelHeight = propertyLabel.getPreferredSize().height;
int compositePanelHeight = compositePanel.getPreferredSize().height;
if ( compositePanelHeight < titleLabelHeight + propertyLabelHeight){
throw new RuntimeException("Preferred size of the component returned "
+ "by the renderer is incorrect");
}
return compositePanel;
}
}
}
As I am aware that the previous example is a bit far-fetched, here an example which includes a JTable
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class SwingTableTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
DefaultTableModel tableModel = new DefaultTableModel(0, 1);
JTable table = new JTable(tableModel);
table.setDefaultRenderer(Object.class, new DataResultRenderer());
tableModel.addRow(new Object[]{new Object()});
tableModel.addRow(new Object[]{new Object()});
tableModel.addRow(new Object[]{new Object()});
JFrame testFrame = new JFrame("TestFrame");
testFrame.getContentPane().add(new JScrollPane(table));
testFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
testFrame.setSize(new Dimension(300, testFrame.getPreferredSize().height));
testFrame.setVisible(true);
}
});
}
private static class DataResultRenderer implements TableCellRenderer {
private final JPanel compositePanel;
private final JLabel titleLabel = new JLabel();
private final JLabel propertyLabel = new JLabel();
public DataResultRenderer() {
JPanel labelPanel = new JPanel();
labelPanel.setOpaque(false);
labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.PAGE_AXIS));
labelPanel.add(titleLabel);
labelPanel.add(propertyLabel);
compositePanel = new JPanel(new BorderLayout());
//normally it contains more components,
//but that is not needed to illustrate the problem
compositePanel.add(labelPanel, BorderLayout.CENTER);
}
#Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
titleLabel.setText("Title");
if ( row == 2 ){
propertyLabel.setText("<html>Single property: value</html>");
} else {
String text = "<html>";
text += "First property<br/>";
text += "Second property<br/>";
text += "Third property:value";
text += "</html>";
propertyLabel.setText(text);
}
int titleLabelHeight = titleLabel.getPreferredSize().height;
int propertyLabelHeight = propertyLabel.getPreferredSize().height;
int compositePanelHeight = compositePanel.getPreferredSize().height;
if ( compositePanelHeight < titleLabelHeight + propertyLabelHeight){
throw new RuntimeException("Preferred size of the component returned "
+ "by the renderer is incorrect");
}
table.setRowHeight(row, compositePanel.getPreferredSize().height);
return compositePanel;
}
}
}
I am looking for a way to update the row height of the table to ensure that the multi-line content is completely visible, without knowing up front how many lines each row will contain.
So either I need a solution to retrieve the correct preferred size, or my approach is completely wrong and then I need a better one.
Note that the above examples are simplified. In the real code, the "renderer" (the code responsible for creating the component) is decorated a few times. This means that the outer renderer is the only with access to the JTable, and it has no knowledge about what kind of Component the inner code returns.
Because setRowHeight() "Sets the height, in pixels, of all cells to rowHeight, revalidates, and repaints," the approach is unsound. Absent throwing an exception, profiling shows 100% CPU usage as an endless cascade of repaints tries to change the row height repeatedly. Moreover, row selection becomes unreliable.
Some alternatives include these:
Use TablePopupEditor to display multi-line content on request from a TableCellEditor.
Update an adjacent multi-line panel from a TableModelListener, as shown here.
I got a JTable, which i applied AUTO_RESIZE_LAST_COLUMN to. It auto resizes last column when i drag the columns left or right..
However, the JTable are attached to a JPanel with a BorderLayout manager. When i resize the JFrame, the JPanel resize, and since the JTable fills the JPanel, the JTable resizes too. But when i resize the JFrame, the AUTO_RESIZE_LAST_COLUMN doesnt works, but instead it resizes all the columns.
I want it to only auto_resize the last column, when the JFrame changes size, instead of resize all columns.
code:
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class JTableResize extends JFrame {
private JTable table;
private JPanel panel;
private JScrollPane pane;
DefaultTableModel model = new DefaultTableModel();
public JTableResize() {
super("JTable - Resize Problem");
setLayout(new BorderLayout());
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBackground(Color.red);
add(panel, BorderLayout.CENTER);
table = new JTable(model);
//panel.add(table);
model.addColumn("Resize");
model.addColumn("Problem");
model.addColumn("........");
model.addColumn("This should resize");
pane = new JScrollPane(table);
panel.add(pane);
//this is supposed to resize last column.. It works when you drag in the columns, but not when frame are resized
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
table.getTableHeader().setReorderingAllowed(false);
table.setShowVerticalLines(false);
for (int i = 0; i <= 50; i++) {
model.addRow(new Object[] {i, i, i, i});
}
}
public static void main(String[] args) {
JTableResize jtr = new JTableResize();
jtr.setSize(500, 500);
jtr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jtr.setVisible(true);
}
}
Check out the API documentation from the doLayout() method of JTable.
Before the layout begins the method gets the resizingColumn of the tableHeader. When the method is called as a result of the resizing of an enclosing window, the resizingColumn is null. This means that resizing has taken place "outside" the JTable and the change - or "delta" - should be distributed to all of the columns regardless of this JTable's automatic resize mode.
So this behaviour is not support by default.
Overriding the doLayout() method of the JTable and setting the "resizing column" to the last column seems to do the trick:
#Override
public void doLayout()
{
if (tableHeader != null)
{
TableColumn resizingColumn = tableHeader.getResizingColumn();
// Viewport size changed. Increase last columns width
if (resizingColumn == null)
{
TableColumnModel tcm = getColumnModel();
int lastColumn = tcm.getColumnCount() - 1;
tableHeader.setResizingColumn( tcm.getColumn( lastColumn ) ) ;
}
}
super.doLayout();
}
I am putting a JTable into a JScrollPane
But When I set JTable Auto Resizeable, then it won't have horizontal scroll bar.
if I set AUTO_RESIZE_OFF, then the Jtable won't fill the width of its container when the column width is not big enough.
So how can I do this:
when the table is not wide enough, expand to fill its container width
when the table is wide enough, make it scrollable.
Thanks
You need to customize the behaviour of the Scrollable interface.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableHorizontal extends JFrame
{
public TableHorizontal()
{
final JTable table = new JTable(10, 5)
{
public boolean getScrollableTracksViewportWidth()
{
return getPreferredSize().width < getParent().getWidth();
}
};
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
final JScrollPane scrollPane = new JScrollPane( table );
getContentPane().add( scrollPane );
}
public static void main(String[] args)
{
TableHorizontal frame = new TableHorizontal();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setSize(400, 300);
frame.setVisible(true);
}
}
The above code basically sizes the component at its preferred size or the viewport size, whichever is greater.
If for some reason customising JTable is not an option (e.g. it might be created in third-party code), you can achieve the same result by setting it to toggle between two different JTable AUTO_RESIZE modes whenever the containing viewport is resized, e.g.:
jTable.getParent().addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(final ComponentEvent e) {
if (jTable.getPreferredSize().width < jTable.getParent().getWidth()) {
jTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
} else {
jTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
}
});
I found that all that is needed is to include
table = new JTable(model);
// this enables horizontal scroll bar
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
and then when the required viewport width and height have been calculated, include
frame.getContentPane().add(new JScrollPane(table))
table.setPreferredScrollableViewportSize(new Dimension(width,height));
If you set the Layout of its container to BorderLayout with a BorderLayout.CENTER layout constraint, then the JTable will auto resize to fit its container.
If you want to make a component scrollable, you can wrap the JTable with a JScrollPane.
setLayout(new BorderLayout());
add(new JScrollPane(new JTable()), BorderLayout.CENTER);