Last column does not appear on screen in a JTable - java

I create a JTable at runtime. The number of columns will vary from a few to hundreds.
I have a database routine which populates a vector of vectors.
The topmost row is used as the header.
The data in the columns vary in size, and the column sizes are computed and set to the maximum width for that column. That is to say, each column will vary in size.
The table header in the table and the scroll pane are hidden via:
Common.myTBL.setTableHeader(null);
Common.myScrollPanel.setColumnHeaderView(null);
Common.myScrollPanel.setViewportView(Common.myTBL);
pack();
When the number of columns in the table is small - say 5 or 6 - I don't have any problems.
Common.myTBL.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
But there is a particular case where the number of columns is 174 (0 to 173). The last column does not appear on the screen. I tried adding an extra dummy column (175th) and part of column 174 appeared.
This is the code that creates the Table Model and Table Object
public void createTableModel() {
int noOfColumns = 0;
int noOfRows = 0;
noOfRows = Common.tableData.size();
noOfColumns = Common.tableData.get(0).size();
System.out.println("\nNumber of Rows in the VECTOR = "+noOfRows);
System.out.println("\nNumber of columns in the VECTOR = "+noOfColumns);
Common.myTableModel = new AttributiveCellTableModel(noOfRows, noOfColumns);
System.out.println("\nNumber of Rows in the MODEL= "+Common.myTableModel.getRowCount());
System.out.println("\nNumber of columns in the MODEL = "+Common.myTableModel.getColumnCount());
Common.theRoots = Common.tableData.get(0);
Common.myTableModel.setDataVector(Common.tableData, Common.theRoots);
Common.cellAtt = (CellSpan)Common.myTableModel.getCellAttribute();
}
public void createTableObject() {
Common.myTBL = new MultiSpanCellTable(Common.myTableModel) {
public Component prepareRenderer(TableCellRenderer renderer, int Index_row, int Index_col) {
Component comp = super.prepareRenderer(renderer, Index_row, Index_col);
JComponent jcomp = (JComponent) comp;
Common.originalBorder = jcomp.getBorder();
comp.setBackground(setRowColor(Index_row));
jcomp.setToolTipText(getToolTip(Index_row, Index_col));
comp.setFont(Common.defaultFont14);
comp.setForeground(setGenderColor(Index_row, Index_col));
jcomp.setBorder(setBorderType(Index_row, Index_col));
return comp;
}
};
Common.myTBL.setRowHeight(25);
Common.myTBL.setBorder(null);
Common.myTBL.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
Common.myTBL.setCellSelectionEnabled(true);
Common.myTBL.setGridColor(new java.awt.Color(0, 51, 255));
Common.myTBL.setFillsViewportHeight(true);
Common.myTBL.revalidate();
}

Related

How to individually set the width of a row on a table/scrollpane

i have a DefaultTableModel:
DefaultTableModel model = new DefaultTableModel(data, beschriftung)
{
// Returning the Class of each column will allow different
// renderers to be used based on Class
public Class getColumnClass(int column)
{
return getValueAt(0, column).getClass();
}
};
This is inside a table:
JTable table = new JTable(model);
table.setEnabled(false);
table.setRowHeight(50);
And the table is inside a scrollPane:
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBounds(1215, 11, 300, 300);
scrollPane.setWheelScrollingEnabled(true);
scrollPane.setEnabled(false);
getContentPane().add(scrollPane);
I have two rows, but want the first one to be shorter than the second one. Any suggestions?
This is how it looks like:
This is how I want it to look like:
Check out:
Check of the section from the Swing tutorial on Setting and Changing Columns Widths. It shows how you can manually set a preferred size for each column.
The basic code would be:
table.getColumnModel().getColumn(???).setPreferredWidth(???)
You can also check out the Table Column Adjuster for automatic sizing of columns based on the data contained in the model
The basic code for this approach is:
JTable table = new JTable( ... );
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
for (int column = 0; column < table.getColumnCount(); column++)
{
TableColumn tableColumn = table.getColumnModel().getColumn(column);
int preferredWidth = tableColumn.getMinWidth();
int maxWidth = tableColumn.getMaxWidth();
for (int row = 0; row < table.getRowCount(); row++)
{
TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
Component c = table.prepareRenderer(cellRenderer, row, column);
int width = c.getPreferredSize().width + table.getIntercellSpacing().width;
preferredWidth = Math.max(preferredWidth, width);
// We've exceeded the maximum width, no need to check other rows
if (preferredWidth >= maxWidth)
{
preferredWidth = maxWidth;
break;
}
}
tableColumn.setPreferredWidth( preferredWidth );
}
The TableColumnAdjuster class in the link contains other features as well.
Also check out the JTable API for the setAutoResizeMode(...) method to determine how space is allocated if the width of the table changes.

