4

I am developing a Java Desktop Application (jdk1.6) with Swing. My problem is about Multi-line cells(text-wrapping) with auto-adjusting cell height property in JTable.

I could already implement this structure in this way:

  • Table has its own cellrenderer.
  • Cells are JTextArea with wraptext=true
  • I count the rows in the JTextArea after inserting the text to the cell and adjust the row height of related row accordingly.
  • Cell width is automatically adjusted. (from preferred size)

2 problems about this structure:

1) During the execution of the program, it can count the rows and adjusts the row height properly.

But at the first initialization (first setModel()) it calculates the row count of "the first cell" of the table,i.e (0,0), much more than it is. I have debugged the code and found that it counts the letters in the text and multiplies with row height 16. (as if the width of cell was 1 letter) . At the end I get a very very high first row. Other rows are ok.

When I doesn't insert any text to (0,0), no problem occurs.

2) Row count doesn't work when I disable the table auto-resize property and determine the cell widths manually.

Here is my cell renderer:

public class MultiLineCellRenderer extends JTextArea implements TableCellRenderer {
private JTable DependentTable;

public MultiLineCellRenderer() {
DependentTable=null;
setLineWrap(true);
setWrapStyleWord(true);
setOpaque(true);
} 

public Component getTableCellRendererComponent(JTable table, Object value,
  boolean isSelected, boolean hasFocus, int row, int column) {      


   (...) //some background color adjustments


   setText((value == null) ? "" : value.toString());


   int numOfLines = getWrappedLines(this); // Counting the lines
   int height_normal = table.getRowHeight(row);// read the height of the row

   if(DependentTable == null) // for this case always null
   {
    if (height_normal < numOfLines*16)
    {
        table.setRowHeight(row,numOfLines*16);
    }
   }
  else
        (...)

return this;

}

Here how I calculate number of rows:

public static int getWrappedLines(JTextArea component)
{
    View view = component.getUI().getRootView(component).getView(0);
    int preferredHeight = (int)view.getPreferredSpan(View.Y_AXIS);
    int lineHeight = component.getFontMetrics( component.getFont() ).getHeight();
    return preferredHeight / lineHeight;
} 

------------------------------------

I removed the row-adjusting-code to the outside of the class. The table has a Model Listener now. The first row is not extreme large in this time. Calling this method works but the problem is about counting the wrapped lines. Every time I fill in the cell with a very long text, the line is wrapped properly but my counter returns 1. (code the wrappedline counter is above. same as the one in renderer. But It worked fine there)

here is my model listener:

public class ModelListener implements TableModelListener {

JTable mainTable;
JTable depTable;

public ModelListener(JTable m, JTable d) {
    mainTable = m;
    depTable = d;
}

public ModelListener(JTable m){
    mainTable = m;
    depTable = null;       
}

public void tableChanged(TableModelEvent tme) {
    int fRow = tme.getFirstRow();
    int col = tme.getColumn();

    JTextArea cellArea = (JTextArea)mainTable.getDefaultRenderer(Object.class);

    int numOfLines = getWrappedLines(cellArea); //countLines();
    int height_normal = mainTable.getRowHeight(fRow);


    System.out.println("h normal:"+height_normal);
    System.out.println("numLines:"+numOfLines);
    System.out.println("value:"+mainTable.getModel().getValueAt(fRow, col));
    System.out.println("width:"+cellArea.getPreferredSize().width);

    if(depTable == null)
    {
        if (height_normal < numOfLines*16)
        {
            mainTable.setRowHeight(fRow,numOfLines*16);
        }
    }
    else
    {
       //(---)
    }
    mainTable.repaint();

}

The result of printlines:

  • preferredHeight: 15 // from wrapped lines function
  • lineHeight: 15 // from wrapped lines function
  • h normal:25
  • numLines:1
  • value:looooooooooooooooooooooonng tesssssssssssssssssssssssst // appears to be 2 lines
  • width:104

Thanks in advance :)

Isil

alex2410
  • 10,904
  • 3
  • 25
  • 41
isilpekel
  • 121
  • 1
  • 8
  • Didn't look into your problem, just: do _not_ (as in no-no-never-ever) change the table's state from inside the renderer. Instead, move the sizing logic somewhere else: loop through the table's rows, prepare the renderers with the values, query its preferred size/wrapping lines and set the row heights accordingly – kleopatra Nov 06 '11 at 10:02
  • Could it be that the JTextArea thinks that it is only one line high?? Without setting the width of the JTextArea, you will never be able to determine how heigh it should be. – MadProgrammer Jul 11 '12 at 00:41
  • But the width was automatically set and this problem only happens in the (0,0) index of the related table although all cells are derived from the same classes with the same properties. I also remember setting the cell width to a fixed value (sorry the project isn't being developed anymore) it didn't help. – isilpekel Jul 11 '12 at 16:11
  • @kleopatra would you mind explaining why? – Igor Dec 20 '12 at 00:19
  • @isilpekel I've been testing your code an the problem goes away when you follow kleopatra's advice. Put the row height resizing code in its own method not in the renderer. – DSquare Jan 31 '14 at 21:37
  • @DSquare thank you for the answer but I cannot decide which method works. I don't have the code any more. Obviously, the problem was the bad design. Your answer will be a reference for the others. – isilpekel Feb 01 '14 at 17:26

1 Answers1

2

Just for having the solution at Hand right now, even after a few years, it may help some others. you can set a fixed number of rows or use a dynamic row Count which fits the Content. it should be easy for you to Extend this in any way.

package com.mycompany;

import java.awt.Component;
import java.util.List;

import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;
import javax.swing.table.TableCellRenderer;

public class MultiLineCellRenderer extends JTextArea implements
TableCellRenderer {

    private static final long serialVersionUID = 1L;
    boolean                     limit               = false;

    public MultiLineCellRenderer() {
        setLineWrap(true);
        setWrapStyleWord(true);
        setOpaque(true);
        setBorder(new EmptyBorder(-1, 2, -1, 2));
        this.limit = false;
        setRows(1);
    }

    public MultiLineCellRenderer(int rows) {
        this();
        setRows(rows);
        this.limit = true;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        setText(value == null ? "" : value.toString());

        int dynamicHeight = getLineCount() > getRows() && this.limit ? getRows() : getLineCount();
        int newHeight = table.getRowHeight() * dynamicHeight;
        if (table.getRowHeight(row) != newHeight) {
            table.setRowHeight(row, newHeight);
        }

        if (isSelected) {
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
        }
        else {
            setForeground(table.getForeground());
            setBackground(table.getBackground());
        }

        return this;
    }
}
gantners
  • 471
  • 4
  • 16
  • Minor flaw: the number of lines seems to be the number of newlines plus 1—if long lines wrap, this does not seem to get considered. – user149408 May 02 '20 at 21:22