« Return to Thread: erratic scrolling behavior for a JTable with variable height rows

Re: erratic scrolling behavior for a JTable with variable height rows

by Larry Frissell :: Rate this Message:

Reply to Author | View in Thread

I believe this may work for you, changing the refresh() to:

    public JTable refresh(JTable mTable) {
       int temp;
       temp = mTable.getSelectedRow();
        this.fireTableChanged(new TableModelEvent(this));
        mTable.changeSelection(temp, 0, false, true);
        return mTable;
    }
Of couse, after a delete()  no columns are selected, so it will jump to the begining if you delete and refresh.
 
Larry

 
On Wed, Jun 17, 2009 at 3:42 PM, J. Talafous <jtalafous@...> wrote:
Here's a question for those Swing gurus...

I downloaded the source from this book:
http://java.sun.com/developer/Books/gui/swing2/
for the variable row height JTable found at the end of Chapter 18.  I
modified it to handle 351 rows of varying size data.  It is appended
at the end of this message as a single file named ExpenseReport.java.
I also added a refresh button whose actionPerformed() refreshes the
JTable by a fireTableChanged.  This button is the third button over

Straightforward stuff, right?  Well, compile this file and run it.
Scroll to (say) 75% with the scrollbar.  Click the refresh button.
The scrollbar jumps to the top and the user loses his place.   Is
there anyway to fix this example, so that I can apply it to my
problem?  The solution has eluded all of us.

Thanks for any help.

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.text.Format;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.EventObject;
import java.util.Locale;
import java.util.Vector;

import javax.swing.AbstractCellEditor;
import javax.swing.DefaultCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.SoftBevelBorder;
import javax.swing.event.TableModelEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

public class ExpenseReport extends JFrame {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    protected JTable m_table;
    protected ExpenseReportData m_data;
    protected JLabel m_tolatLbl;
    protected JLabel m_approvedLbl;
    protected NumberFormat m_moneyFormat = NumberFormat
            .getCurrencyInstance(Locale.US);

    static {
        UIManager.put("ComboBox.foreground", UIManager
                .getColor("Table.foreground"));
        UIManager.put("ComboBox.background", UIManager
                .getColor("Table.background"));
        UIManager.put("ComboBox.selectionForeground", UIManager
                .getColor("Table.selectionForeground"));
        UIManager.put("ComboBox.selectionBackground", UIManager
                .getColor("Table.selectionBackground"));
        UIManager.put("ComboBox.font", UIManager.getFont("Table.font"));
    }

    public ExpenseReport() {
        super("Expense Report");
        setSize(600, 300);

        m_data = new ExpenseReportData(this);

        m_table = new JTable();
        m_table.setAutoCreateColumnsFromModel(false);
        m_table.setModel(m_data);
        m_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        for (int k = 0; k < m_data.getColumnCount(); k++) {
            TableCellRenderer renderer = null;
            TableCellEditor editor = null;
            switch (k) {
            case ExpenseReportData.COL_DATE:
                SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy");
                renderer = new FormattedCellRenderer(dateFormat);
                editor = new FormattedCellEditor(new JFormattedTextField(
                        dateFormat));
                break;

            case ExpenseReportData.COL_AMOUNT:
                renderer = new FormattedCellRenderer(m_moneyFormat);
                editor = new FormattedCellEditor(new JFormattedTextField(
                        m_moneyFormat));
                break;

            case ExpenseReportData.COL_CATEGORY:
                renderer = new DefaultTableCellRenderer();
                JComboBox combo = new JComboBox(ExpenseReportData.CATEGORIES);
                combo.setRequestFocusEnabled(false);
                editor = new DefaultCellEditor(combo);
                break;

            case ExpenseReportData.COL_APPROVED:
                renderer = new CheckCellRenderer();
                JCheckBox chBox = new JCheckBox();
                chBox.setHorizontalAlignment(SwingConstants.CENTER);
                chBox.setBackground(m_table.getBackground());
                editor = new DefaultCellEditor(chBox);
                break;

            case ExpenseReportData.COL_DESCRIPTION:
                renderer = new TextAreaCellRenderer();
                editor = new TextAreaCellEditor();
                break;
            }
            if (renderer instanceof JLabel) {
                ((JLabel) renderer)

.setHorizontalAlignment(ExpenseReportData.m_columns[k].m_alignment);
            }
            if (editor instanceof DefaultCellEditor) {
                ((DefaultCellEditor) editor).setClickCountToStart(2);
            }

            TableColumn column = new TableColumn(k,
                    ExpenseReportData.m_columns[k].m_width, renderer, editor);
            m_table.addColumn(column);
        }

        JTableHeader header = m_table.getTableHeader();
        header.setUpdateTableInRealTime(false);

        JScrollPane ps = new JScrollPane();
        ps.getViewport().setBackground(m_table.getBackground());
        ps.setSize(550, 150);
        ps.getViewport().add(m_table);
        getContentPane().add(ps, BorderLayout.CENTER);

        JToolBar tb = createToolbar();
        getContentPane().add(tb, BorderLayout.NORTH);

        JPanel p = new JPanel(new GridLayout(1, 2, 5, 5));

        m_tolatLbl = new JLabel("Total: ");
        m_tolatLbl.setFont(new Font("Helvetica", Font.PLAIN, 14));
        m_tolatLbl.setBorder(new SoftBevelBorder(BevelBorder.LOWERED));
        p.add(m_tolatLbl);

        m_approvedLbl = new JLabel("Approved: ");
        m_approvedLbl.setFont(new Font("Helvetica", Font.PLAIN, 14));
        m_approvedLbl.setBorder(new SoftBevelBorder(BevelBorder.LOWERED));
        p.add(m_approvedLbl);

        getContentPane().add(p, BorderLayout.SOUTH);

        calcTotal();
    }