How do I maintain cell widths when merging cells in a word table using apache poi?

I'm creating a word document with a table using Java and Apache POI.
I can create the table easily enough, set each column with different widths and then merge the cells to produce the desired effect (see images below) however when I open the word document some of the cells have been adjusted so that their edges snap together. I have found that adding an additional row to the beginning of the table and leaving all cells unmerged keeps the rest of the rows intact, but removing this row later using table.removeRow(0); affects the rest of the rows. If I open the word document and manually delete the row, the cells stay where they are. Is there anything I can do to preserve the layout of the cells?
correct layout with an additional unmerged top row
the result after removing the top row
This is the function that creates the word doc and table:
public static void createWord() {
// Blank Document
XWPFDocument document = new XWPFDocument();
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
CTPageMar pageMar = sectPr.addNewPgMar();
pageMar.setLeft(BigInteger.valueOf(300L));
pageMar.setTop(BigInteger.valueOf(300L));
pageMar.setRight(BigInteger.valueOf(300L));
pageMar.setBottom(BigInteger.valueOf(300L));
XWPFParagraph paragraph = document.createParagraph();
paragraph.setSpacingBefore(0);
paragraph.setSpacingAfter(0);
// determine the number of rows and columns required
int rows = 3;
int cols = 6;
// create table
XWPFTable table = document.createTable(rows+1, cols);
CTTblPr tblPr = table.getCTTbl().getTblPr();
if (null == tblPr) {
tblPr = table.getCTTbl().addNewTblPr();
}
// set table width
CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
width.setType(STTblWidth.PCT);
width.setW(BigInteger.valueOf(5000)); // 5000 * 1/50 = 100%
//set row height
for(XWPFTableRow row:table.getRows()) {
row.setHeight(22);
}
// set width of each column
for (int row = 0; row <= rows; row++) {
setCellWidthPercentage(table, row, 0, 0.188);
setCellWidthPercentage(table, row, 1, 0.125);
setCellWidthPercentage(table, row, 2, 0.063);
setCellWidthPercentage(table, row, 3, 0.25);
setCellWidthPercentage(table, row, 4, 0.25);
setCellWidthPercentage(table, row, 5, 0.125);
}
mergeCellHorizontally(table, 1, 0, 2);
mergeCellHorizontally(table, 2, 0, 1);
mergeCellHorizontally(table, 2, 2, 4);
mergeCellHorizontally(table, 3, 1, 3);
// remove first row (comment out this line to see issue)
table.removeRow(0);
// Write the Document in file system
try {
File docFile = new File("C:\\doc.docx");
docFile.createNewFile();
FileOutputStream out = new FileOutputStream(docFile, false);
document.write(out);
out.close();
document.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
I'm using the code below to merge cells horizontally:
static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
for(int colIndex = fromCol; colIndex <= toCol; colIndex++){
XWPFTableCell cell = table.getRow(row).getCell(colIndex);
CTHMerge hmerge = CTHMerge.Factory.newInstance();
if(colIndex == fromCol) {
// The first merged cell is set with RESTART merge value
hmerge.setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
hmerge.setVal(STMerge.CONTINUE);
}
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr != null) {
tcPr.setHMerge(hmerge);
} else {
// only set an new TcPr if there is not one already
tcPr = CTTcPr.Factory.newInstance();
tcPr.setHMerge(hmerge);
cell.getCTTc().setTcPr(tcPr);
}
}
}
and this function to assign width values to the columns before merging:
private static void setCellWidthPercentage(XWPFTable table, int row, int col, double width) {
// prevent out of bounds exception
if (row < 0 || row >= table.getRows().size()) return;
if (col < 0 || col >= table.getRow(row).getTableCells().size()) return;
// assign widths in units of 1/50 of a percentage
CTTblWidth tblW = table.getRow(row).getCell(col).getCTTc().addNewTcPr().addNewTcW();
tblW.setType(STTblWidth.PCT);
tblW.setW(BigInteger.valueOf(Math.round(width * 50)));
}
Thanks in advance!
The problem you see is that Word renders tables in respect of the column width settings of the row which has the most columns in it. If other rows contradict the column width settings of that row, then their column width setting will be ignored. And after merging cells you are not correcting the column width settings. For example after mergeCellHorizontally(table, 0, 0, 2); column 0 in row 0 is up to column 2 now. So column 0 now need width of formerly columns 0 + 1 + 2. But since you are not correcting that, it stays width of formerly column 0 only and gets ignored while rendering if that contradicts the width settings of the row having the most columns.
So the main problem is that your code lacks correcting the column width settings in the rows after merging cells.
I have shown this already in how to set specific cell width in different row in apache poi table?.
But there are more issues.
First the method mergeCellHorizontally should merge cells horizontally by setting grid span instead of using CTHMerge. This is much more compatible to all kinds of word processing applications which open *.docx files than using CTHMerge.
Second there always should be used the last apache poi version. Current apache poi 4.1.2 provides XWPFTable.setWidth and XWPFTableCell.setWidth. So no own set-width-methods are necessary.
And third you should create a table grid for the table with widths of the columns. This is necessary for Libreoffice/OpenOffice to accept the column widths. Unfortunately this needs calculating the column widths in unit twentieths of a point (1/1440 of an inch) since TblGrid - GridCol does not accepts percent values.
The following complete example shows all this and creates the table you want.
import java.io.FileOutputStream;
import java.math.BigInteger;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
public class CreateWordTableMergedCells {
//merging horizontally by setting grid span instead of using CTHMerge
static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
XWPFTableCell cell = table.getRow(row).getCell(fromCol);
// Try getting the TcPr. Not simply setting an new one every time.
CTTcPr tcPr = cell.getCTTc().getTcPr();
if (tcPr == null) tcPr = cell.getCTTc().addNewTcPr();
// The first merged cell has grid span property set
if (tcPr.isSetGridSpan()) {
tcPr.getGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
} else {
tcPr.addNewGridSpan().setVal(BigInteger.valueOf(toCol-fromCol+1));
}
// Cells which join (merge) the first one, must be removed
for(int colIndex = toCol; colIndex > fromCol; colIndex--) {
table.getRow(row).getCtRow().removeTc(colIndex);
table.getRow(row).removeCell(colIndex);
}
}
public static void main(String[] args) throws Exception {
XWPFDocument document= new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("The table:");
// determine the number of rows and columns required
int rows = 3;
int cols = 6;
//create table
XWPFTable table = document.createTable(rows, cols);
//set table width
table.setWidth("100%");
double[] columnWidths = new double[] { // columnWidths in percent
0.188, 0.125, 0.062, 0.25, 0.25, 0.125
};
//create CTTblGrid for this table with widths of the columns.
//necessary for Libreoffice/Openoffice to accept the column widths.
//values are in unit twentieths of a point (1/1440 of an inch)
int w100Percent = 6*1440; // twentieths of a point (1/1440 of an inch); 6 inches
//first column
table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(
Math.round(w100Percent*columnWidths[0])));
//other columns
for (int c = 1; c < cols; c++) {
table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(
Math.round(w100Percent*columnWidths[c])));
}
// set width of each column in each row
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
table.getRow(r).getCell(c).setWidth("" + (columnWidths[c]*100.0) + "%");
}
}
//using the merge method
mergeCellHorizontally(table, 0, 0, 2); // after that column 0 is up to column 2
//column 0 now need width of formerly columns 0 + 1 + 2
table.getRow(0).getCell(0).setWidth("" + ((columnWidths[0]+columnWidths[1]+columnWidths[2])*100.0) + "%");
mergeCellHorizontally(table, 1, 0, 1); // after that column 0 is up to column 1
//column 0 now need width of formerly columns 0 + 1
table.getRow(1).getCell(0).setWidth("" + ((columnWidths[0]+columnWidths[1])*100.0) + "%");
mergeCellHorizontally(table, 1, 1, 3); // formerly col 2 is now col 1 and after that formerly column 2 is up to column 4
//current column 1 now need width of formerly columns 2 + 3 + 4
table.getRow(1).getCell(1).setWidth("" + ((columnWidths[2]+columnWidths[3]+columnWidths[4])*100.0) + "%");
mergeCellHorizontally(table, 2, 1, 3); // after that column 1 is up to column 3
//column 1 now need width of formerly columns 1 + 2 + 3
table.getRow(2).getCell(1).setWidth("" + ((columnWidths[1]+columnWidths[2]+columnWidths[3])*100.0) + "%");
paragraph = document.createParagraph();
FileOutputStream out = new FileOutputStream("create_table.docx");
document.write(out);
out.close();
}
}

