Reputation: 977
I write a Java application and I need to a object of class JTable
that has some features :
But my code does not work fast and has not top features completely, I am write a minimal version of my application in last of question:
This is my gui class:
import javax.swing.*;
import java.awt.*;
public class GUI extends JFrame {
private BankTable table;
private JScrollPane scrollPane;
public GUI(){
super("Bank Table");
JPanel contentPanel = new JPanel();
setContentPane(contentPanel);
contentPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
setLayout(new BorderLayout());
setMinimumSize(new Dimension(1000,700));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
makeTable();
scrollPane = new JScrollPane(table);
scrollPane.getVerticalScrollBar().setUnitIncrement(50);
add(scrollPane,BorderLayout.CENTER);
setVisible(true);
}
public void makeTable(){
String[][]data= new String[][]{{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/01"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/02"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/03"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/04"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/05"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/06"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/07"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/08"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/09"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/10"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/11"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/12"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/13"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/14"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/15"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/16"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/17"},
{"0212670003009", "ص 318", "77081111111111111111111111111111111111634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/18"},
{"0212670003009", "ص 318", "77081222222222222222222222222222634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/19"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/20"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/21"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/22"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/23"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/24"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/25"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/26"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/27"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/28"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/29"},
{"0212670003009", "ص 318", "77081634","38000000","0","331142","2102","وصول چک","08:56:46","1397/05/30"}};
String[] columns= new String[]{"شماره حساب", "اطلاعات اضافه", "مانده","واریز","برداشت","فیش/ حواله","کد شعبه","شرح","ساعت","تاریخ"};
table = new BankTable(data,columns);
table.updateRowHeights();
System.out.println("Table");
}
public static void main(String args[]){
GUI gui = new GUI();
}
}
This is render of table :
import java.awt.*;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
public class TextAreaRenderer extends JTextArea
implements TableCellRenderer {
public TextAreaRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
setEditable(false);
setCursor(null);
setOpaque(false);
setFocusable(false);
setLineWrap(true);
setWrapStyleWord(true);
}
public Component getTableCellRendererComponent(JTable jTable,
Object obj, boolean isSelected, boolean hasFocus, int row,
int column) {
setText((String)obj);
setFont(new Font("bnazanin", Font.BOLD, 15));
return this;
}
}
And This is table class:
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
public class BankTable extends JTable {
public BankTable(String[][] data,String[] columns){
super(data,columns);
setForeground(Color.BLACK);
setBounds(60,80,400,600);
changeTableHeader();
DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
rightRenderer.setHorizontalAlignment(JLabel.RIGHT);
TextAreaRenderer textAreaRenderer = new TextAreaRenderer();
for(int i=0 ; i<columns.length ; i++){
getColumnModel().getColumn(i).setCellRenderer(textAreaRenderer);
}
}
public void changeTableHeader(){
getTableHeader().setBackground(new Color(57,77,112));
getTableHeader().setForeground(Color.WHITE);
getTableHeader().setFont(new Font("Calibri Light", Font.BOLD, 20));
}
@Override
public boolean isCellEditable(int i, int i1) {
return false;
}
public void updateRowHeights()
{
for (int row = 0; row < getRowCount(); row++)
{
int rowHeight = getRowHeight();
for (int column = 0; column < getColumnCount(); column++)
{
Component comp = prepareRenderer(getCellRenderer(row, column), row, column);
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}
setRowHeight(row, rowHeight + 20);
}
}
}
EDIT: This is a simple for my table
Upvotes: 1
Views: 1262
Reputation: 347194
So, I've spent a bit of time banging my head against this trying a verity of different things.
Starting with TextAreaRenderer
. I'd prefer to use the DefaultTableCelllRenderer
, but after playing around with that, I wasn't able to get it to work satisfactorily. So, instead, I took most of it's own optimisations and applied them to the TextAreaRenderer
itself.
One of things you really want to do is reduce the amount changes that each getTableCellRendererComponent
will make. JTextArea
is already a complex component.
I removed the setOpaque
call and moved the setFont
call to the constructor. I also added selection support (it was part of my testing)
public class TextAreaRenderer extends JTextArea
implements TableCellRenderer {
public TextAreaRenderer() {
setLineWrap(true);
setWrapStyleWord(true);
setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
setEditable(false);
setCursor(null);
//setOpaque(false);
setFocusable(false);
setLineWrap(true);
setWrapStyleWord(true);
setFont(new Font("bnazanin", Font.BOLD, 15));
getCaret().setBlinkRate(0);
}
public Component getTableCellRendererComponent(JTable jTable,
Object obj, boolean isSelected, boolean hasFocus, int row,
int column) {
setText((String) obj);
if (isSelected) {
System.out.println("!!Selected");
setBackground(jTable.getSelectionBackground());
setForeground(jTable.getSelectionForeground());
} else {
setBackground(jTable.getBackground());
setForeground(jTable.getForeground());
}
return this;
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*/
public boolean isOpaque() {
Color back = getBackground();
Component p = getParent();
if (p != null) {
p = p.getParent();
}
// p should now be the JTable.
boolean colorMatch = (back != null) && (p != null)
&& back.equals(p.getBackground())
&& p.isOpaque();
return !colorMatch && super.isOpaque();
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*
* @since 1.5
*/
public void invalidate() {
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*/
public void validate() {
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*/
public void revalidate() {
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*/
public void repaint(long tm, int x, int y, int width, int height) {
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*/
public void repaint(Rectangle r) {
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*
* @since 1.5
*/
public void repaint() {
}
/**
* Overridden for performance reasons. See the
* <a href="#override">Implementation Note</a>
* for more information.
*/
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
}
}
I then attacked the BlankTable
...
One of the big changes is the table is now capable of calculating the height of the rows dynamically. Because the calculation is expensive, the result of this operation is cached. The problem, though, is the JTable
also has it's own internal cache ... which is private 🤬 ... so rather then tracking down the reflection route, we need to provide some compensation to circumvent some of the side effects this API creates.
When calling setRowHeight
, the JTable
call invalidate
and repaint
itself. When we're performing our calculations we'd like to stop this, but there are some circumstances under which we should still allow to work (such as when the columns are resized and the JTable
is invalidated
)
public class BankTable extends JTable {
public BankTable(String[][] data, String[] columns) {
super(data, columns);
changeTableHeader();
setGridColor(Color.RED);
TableCellRenderer textAreaRenderer = new TextAreaRenderer();
for (int i = 0; i < columns.length; i++) {
getColumnModel().getColumn(i).setCellRenderer(textAreaRenderer);
}
}
public void changeTableHeader() {
getTableHeader().setBackground(new Color(57, 77, 112));
getTableHeader().setForeground(Color.WHITE);
getTableHeader().setFont(new Font("Calibri Light", Font.BOLD, 20));
}
@Override
public boolean isCellEditable(int i, int i1) {
return false;
}
private Map<Integer, Integer> rowMap = new HashMap<>();
private boolean quietUpdate = false;
private boolean forceUpdate = false;
@Override
public void invalidate() {
rowMap.clear();
forceUpdate = true;
super.invalidate();
forceUpdate = false;
}
@Override
protected void resizeAndRepaint() {
if (quietUpdate) {
if (forceUpdate) {
super.resizeAndRepaint();
}
} else {
super.resizeAndRepaint();
}
}
@Override
public int getRowHeight(int row) {
Integer rowHeight = rowMap.get(row);
if (rowHeight == null) {
TableColumnModel model = getColumnModel();
rowHeight = getRowHeight();
for (int column = 0; column < getColumnCount(); column++) {
int colWidth = model.getColumn(column).getWidth();
Component comp = prepareRenderer(getCellRenderer(row, column), row, column);
comp.setSize(new Dimension(colWidth, Integer.MAX_VALUE));
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}
rowMap.put(row, rowHeight);
quietUpdate = true;
setRowHeight(row, rowHeight);
quietUpdate = false;
}
return rowHeight;
}
@Override
public void tableChanged(TableModelEvent e) {
if (rowMap == null) {
super.tableChanged(e);
return;
}
if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
rowMap.clear();
super.tableChanged(e);
return;
}
if (e.getType() == TableModelEvent.INSERT) {
super.tableChanged(e);
return;
}
int modelColumn = e.getColumn();
int start = e.getFirstRow();
int end = e.getLastRow();
if (e.getType() == TableModelEvent.DELETE || e.getType() == TableModelEvent.UPDATE) {
for (int row = start; row <= end; row++) {
rowMap.remove(row);
}
super.tableChanged(e);
return;
}
if (end == Integer.MAX_VALUE) {
rowMap.clear();
}
super.tableChanged(e); //To change body of generated methods, choose Tools | Templates.
}
}
There are still a few other areas which might need further optimising (I've not tested a sorted table, so tableChanged
which need to be updated and I've not tested for changes to a cell either)
After the initial load, I've found the rendering to be reasonable. Because of the component in use, the font and other system issues, you're milage may fair.
Upvotes: 1
Reputation: 324108
For what is worth here is some old code I had lying around.
It uses a JTextArea with wrapping but limits the lines in the text area to 2 lines. You can then scroll the text area to see the other lines.
Maybe this type of approach will help with your requirement?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableTextArea extends JFrame
{
public TableTextArea()
{
JTable table = new JTable(40, 5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
table.setRowHeight(40);
table.setValueAt("one two three four five six seven eight nine ten", 0, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 1, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 0, 4);
table.setValueAt("one two three four five six seven eight nine ten", 2, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 3, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 4, 4);
table.setValueAt("one two three four five six seven eight nine ten", 5, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 6, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 7, 4);
table.setValueAt("one two three four five six seven eight nine ten", 8, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 9, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 10, 4);
table.setValueAt("one two three four five six seven eight nine ten", 11, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 12, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 13, 4);
table.setValueAt("one two three four five six seven eight nine ten", 14, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 15, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 16, 4);
table.setValueAt("one two three four five six seven eight nine ten", 17, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 18, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 19, 4);
table.setValueAt("one two three four five six seven eight nine ten", 20, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 21, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 22, 4);
table.setValueAt("one two three four five six seven eight nine ten", 23, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 24, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 25, 4);
table.setValueAt("one two three four five six seven eight nine ten", 26, 2);
table.setValueAt("aaa bbb ccccc dddd eeee fff ggggg hhhhh iiii jjj", 27, 2);
table.setValueAt("1111 2222 3333 4444 5555 6666 7777 8888 9999 0000", 28, 4);
JScrollPane scrollPane = new JScrollPane( table );
add( scrollPane );
// Override default renderer for a specific column
TableCellRenderer renderer = new TextAreaRenderer();
table.getColumnModel().getColumn(2).setCellRenderer( renderer );
table.getColumnModel().getColumn(4).setCellRenderer( renderer );
table.changeSelection(0, 0, false, false);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
TableTextArea frame = new TableTextArea();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
});
}
/*
**
*/
class TextAreaRenderer implements TableCellRenderer
{
private JTextArea renderTextArea;
private JScrollPane renderScrollPane;
private JTextArea focusTextArea;
private JScrollPane focusScrollPane;
private boolean firstTime = true;
public TextAreaRenderer()
{
renderTextArea = new JTextArea();
renderTextArea.setEditable( false );
renderTextArea.setLineWrap( true );
renderTextArea.setWrapStyleWord( true );
renderScrollPane = new JScrollPane( renderTextArea );
renderScrollPane.setBorder(null);
renderScrollPane.revalidate();
focusTextArea = new JTextArea();
focusTextArea.setEditable( false );
focusTextArea.setLineWrap( true );
focusTextArea.setWrapStyleWord( true );
focusScrollPane = new JScrollPane( focusTextArea );
focusScrollPane.setBorder(null);
}
public Component getTableCellRendererComponent(
final JTable table, Object value, boolean isSelected, boolean hasFocus, final int row, final int column)
{
// For some reason the scrollbars don't appear on the first cell renderered.
// Forcing a repaint of the cell seems to fix the problem.
if (firstTime)
{
firstTime = false;
Rectangle cellRectangle = table.getCellRect(row, column, false);
// renderScrollPane.setBounds(cellRectangle);
// table.add(renderScrollPane);
// renderScrollPane.revalidate();
table.repaint(cellRectangle);
}
System.out.println(row + " :: " + column + " : " + hasFocus);
table.remove(focusScrollPane);
renderTextArea.setText( value != null ? value.toString() : "" );
renderTextArea.setCaretPosition(0);
if (hasFocus)
{
renderTextArea.setBackground( table.getSelectionBackground() );
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
addRealTextAreaToTable(table, row, column);
}
});
}
else if (isSelected)
renderTextArea.setBackground( table.getSelectionBackground() );
else
renderTextArea.setBackground( table.getBackground() );
return renderScrollPane;
}
private void addRealTextAreaToTable(JTable table, int row, int column)
{
System.out.println(row + " :: " + column);
Object value = table.getValueAt(row, column);
focusTextArea.setText( value != null ? value.toString() : "" );
focusTextArea.setCaretPosition(0);
// focusTextArea.setBackground( table.getBackground() );
focusTextArea.setBackground( table.getSelectionBackground() );
Rectangle cellRectangle = table.getCellRect(row, column, false);
focusScrollPane.setBounds(cellRectangle);
table.add(focusScrollPane);
focusScrollPane.revalidate();
focusTextArea.requestFocusInWindow();
}
}
}
Upvotes: 2