为什么我无法使用鼠标可靠地取消选择/选择JTable布尔字段,如果我使用键盘进入字段,我总是可以使用空格键取消选择/选择。但是有时鼠标会起作用,有时甚至不起作用。
为了使事情进一步复杂化,IS_COMPILATION布尔字段的基础数据为true / false,但对于所有其他基础字段为1/0,IS_COMPILATION字段似乎始终正常,但其他字段是零星的。
尝试在没有太多运气的情况下评论各种代码。我在这里发布了JTable和TableModel子类。
import com.jthink.songkong.text.TextLabel;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.ArrayList;
public class EditSongsTable extends JTable
{
public EditSongsTable()
{
//Disable F2 key for editing and replace with Enter character.
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(java.awt.event.
KeyEvent.VK_F2, 0), "none");
getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(java.awt.event.
KeyEvent.VK_ENTER, 0), "startEditing");
//Changes Editor for Strings
setDefaultEditor(Object.class, new TextFieldCellEditor());
CopyAction copy = new CopyAction(this);
PasteAction paste = new PasteAction(this);
//Add Copy/Paste Popup Menu
final JPopupMenu pm = new JPopupMenu();
pm.add(copy);
pm.add(paste);
//Replace default table actions with our copy/paste actions
getActionMap().put("copy", copy);
getActionMap().put("paste", paste);
addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
if (e.isPopupTrigger())
{
highlightCells(e);
doPopup(e);
}
}
@Override
public void mouseReleased(MouseEvent e)
{
if (e.isPopupTrigger())
{
highlightCells(e);
doPopup(e);
}
}
protected void doPopup(MouseEvent e)
{
pm.show(e.getComponent(), e.getX(), e.getY());
}
/**
* Highlight cell if nothing selected
*
* @param e
*/
protected void highlightCells(MouseEvent e)
{
JTable table = (JTable) e.getSource();
if(table.getSelectedRows().length==0 || table.getSelectedColumns().length==0)
{
Point point = e.getPoint();
int row = table.rowAtPoint(point);
int col = table.columnAtPoint(point);
table.setRowSelectionInterval(row, row);
table.setColumnSelectionInterval(col, col);
}
}
});
}
/**
* RowNo column needs a different renderer from default
*
* @param row
* @param column
* @return the row label renderer
*/
@Override
public TableCellRenderer getCellRenderer(int row, int column)
{
if (column == 0)
{
return TableRowLabelRenderer.getInstanceOf();
}
else
{
return super.getCellRenderer(row, column);
}
}
class CopyAction extends AbstractAction
{
private JTable table;
public CopyAction(JTable table)
{
this.table = table;
putValue(NAME, TextLabel.COPYBUTTON.getMsg());
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
}
@Override
public void actionPerformed(ActionEvent e)
{
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
java.util.List<Object> data = new ArrayList<>();
final int[] rows = getSelectedRows();
for (int row : rows)
{
final int[] cols = getSelectedColumns();
for (int col : cols)
{
data.add(table.getValueAt(row, col));
}
}
cb.setContents(new CellTransferable(data), null);
}
}
class PasteAction extends AbstractAction
{
private JTable table;
public PasteAction(JTable tbl)
{
putValue(NAME, TextLabel.PASTEBUTTON.getMsg());
putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
table = tbl;
final Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
cb.addFlavorListener(new FlavorListener()
{
@Override
public void flavorsChanged(FlavorEvent e)
{
setEnabled(cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR)
|| cb.isDataFlavorAvailable(DataFlavor.stringFlavor));
}
});
setEnabled(cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR)
|| cb.isDataFlavorAvailable(DataFlavor.stringFlavor));
}
@Override
public void actionPerformed(ActionEvent e)
{
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
if (cb.isDataFlavorAvailable(CellTransferable.CELL_DATA_FLAVOR))
{
try
{
int i =0;
java.util.List<Object> values = (java.util.List<Object>) cb.getData(CellTransferable.CELL_DATA_FLAVOR);
final int[] rows = getSelectedRows();
for (int row : rows)
{
final int[] cols = getSelectedColumns();
for (int col : cols)
{
if(i>=values.size())
{
i=0;
}
if(table.getColumnClass(col)==Boolean.class)
{
if(values.get(i) instanceof Boolean)
{
table.setValueAt(values.get(i), row, col);
i++;
}
}
else
{
if(values.get(i) instanceof Boolean)
{
table.setValueAt(((Boolean)values.get(i)).toString(), row, col);
}
else
{
table.setValueAt(values.get(i), row, col);
}
i++;
}
}
}
}
catch (UnsupportedFlavorException | IOException ex)
{
ex.printStackTrace();
}
}
else if(cb.isDataFlavorAvailable(DataFlavor.stringFlavor))
{
try
{
String data = (String)cb.getData(DataFlavor.stringFlavor);
final int[] rows = getSelectedRows();
for (int row : rows)
{
final int[] cols = getSelectedColumns();
for (int col : cols)
{
table.setValueAt(data, row, col);
}
}
}
catch (UnsupportedFlavorException | IOException ex)
{
ex.printStackTrace();
}
}
}
}
public static class CellTransferable implements Transferable
{
public static final DataFlavor CELL_DATA_FLAVOR = new DataFlavor(Object.class, "application/x-cell-value");
private Object cellValue;
public CellTransferable(Object cellValue)
{
this.cellValue = cellValue;
}
@Override
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[]{CELL_DATA_FLAVOR};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return CELL_DATA_FLAVOR.equals(flavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
{
if (!isDataFlavorSupported(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
return cellValue;
}
}
}
表格型号
import com.google.common.collect.ArrayTable;
import com.google.common.collect.Table;
import com.jthink.songkong.text.SongFieldDataType;
import com.jthink.songkong.text.SongFieldName;
import com.jthink.songlayer.Song;
import com.jthink.songlayer.SongFieldKey;
import javax.swing.table.DefaultTableModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import static com.jthink.songkong.analyse.toplevelanalyzer.EditSongsController.EDIT_MULTI_VALUE;
/**
* Edit Songs table model
*
*/
public class EditSongsTableModel extends DefaultTableModel
{
private Table<Song, SongFieldName, String> edits = null;
private List<SongFieldName> fields;
private List<Song> songs;
public EditSongsTableModel(Set<SongFieldName> fields, List<Song> songs)
{
super(songs.size(), fields.size());
this.fields= new ArrayList<>(fields);
this.songs=songs;
edits = ArrayTable.create(songs, fields);
for(Song song:songs)
{
song.setNewFilename(song.getFilename());
}
Vector<String> columnNames = new Vector<>();
columnNames.add("#");
for (SongFieldName next : fields)
{
columnNames.add(next.getName());
}
this.setColumnIdentifiers(columnNames);
}
/**
* Get the value relating this table field, making adjustments for certain fields for display
*
* @param row
* @param column
* @return
*/
public Object getValueAt(int row, int column)
{
if(column==0)
{
return String.valueOf(row + 1);
}
else
{
SongFieldName sfn = fields.get(column -1);
Song song = songs.get(row);
if(sfn.getSongFieldKey()== SongFieldKey.FILENAME)
{
return song.getNewFilename();
}
else if (sfn.getDataType() == SongFieldDataType.BOOLEAN)
{
if(sfn==SongFieldName.IS_COMPILATION)
{
return Boolean.valueOf(song.getFieldValueOrEmptyString(sfn.getSongFieldKey()));
}
else
{
if(song.getFieldValueOrEmptyString(sfn.getSongFieldKey()).equals("1"))
{
return Boolean.TRUE;
}
else
{
return Boolean.FALSE;
}
}
}
else
{
return song.getFieldValueTripleSemiColonSeparatedOrEmptyString(sfn.getSongFieldKey());
}
}
}
@Override
public void setValueAt(Object val, int row, int column)
{
SongFieldName sfn = fields.get(column - 1);
Song song = songs.get(row);
if(edits.get(song,sfn)==null)
{
//Save Original Value
edits.put(song, sfn, song.getFieldValueOrEmptyString(sfn.getSongFieldKey()));
}
if(sfn.getSongFieldKey()== SongFieldKey.FILENAME)
{
song.setNewFilename((String) val);
}
else if(sfn.getDataType()== SongFieldDataType.BOOLEAN)
{
if(sfn==SongFieldName.IS_COMPILATION)
{
song.setField(sfn.getSongFieldKey(), ((Boolean) val).toString());
}
else
{
if(val==Boolean.TRUE)
{
song.setField(sfn.getSongFieldKey(), "1");
}
else
{
song.setField(sfn.getSongFieldKey(), "0");
}
}
}
else
{
song.setField(sfn.getSongFieldKey(),
((String) val).replace(EDIT_MULTI_VALUE, "\0"));
}
fireTableCellUpdated(row, column);
}
public Class getColumnClass(int columnIndex)
{
if(columnIndex == 0)
{
return String.class;
}
else
{
SongFieldName sfn = fields.get(columnIndex - 1);
if (sfn.getDataType() == SongFieldDataType.BOOLEAN)
{
return Boolean.class;
}
else
{
return super.getColumnClass((columnIndex));
}
}
}
/**
* Cannot Edit table Row Header
*
* @param rowIndex
* @param columnIndex
* @return
*/
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return columnIndex > 0;
}
/**
* Reset to original data
*
* @return
*/
public boolean reset()
{
for(Table.Cell<Song, SongFieldName, String> cell:edits.cellSet())
{
if(cell.getValue()!=null)
{
SongFieldName sfn = cell.getColumnKey();
if(sfn.getSongFieldKey()== SongFieldKey.FILENAME)
{
cell.getRowKey().setNewFilename(cell.getValue());
}
else
{
cell.getRowKey().setField(cell.getColumnKey().getSongFieldKey(), cell.getValue());
}
}
}
fireTableDataChanged();
return true;
}
}
TextFieldCellEditor
import javax.swing.*;
import javax.swing.text.Caret;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.util.EventObject;
/**
* For String fields typing in keybaord when field has focus causes current value to be removed and editing to start
* immediatley
*
*/
public class TextFieldCellEditor extends DefaultCellEditor
{
private EventObject event;
public TextFieldCellEditor()
{
super(new JTextField());
}
public final Component getTableCellEditorComponent(final JTable table, final Object val,
final boolean isSelected,
final int row, final int column)
{
//If entered field using keyboard clear the current value
final JTextField editField = (JTextField) super.getTableCellEditorComponent(table,
val,
isSelected,
row,
column);
if (event instanceof KeyEvent)
{
final Caret caret = editField.getCaret();
caret.setDot(0);
editField.setText("");
}
return editField;
}
public boolean isCellEditable(EventObject anEvent)
{
event = anEvent;
return super.isCellEditable(anEvent);
}
}
答案 0 :(得分:0)
问题是由于使用了来自https://tips4java.wordpress.com/2008/11/10/table-column-adjuster/的TableColumnAdjuster代码,该代码通常用作
TableColumnAdjuster tca = new TableColumnAdjuster(table);
tca.adjustColumns();
我无法解决TableColumnAdjuster的实际问题,但最后我从中提取了一些代码并添加了一个JTable的抽象子类,然后我为我的用例扩展。我需要它来设置合理的起始列宽度,我还调整为设置 preferredSize 而不是 size 除了某些特定的列。
这解决了这个问题,我现在可以使用鼠标可靠地禁用/启用复选框。
public class CorrectlySizedTable extends JTable
{
public void adjustColumns()
{
TableColumnModel tcm = getColumnModel();
for (int i = 0; i < tcm.getColumnCount(); i++)
{
adjustColumn(i);
}
}
public void adjustColumn(final int column)
{
TableColumn tableColumn = getColumnModel().getColumn(column);
if (! tableColumn.getResizable()) return;
int columnHeaderWidth = getColumnHeaderWidth( column );
int columnDataWidth = getColumnDataWidth( column );
int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);
updateTableColumn(column, preferredWidth);
}
/*
* Calculated the width based on the column name
*/
private int getColumnHeaderWidth(int column)
{
TableColumn tableColumn = getColumnModel().getColumn(column);
Object value = tableColumn.getHeaderValue();
TableCellRenderer renderer = tableColumn.getHeaderRenderer();
if (renderer == null)
{
renderer = getTableHeader().getDefaultRenderer();
}
Component c = renderer.getTableCellRendererComponent(this, value, false, false, -1, column);
return c.getPreferredSize().width;
}
/*
* Calculate the width based on the widest cell renderer for the
* given column.
*/
private int getColumnDataWidth(int column)
{
int preferredWidth = 0;
int maxWidth = getColumnModel().getColumn(column).getMaxWidth();
for (int row = 0; row < getRowCount(); row++)
{
preferredWidth = Math.max(preferredWidth, getCellDataWidth(row, column));
// We've exceeded the maximum width, no need to check other rows
if (preferredWidth >= maxWidth)
break;
}
return preferredWidth;
}
/*
* Get the preferred width for the specified cell
*/
private int getCellDataWidth(int row, int column)
{
// Inovke the renderer for the cell to calculate the preferred width
TableCellRenderer cellRenderer = getCellRenderer(row, column);
Component c = prepareRenderer(cellRenderer, row, column);
int width = c.getPreferredSize().width + getIntercellSpacing().width;
return width;
}
private void updateTableColumn(int column, int width)
{
final TableColumn tableColumn = getColumnModel().getColumn(column);
MainWindow.logger.severe("ColumnWidth:"+width);
tableColumn.setPreferredWidth(width);
if(column==0 || getColumnClass(column)==Boolean.class)
{
tableColumn.setMaxWidth(width);
}
}
public void setModel(TableModel dataModel) {
super.setModel(dataModel);
adjustColumns();
}
}