    public void calcTotal() {
        double total = 0;
        double approved = 0;
        for (int k = 0; k < m_data.getRowCount(); k++) {
            Double amount = (Double) m_data.getValueAt(k,
                    ExpenseReportData.COL_AMOUNT);
            total += amount.doubleValue();

            Boolean flag = (Boolean) m_data.getValueAt(k,
                    ExpenseReportData.COL_APPROVED);
            if (flag.booleanValue()) {
                approved += amount.doubleValue();
            }
        }
        m_tolatLbl.setText("Total: " + m_moneyFormat.format(total));
        m_approvedLbl.setText("Approved: " + m_moneyFormat.format(approved));
    }

    protected JToolBar createToolbar() {
        JToolBar tb = new JToolBar();
        tb.setFloatable(false);

        JButton bt = new JButton("insert row");
        bt.setToolTipText("Insert Row");
        bt.setRequestFocusEnabled(false);
        ActionListener lst = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int nRow = m_table.getSelectedRow() + 1;
                m_data.insert(nRow);

                m_table.tableChanged(new TableModelEvent(m_data, nRow, nRow,
                        TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT));
                m_table.setRowSelectionInterval(nRow, nRow);
            }
        };
        bt.addActionListener(lst);
        tb.add(bt);

        bt = new JButton("delete row");
        bt.setToolTipText("Delete Row");
        bt.setRequestFocusEnabled(false);
        lst = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int nRow = m_table.getSelectedRow();
                if (m_data.delete(nRow)) {
                    m_table.tableChanged(new TableModelEvent(m_data, nRow,
                            nRow, TableModelEvent.ALL_COLUMNS,
                            TableModelEvent.DELETE));
                    m_table.clearSelection();
                    calcTotal();
                }
            }
        };
        bt.addActionListener(lst);
        tb.add(bt);

        bt = new JButton("refresh table");
        bt.setToolTipText("Refresh table");
        bt.setRequestFocusEnabled(false);
        lst = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                m_data.refresh();
            }
        };
        bt.addActionListener(lst);
        tb.add(bt);

        return tb;
    }

    public static void main(String argv[]) {
        ExpenseReport frame = new ExpenseReport();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

class TextAreaCellRenderer extends JTextArea implements TableCellRenderer {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    protected static Border m_noFocusBorder = new EmptyBorder(1, 1, 1, 1);
    protected static Border m_focusBorder = UIManager
            .getBorder("Table.focusCellHighlightBorder");

    public TextAreaCellRenderer() {
        setEditable(false);
        setLineWrap(true);
        setWrapStyleWord(true);
        setBorder(m_noFocusBorder);
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int nRow, int nCol) {
        if (value instanceof String) {
            setText((String) value);
        }

        setBackground(isSelected && !hasFocus ? table.getSelectionBackground()
                : table.getBackground());
        setForeground(isSelected && !hasFocus ? table.getSelectionForeground()
                : table.getForeground());
        setFont(table.getFont());
        setBorder(hasFocus ? m_focusBorder : m_noFocusBorder);

        // Adjust row's height
        int width = table.getColumnModel().getColumn(nCol).getWidth();
        setSize(width, 1000);
        int rowHeight = getPreferredSize().height;
        if (table.getRowHeight(nRow) != rowHeight) {
            table.setRowHeight(nRow, rowHeight);
        }

        return this;
    }

    @Override
    public String getToolTipText(MouseEvent event) {
        return null;
    }
}

class TextAreaCellEditor extends AbstractCellEditor implements TableCellEditor {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    public static int CLICK_COUNT_TO_EDIT = 2;
    protected JTextArea m_textArea;
    protected JScrollPane m_scroll;

    public TextAreaCellEditor() {
        m_textArea = new JTextArea();
        m_textArea.setLineWrap(true);
        m_textArea.setWrapStyleWord(true);

        m_scroll = new JScrollPane(m_textArea,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    }

    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int nRow, int nCol) {
        m_textArea.setBackground(table.getBackground());
        m_textArea.setForeground(table.getForeground());
        m_textArea.setFont(table.getFont());
        m_textArea.setText(value == null ? "" : value.toString());

        return m_scroll;
    }

    public Object getCellEditorValue() {
        return m_textArea.getText();
    }

    @Override
    public boolean isCellEditable(EventObject anEvent) {
        if (anEvent instanceof MouseEvent) {
            int click = ((MouseEvent) anEvent).getClickCount();
            return click >= CLICK_COUNT_TO_EDIT;
        }
        return true;
    }
}

class CheckCellRenderer extends JCheckBox implements TableCellRenderer {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    protected static Border m_noFocusBorder = new EmptyBorder(1, 1, 1, 1);
    protected static Border m_focusBorder = UIManager
            .getBorder("Table.focusCellHighlightBorder");

    public CheckCellRenderer() {
        super();
        setOpaque(true);
        setBorderPainted(true);
        setBorder(m_noFocusBorder);
        setHorizontalAlignment(SwingConstants.CENTER);
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int nRow, int nCol) {
        if (value instanceof Boolean) {
            Boolean b = (Boolean) value;
            setSelected(b.booleanValue());
        }

        setBackground(isSelected && !hasFocus ? table.getSelectionBackground()
                : table.getBackground());
        setForeground(isSelected && !hasFocus ? table.getSelectionForeground()
                : table.getForeground());
        setFont(table.getFont());
        setBorder(hasFocus ? m_focusBorder : m_noFocusBorder);

        return this;
    }
}

class FormattedCellRenderer extends DefaultTableCellRenderer {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
    protected Format m_format;

    public FormattedCellRenderer(Format format) {
        m_format = format;
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int nRow, int nCol) {
        return super.getTableCellRendererComponent(table, value == null ? null
                : m_format.format(value), isSelected, hasFocus, nRow, nCol);
    }
}

class FormattedCellEditor extends DefaultCellEditor {
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    public FormattedCellEditor(final JFormattedTextField formattedTextField) {
        super(formattedTextField);
        formattedTextField.removeActionListener(delegate);
        delegate = new EditorDelegate() {
            /**
             *
             */
            private static final long serialVersionUID = 1L;

            @Override
            public void setValue(Object value) {
                formattedTextField.setValue(value);
            }

            @Override
            public Object getCellEditorValue() {
                return formattedTextField.getValue();
            }
        };
        formattedTextField.addActionListener(delegate);
        formattedTextField.setBorder(null);
    }
}

class ExpenseData {
    public Date m_date;
    public Double m_amount;
    public Integer m_category;
    public Boolean m_approved;
    public String m_description;

    public ExpenseData() {
        m_date = new Date();
        m_amount = new Double(0);
        m_category = new Integer(1);
        m_approved = new Boolean(false);
        m_description = "";
    }

    public ExpenseData(Date date, double amount, int category,
            boolean approved, String description) {
        m_date = date;
        m_amount = new Double(amount);
        m_category = new Integer(category);
        m_approved = new Boolean(approved);
        m_description = description;
    }
}

class ColumnData {
    public String m_tolatLbl;
    int m_width;
    int m_alignment;

    public ColumnData(String title, int width, int alignment) {
        m_tolatLbl = title;
        m_width = width;
        m_alignment = alignment;
    }
}

class ExpenseReportData extends AbstractTableModel {
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    public static final ColumnData m_columns[] = {
            new ColumnData("Date", 80, SwingConstants.LEFT),
            new ColumnData("Amount", 80, SwingConstants.RIGHT),
            new ColumnData("Category", 130, SwingConstants.LEFT),
            new ColumnData("Approved", 80, SwingConstants.CENTER),
            new ColumnData("Description", 180, SwingConstants.LEFT) };

    public static final int COL_DATE = 0;
    public static final int COL_AMOUNT = 1;
    public static final int COL_CATEGORY = 2;
    public static final int COL_APPROVED = 3;
    public static final int COL_DESCRIPTION = 4;

    public static final String[] CATEGORIES = { "Fares", "Logging",
            "Business meals", "Others" };

    protected ExpenseReport m_parent;
    protected Vector m_vector;

    public ExpenseReportData(ExpenseReport parent) {
        m_parent = parent;
        m_vector = new Vector();
        setDefaultData();
    }

    public void setDefaultData() {
        m_vector = new Vector();
        try {
            SimpleDateFormat f = new SimpleDateFormat("MM/dd/yy");
            for (int i=0; i<351; i++) {
            m_vector.addElement(new ExpenseData(f.parse("12/06/04"), 200, 0,
                    true, "Airline tickets"));
            m_vector.addElement(new ExpenseData(f.parse("12/05/04"), 50, 2,
                    false, "Lunch with client"));
            m_vector
                    .addElement(new ExpenseData(
                            f.parse("12/05/04"),
                            120,
                            1,
                            true,
                            "Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Quisque nec urna nulla. Vivamus at tellus ut sapien
consequat hendrerit. Vestibulum eu faucibus enim. Vivamus cursus enim
vel nisl dictum imperdiet. Sed mollis, augue sit amet accumsan
condimentum, sapien orci molestie augue, fermentum facilisis nunc
libero lacinia dui. Duis non ipsum nec arcu condimentum dignissim.
Suspendisse quis justo nec lacus tristique faucibus. Vestibulum
scelerisque tristique mollis. Vivamus lobortis tempor lectus eget
iaculis. Sed viverra varius tempor. In laoreet nulla sed felis
dignissim accumsan. Suspendisse potenti. Suspendisse quam nisi,
pellentesque quis porttitor quis, consequat eget eros. Vivamus
vestibulum odio sit amet purus auctor viverra. Proin tempus venenatis
ornare. Duis eget sapien nibh. "));
            }
            } catch (java.text.ParseException ex) {
        }
    }

    public int getRowCount() {
        return m_vector == null ? 0 : m_vector.size();
    }

    public int getColumnCount() {
        return m_columns.length;
    }

    @Override
    public String getColumnName(int nCol) {
        return m_columns[nCol].m_tolatLbl;
    }

    @Override
    public boolean isCellEditable(int nRow, int nCol) {
        return true;
    }

    public Object getValueAt(int nRow, int nCol) {
        if (nRow < 0 || nRow >= getRowCount()) {
            return "";
        }
        ExpenseData row = (ExpenseData) m_vector.elementAt(nRow);
        switch (nCol) {
        case COL_DATE:
            return row.m_date;
        case COL_AMOUNT:
            return row.m_amount;
        case COL_CATEGORY:
            return CATEGORIES[row.m_category.intValue()];
        case COL_APPROVED:
            return row.m_approved;
        case COL_DESCRIPTION:
            return row.m_description;
        }
        return "";
    }

    @Override
    public void setValueAt(Object value, int nRow, int nCol) {
        if (nRow < 0 || nRow >= getRowCount() || value == null) {
            return;
        }
        ExpenseData row = (ExpenseData) m_vector.elementAt(nRow);
        String svalue = value.toString();

        switch (nCol) {
        case COL_DATE:
            row.m_date = (Date) value;
            break;
        case COL_AMOUNT:
            if (value instanceof Double) {
                row.m_amount = (Double) value;
            } else {
                row.m_amount = new Double(((Number) value).doubleValue());
            }
            m_parent.calcTotal();
            break;
        case COL_CATEGORY:
            for (int k = 0; k < CATEGORIES.length; k++) {
                if (svalue.equals(CATEGORIES[k])) {
                    row.m_category = new Integer(k);
                    break;
                }
            }
            break;
        case COL_APPROVED:
            row.m_approved = (Boolean) value;
            m_parent.calcTotal();
            break;
        case COL_DESCRIPTION:
            row.m_description = svalue;
            break;
        }
    }

    public void insert(int nRow) {
        if (nRow < 0) {
            nRow = 0;
        }
        if (nRow > m_vector.size()) {
            nRow = m_vector.size();
        }
        m_vector.insertElementAt(new ExpenseData(), nRow);
    }

    public boolean delete(int nRow) {
        if (nRow < 0 || nRow >= m_vector.size()) {
            return false;
        }
        m_vector.remove(nRow);
        return true;
    }

    public void refresh() {
        this.fireTableChanged(new TableModelEvent(this));
    }
}

_______________________________________________
ajug-members mailing list
ajug-members@...
http://www.ajug.org/mailman/listinfo/ajug-members


_______________________________________________
ajug-members mailing list
ajug-members@...
http://www.ajug.org/mailman/listinfo/ajug-members

 « Return to Thread: erratic scrolling behavior for a JTable with variable height rows