Show data from JTable in Jtextfield with 2 JFrames

I am making a program for school.
My program has two JFrame's
The first Jframe = Basisscherm
The second Jframe = Toetsenbord
On the Jframe basisscherm i've got a Jtable filled with data from MYSQL Database. This Table showing labels and with this labels are specific text so each label has his own text this is in the same data base
Now on the Jframe toetsenbord i've got a Jtextfield with the name: Tekst
Now my problem is i want to show the text in the jtextfield by selecting the label from the jtable and clicking on a ok button but i don't now where to start
Have a look at this. using which you can get the selected text in JTable.
JTable table = new JTable();
if (table.getColumnSelectionAllowed()
&& !table.getRowSelectionAllowed()) {
// Column selection is enabled
// Get the indices of the selected columns
int[] vColIndices = table.getSelectedColumns();
} else if (!table.getColumnSelectionAllowed()
&& table.getRowSelectionAllowed()) {
// Row selection is enabled
// Get the indices of the selected rows
int[] rowIndices = table.getSelectedRows();
} else if (table.getCellSelectionEnabled()) {
// Individual cell selection is enabled
// In SINGLE_SELECTION mode, the selected cell can be retrieved using
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
int rowIndex = table.getSelectedRow();
int colIndex = table.getSelectedColumn();
// In the other modes, the set of selected cells can be retrieved using
table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// Get the min and max ranges of selected cells
int rowIndexStart = table.getSelectedRow();
int rowIndexEnd = table.getSelectionModel().getMaxSelectionIndex();
int colIndexStart = table.getSelectedColumn();
int colIndexEnd = table.getColumnModel().getSelectionModel()
.getMaxSelectionIndex();
// Check each cell in the range
for (int r=rowIndexStart; r<=rowIndexEnd; r++) {
for (int c=colIndexStart; c<=colIndexEnd; c++) {
if (table.isCellSelected(r, c)) {
// cell is selected
}
}
}
}

