How to center JTable cells to the middle of the JTable?
I am using table to paint boxes in the table, but when resized, the cells remain left-top aligned. When the table fits the cells nicely, it is not a problem. But when I resize the window (and the table with it), it is wrong:
My question is, is it possible to make the cells appear in the middle of the table? I suppose I could use glues on sides (this is BorderLayout), but I would rather take this approach.
Since your TableCellRenderer is just painting colored blocks, you could put the table in a FlowLayout and override getPreferredScrollableViewportSize().
private static final int WIDE = 10;
private static final int HIGH = 20;
private static final int SIZE = 50;
...
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(WIDE * SIZE, HIGH * SIZE);
}
Related
I'm trying to create a grid for my snakes and ladders game in java, but I have a small problem where there is an unwanted space in the grids I've created
Does anyone know how I can get rid of that?
This is the code I have for the client side (Client.java):
//Initialize Grid Cells
private Cell[][] cell = new Cell[10][10];
//Create Grid Layout
GridLayout GameBoard = new GridLayout(10, 10, 1, 1); //Create GridLayout
GameArea.setLayout(GameBoard); //Add GridLayout
GameArea.setPreferredSize(new Dimension(590,560));
GameArea.setOpaque(false);
//Add Cells to Grid
for (int v = 0; v < 10; v++)
for (int h = 0; h < 10; h++)
GameArea.add(cell[v][h] = new Cell(v, h, this));
//Individual Image on Each Cell
cell[1][0].add(new JLabel(GreenGrid));
This is the code I have for the cells(Cells.java), it also extends JPanel:
//Indicate the row and column of this cell in the board
private int GridRow;
private int GridColumn;
private Client parent;
public Cell(int GridRow, int GridColumn, Client GUI) {
this.GridRow = GridRow;
this.GridColumn = GridColumn;
this.parent = GUI;
setBorder(new LineBorder(Color.orange, 1)); // Set cell's border
setBackground(Color.gray);
}
I can't see your image, but I suspect that you have a layout issue. Does your Cell extend JPanel by the way? Do you set its layout manager or do you use the default FlowLayout?
Consider:
You should not call setPreferredSize(...) on the GameBoard as that will then dictate your grid cell size, which is too big.
Rather, the Cells themselves should be dictating the size of themselves and the entire grid.
Consider having Cell override getPreferredSize() and returning the dimension of the JLabel's image (if one is present) or else returning the super's result.
Be sure to call pack() on your top-level window after adding all components and before setting it visible.
Consider having Cell use a BorderLayout and adding your JLabel to the BorderLayout.CENTER position so that it fills the cell (if that's what you want it to do).
For more help and better help, consider creating and posting an sscce.
Your first problem is here...
GridLayout GameBoard = new GridLayout(10, 10, 1, 1); //Create GridLayout
As described in the JavaDocs...
public GridLayout(int rows,
int cols,
int hgap,
int vgap)
Creates a grid layout with the specified number of rows and columns.
All components in the layout are given equal size.
In addition, the horizontal and vertical gaps are set to the specified
values. Horizontal gaps are placed between each of the columns.
Vertical gaps are placed between each of the rows.
One, but not both, of rows and cols can be zero, which means that any
number of objects can be placed in a row or in a column.
What this means is, you are supplying the gap by supplying non-zero values to the hgap and vgap parameters.
If you used something like...
GridLayout GameBoard = new GridLayout(10, 10); //Create GridLayout
You would end up with something like...
As has already been mentioned, I would avoid using GameArea.setPreferredSize(new Dimension(590,560)); and instead, override the getPreferredSize method the Cell class. Because of the way that GridLayout works, this won't stop the cells from been resized, but this might be desirable anyway...
I have JLabel which I would like to change its size while I resize the window. When JLabel contains String which is too big, the String should be shortened, with right part visible and adds dots on the left hand side of the String.
My JLabel is inside innerPanel which is a header in middlePanel which is added to outerPanel. So when I resize window I use listener on outerPanel in that way:
outerPanel.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent evt) {
int width = ((JPanel) evt.getSource()).getWidth();
windowSize = width;
refresh();
}
// [...] other not used override methods
});
refresh() repaints view and creates new middlePanel where is called class which creates innerPanel where is located my JLabel:
Public class InnerPanel extends JPanel {
private int maxSize;
String string = "<VERY_LONG_STRING>";
private static final int DEFAULT_INDEND_PIXEL = 70;
public InnerPanel(int windowSize) {
maxSize = windowSize - DEFAULT_INDENT_PIXEL;
createPanel();
}
private createPanel() {
// [...] gridbag and GridBagConstraints implementation
String shortString = countString();
JLabel label = new JLabel(shortString);
add(label,gc);
}
private String countString() {
int index = 0;
boolean toBig = true;
StringBuilder sb = new StringBuilder(string);
while(toBig) {
Rectangle2d rect = // [...] code which creates rectangle around text from sb.toString()
// I have no access to repo at home but if it's important I can paste it tomorrow
if(rect.getWidth() > maxSize)
sb.deleteCharAt(0);
else
toBig = false;
}
return sb.toString();
}
}
That's works fine in general, bacause it do resize JLabel in one step when I enlarge window in width. But the problem is appear when I try to reduce the window in width. In this case componentResized() calculate width step by step (and it's called multiple times), gradually decreases width by some amount of pixels till it reach real window size. It's behave in that way even thow I change window size in one step from maximum size to 800. Whole process is so slow, that it takes around a second to fit string to window size. So it looks bit like an animation.
The problem is very rare to me, bacause width in componentResized() method is calculeted step by step only when I assign windowSize variable.
When I give windowSize fixed size like for example 500 - componentResized() is called only onces - with correct width indicated real window size (!!) - and there's no its step by step decrease!
It's look like width variable which is assigned by ((JPanel) evt.getSource()).getWidth() knows that windowSize is used to dynamically change size of JLabel component even before first call of refresh() method.
If anyone have an idea what is going on here - I will be very appreciate for help.
You may be able to adapt one of the approaches shown here to better effect. As shown here, the ellipsis is supplied by the label's UI delegate via a call to SwingUtilities2.clipString(), which appends the clipString. Rather than re-invent the label UI, use TextLayout to determine the required geometry, prepend the ellipsis, and handle the alignment in a table or list renderer, as shown here.
I'm developing a webapplication in the Vaadin framework.
I have a table with 14 columns. The last column holds three icons and a problem I'm having is that like half of the time the table is rendered the icon furthest to the right will be "cut in half" vertically. To avoid this problem I tried to set a fixed width to this column that I think would eradicate the problem, however, nothing happens..
I'm using the conventional approach:
simCardTable.setColumnWidth(actionColumn, 135);
However, no matter what value I set to be the column width the column stays the same... Does anyone know why this is? Is it because it's the last column to be added and therefore there's no space to spare..?
Btw, that is the only column I set a specific width to, all of the restoring columns have a the standard width specified by the width of the column header or the cell content.
Any help would be very appreciated!
As i understand from your question ("The last column holds three icons") you use ColumnGenerator to create this last column, with icons. If I right, it mean that you created some sort of custom layout with this icons inside, in this case for you should work this:
final ColumnGenerator generator = new ColumnGenerator() {
private static final long serialVersionUID = 1L;
#Override
public Component generateCell(Table source, final Object itemId, Object columnId) {
final HorizontalLayout layout = new HorizontalLayout();
layout.setSizeFull();
Embedded icon1 = new Embedded();
Embedded icon2 = new Embedded();
Embedded icon3 = new Embedded();
//Add some themeresource to embedded components
//Do some listners to this icons
layout.addComponent(icon1);
layout.addComponent(icon2);
layout.addComponent(icon3);
//Set column with
setColumnWidth(columnId, 100);
return layout;
}
};
What's the best way to set up a table in a JScrollPane such that the first column is always on the screen in the same position regardless of horizontal scrolling and overlaps columns that pass underneath?
When the scrollbar is at the farthest left, the columns look normal, but as the user scrolls to the right, the secondary columns (2 and on) move underneath the first until the last column appears on the far right of the viewport?
I found a sample taken from Eckstein's "Java Swing" book that sort of does this, but it doesn't allow resizing of the first column. I was thinking of some scheme where one JPanel held a horizontal struct and a table holding the secondary columns and another JPanel which floated over them (fixed regardless of scrolling). The struct would be to keep the viewport range constant as the first column floated around. Ideally I could do it with two tables using the same model, but I'm not sure if the whole idea is a naive solution.
I'd ideally like a setup where multiple tables are on the same scrollpane vertically, where all their first columns are aligned and move together and there are just little horizontal gaps between the individual tables.
Fixed Column Table does most of what you need.
It does not support resizing the fixed column so you would need to add code like:
MouseAdapter ma = new MouseAdapter()
{
TableColumn column;
int columnWidth;
int pressedX;
public void mousePressed(MouseEvent e)
{
JTableHeader header = (JTableHeader)e.getComponent();
TableColumnModel tcm = header.getColumnModel();
int columnIndex = tcm.getColumnIndexAtX( e.getX() );
Cursor cursor = header.getCursor();
if (columnIndex == tcm.getColumnCount() - 1
&& cursor == Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR))
{
column = tcm.getColumn( columnIndex );
columnWidth = column.getWidth();
pressedX = e.getX();
header.addMouseMotionListener( this );
}
}
public void mouseReleased(MouseEvent e)
{
JTableHeader header = (JTableHeader)e.getComponent();
header.removeMouseMotionListener( this );
}
public void mouseDragged(MouseEvent e)
{
int width = columnWidth - pressedX + e.getX();
column.setPreferredWidth( width );
JTableHeader header = (JTableHeader)e.getComponent();
JTable table = header.getTable();
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = (JScrollPane)table.getParent().getParent();
scrollPane.revalidate();
}
};
JTable fixed = fixedColumnTable.getFixedTable();
fixed.getTableHeader().addMouseListener( ma );
JScrollPane has an area specifically for this, the row header (see the diagram in the API:)
All you need to do is:
- create an extra JTable for this fixed area
- hook it up to the first column of your data model
- set it as the row header
- and in the main table omit or remove the first column of data.
When the scrollpane scrolls up and down both tables will scroll in sync with no added code. When the scrollpane scrolls horizontally the row header is always kept visible and only the main table scrolls.
For most cases the only added code you'll need is for the column resizing, like camickr's example.
Check out this class, extracted from http://fahdshariff.blogspot.sg/2010/02/freezing-columns-in-jtable.html
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableModel;
public class FrozenTablePane extends JScrollPane{
public FrozenTablePane(JTable table, int colsToFreeze){
super(table);
TableModel model = table.getModel();
//create a frozen model
TableModel frozenModel = new DefaultTableModel(
model.getRowCount(),
colsToFreeze);
//populate the frozen model
for (int i = 0; i < model.getRowCount(); i++) {
for (int j = 0; j < colsToFreeze; j++) {
String value = (String) model.getValueAt(i, j);
frozenModel.setValueAt(value, i, j);
}
}
//create frozen table
JTable frozenTable = new JTable(frozenModel);
//remove the frozen columns from the original table
for (int j = 0; j < colsToFreeze; j++) {
table.removeColumn(table.getColumnModel().getColumn(0));
} table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
//format the frozen table
JTableHeader header = table.getTableHeader();
frozenTable.setBackground(header.getBackground());
frozenTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
frozenTable.setEnabled(false);
//set frozen table as row header view
JViewport viewport = new JViewport();
viewport.setView(frozenTable);
viewport.setPreferredSize(frozenTable.getPreferredSize());
setRowHeaderView(viewport);
setCorner(JScrollPane.UPPER_LEFT_CORNER,
frozenTable.getTableHeader());
}
}
Thereafter, just call the constructor method:
JTable table = new JTable( <yourData>, <yourColumns> );
FrozenTablePane frozenPane = new FrozenTablePane(table,1);//where 1 is the number of freezed column(s)
I think you are on the right track. What you conceptually have is a table with a 'header column' for each row. I would use two tables - one has the 'leftmost' column in and the other has all the others. Then I would present these in a JSplitPane with the 'leftmost column' table on the left and the rest on the right. There would be a single vertical scrollbar that controlled the y offset of both tables, and a single horizontal scrollbar controlling the right hand pane (only).
You can also use the advanced features of JScrollPane to set a 'header' component on the left of the main scroll area. I've never done it, but you might be able to use that as the 'headers' of your rows.
In a JTable, how can I make some rows automatically increase height to show the complete multiline text inside? This is how it is displayed at the moment:
I do not want to set the height for all rows, but only for the ones which have multiline text.
The only way to know the row height for sure is to render each cell to determine the rendered height. After your table is populated with data you can do:
private void updateRowHeights()
{
for (int row = 0; row < table.getRowCount(); row++)
{
int rowHeight = table.getRowHeight();
for (int column = 0; column < table.getColumnCount(); column++)
{
Component comp = table.prepareRenderer(table.getCellRenderer(row, column), row, column);
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}
table.setRowHeight(row, rowHeight);
}
}
If only the first column can contain multiple line you can optimize the above code for that column only.
Camickr's solution did not work for me at all. My data model was dynamic though - it changed all the time. I guess the mentioned solution works for static data, like coming from an array.
I had JPanel for cell renderer component and it's preferred size wasn't set correctly after using prepareRenderer(...). The size was set correctly after the containing window was already visible and did repaint (2 times in fact after some unspecified, though short time). How could I call updateRowHeights() method shown above then and where would I do this? If I called it in (overriden) Table.paint() it obviously caused infinite repaints. It took me 2 days. Literally. The solution that works for me is this one (this is the cell renderer I used for my column):
public class GlasscubesMessagesTableCellRenderer extends MyJPanelComponent implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
//this method updates GUI components of my JPanel based on the model data
updateData(value);
//this sets the component's width to the column width (therwise my JPanel would not properly fill the width - I am not sure if you want this)
setSize(table.getColumnModel().getColumn(column).getWidth(), (int) getPreferredSize().getHeight());
//I used to have revalidate() call here, but it has proven redundant
int height = getHeight();
// the only thing that prevents infinite cell repaints is this
// condition
if (table.getRowHeight(row) != height){
table.setRowHeight(row, height);
}
return this;
}
}
You must iterate over each row, get the bounding box for each element and adjust the height accordingly. There is no code support for this in the standard JTable (see this article for a solution for Java ... 1.3.1 =8*O).