Auto/best fit JTable columns, but stretch the last column

I have the following JTable (Actually it's a ETable from Netbeans). It stretches across the container it's in - I'd like to keep that, and not use JTable.AUTO_RESIZE_OFF
I'd like to fit it programatically like below, resizing each column to fit the only the cell content, or column header text and having the rightmost column fill the remaining space. How can I do that ?
You do have to set autoResize to OFF (setAutoResizeMode(JTable.AUTO_RESIZE_OFF);), but you also need a helper method to resize your columns.
This is inside a custom class that extends JTable, but you can just as easily reference an existing JTable:
public void resizeColumnWidth() {
int cumulativeActual = 0;
int padding = 15;
for (int columnIndex = 0; columnIndex < getColumnCount(); columnIndex++) {
int width = 50; // Min width
TableColumn column = columnModel.getColumn(columnIndex);
for (int row = 0; row < getRowCount(); row++) {
TableCellRenderer renderer = getCellRenderer(row, columnIndex);
Component comp = prepareRenderer(renderer, row, columnIndex);
width = Math.max(comp.getPreferredSize().width + padding, width);
}
if (columnIndex < getColumnCount() - 1) {
column.setPreferredWidth(width);
cumulativeActual += column.getWidth();
} else { //LAST COLUMN
//Use the parent's (viewPort) width and subtract the previous columbs actual widths.
column.setPreferredWidth((int) getParent().getSize().getWidth() - cumulativeActual);
}
}
}
Call resizeColumnWidth() whenever you add a row.
Optionally add a listener to the table so that the columns are also resized when the table itself is resized:
public MyCustomJTable() {
super();
addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
#Override
public void ancestorResized(HierarchyEvent e) {
super.ancestorResized(e);
resizeColumnWidth();
}
});
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
You can turn off auto resize. In this case the columns will resize automatically according to content. But in this case, if total column width is less than table's width, blank space will be there on right side. If it is greater, horizontal scroll bar will appear.
You can assign preferred width to each column using TableColumn.setPreferredWidth. Swing will try to distribute extra space according to that. But this is also not guaranteed.

How to resize JTable column to string length?

I have a JTable that will have the last column data field change to different string values. I want to resize the column to the string length. What is the formula for string length to width?
I'm going to be using JTable.getColumnModel().getColumn().setPreferredWidth() so I want to know how to translate string length to width value.
you are not really interested in the string length (nor its mapping to a particular font/metrics). You're interested in the preferredSize of the renderingComponent which renderers the cell content. To get that, loop through all rows and query the size, something like
int width = 0;
for (row = 0; row < table.getRowCount(); row++) {
TableCellRenderer renderer = table.getCellRenderer(row, myColumn);
Component comp = table.prepareRenderer(renderer, row, myColumn);
width = Math.max (comp.getPreferredSize().width, width);
}
Or use JXTable (in the SwingX project): it has a method pack() which does the work for you :-)
Edit: the reason to prefer the table's prepareRenderer over manually calling getXXRendererComponent on the renderer is that the table might do decorate visual properties of the renderingComponent. If those decorations effect the prefSize of the component, a manual config is off.
This method will pack a given column in a JTable -
/**
* Sets the preferred width of the visible column specified by vColIndex. The column
* will be just wide enough to show the column head and the widest cell in the column.
* margin pixels are added to the left and right
* (resulting in an additional width of 2*margin pixels).
*/
public static void packColumn(JTable table, int vColIndex, int margin) {
DefaultTableColumnModel colModel = (DefaultTableColumnModel)table.getColumnModel();
TableColumn col = colModel.getColumn(vColIndex);
int width = 0;
// Get width of column header
TableCellRenderer renderer = col.getHeaderRenderer();
if (renderer == null) {
renderer = table.getTableHeader().getDefaultRenderer();
}
java.awt.Component comp = renderer.getTableCellRendererComponent(
table, col.getHeaderValue(), false, false, 0, 0);
width = comp.getPreferredSize().width;
// Get maximum width of column data
for (int r=0; r<table.getRowCount(); r++) {
renderer = table.getCellRenderer(r, vColIndex);
comp = renderer.getTableCellRendererComponent(
table, table.getValueAt(r, vColIndex), false, false, r, vColIndex);
width = Math.max(width, comp.getPreferredSize().width);
}
// Add margin
width += 2*margin;
// Set the width
col.setPreferredWidth(width);
}
Table Column Adjuster works both statically and dynamically and the user can control this.

Categories

Resources