In my application I have use a java table which is similar to this example. My problem is when I change a value of a cell (even in above example) the data model doesn't get updated until I click on a different cell. Even I click on the gray area below the table after changing the cell value the model won't change. I think the reason is cell stay on focused until I click on a different cell. How can I avoid this and update the model without clicking on the table cell. Thanks in advance
I have edit the above sample code to reflect the problem
public class JTableDemo extends JApplet {
private JTextArea txt = new JTextArea(4, 20);
// The TableModel controls all the data:
class DataModel extends AbstractTableModel {
Object[][] data = { { "one", "two", "three", "four" },
{ "five", "six", "seven", "eight" },
{ "nine", "ten", "eleven", "twelve" }, };
// Prints data when table changes:
class TML implements TableModelListener {
public void tableChanged(TableModelEvent e) {
txt.setText(""); // Clear it
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[0].length; j++)
txt.append(data[i][j] + " ");
txt.append("\n");
}
}
}
public DataModel() {
addTableModelListener(new TML());
fireTableDataChanged();
}
public int getColumnCount() {
return data[0].length;
}
public int getRowCount() {
return data.length;
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public void setValueAt(Object val, int row, int col) {
data[row][col] = val;
System.out.println(val);
// Indicate the change has happened:
fireTableDataChanged();
}
public boolean isCellEditable(int row, int col) {
return true;
}
}
public void init() {
Container cp = getContentPane();
JTable table = new JTable(new DataModel());
cp.add(new JScrollPane(table));
cp.add(BorderLayout.SOUTH, txt);
}
public static void main(String[] args) {
run(new JTableDemo(), 350, 200);
}
public static void run(JApplet applet, int width, int height) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(applet);
frame.setSize(width, height);
applet.init();
applet.start();
frame.setVisible(true);
}
}
When you run the example you can see the table content in the text area below the table. After you update a cell it should appear in the text box below. But 'setValueAt' method wont call until you click on a different cell.
That's expected behaviour: the edited value isn't committed to the backing model until an explicit user gesture, like f.i. pressing enter or tabbing out or clicking elsewhere in the table ...
One oddity (some call it bug :-) of JTable is that editing isn't by default terminated when transfering focus to the "outside" of the table. To force it doing so, you need to configure it like:
table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
BTW (unrelated, just for sanity): always fire the most fine-grained event type, here that would be a cellUpdated instead of the dataChanged hammer.
The default update mechanism only changes the model when the cell editor loses the focus. Either tabbing out of the cell or clicking in a different cell will cause the vital "focus lost" event which triggers the model change.
Clicking in the gray area has no effect because there are no active elements there which could process the event - the table ignores the click.
If you want to change this, you need to find an event which tells the computer that the user is "done with editing". How do you know that?
You could add an ActionListener (see http://download.oracle.com/javase/tutorial/uiswing/components/textfield.html). It will get triggered when you press RETURN. In the handler, call fireEditingStopped() to trigger the "copy to model" code (see http://download.oracle.com/javase/tutorial/uiswing/components/table.html#editor).
You could add a "Save" button below the table and call this code in it's ActionListner:
if(null != jTable.getCellEditor()) {
// there is an edit in progress
jTable.getCellEditor().stopCellEditing()
}
You could update the model for each keypress by adding a keypress handler but that might cause the editor to disappear.
[EDIT] Note that adding a "Save" button can disrupt the edit "flow" of users (click table cell, edit, grab mouse, aim, click save, click next cell, go back to keyboard, edit, ...)
For my case
public void setValueAt(Object val, int row, int col) {
data[row][col] = val;
System.out.println(val);
// Indicate the change has happened:
fireTableDataChanged();
}
you need to call super function also.
public void setValueAt(Object val, int row, int col) {
data[row][col] = val;
System.out.println(val);
// Indicate the change has happened:
fireTableDataChanged();
super.setValueAt(val,row,col);
}
Related
I am displaying data in JTable through mouse event in JList. I want to know how I would change the font color of specific data after the user click in JLIst, and Here is Photo for the desired result
list.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
int solutionindex =list.getSelectedIndex();
if(solutionindex==0) {
for (int i=0;i<table.getRowCount();i++) {
for (int j=0;j<table.getColumnCount();j++) {
table.setValueAt(b.charAt(i, j), i,j);
}// end of second for loop
}// end of for loop
}
else {
for (int i=0;i<table.getRowCount();i++) {
for (int j=0;j<table.getColumnCount();j++) {
table.setValueAt(solutions.get(solutionindex1).getBoard().charAt(i, j), i,j);
}// end of second for loop
}// end of for loop
}
table.setDefaultRenderer(String.class, new DefaultTableCellRenderer(){
#Override
public java.awt.Component getTableCellRendererComponent(JTable table,Object value,boolean isSelected,boolean hasFocus,int row,int column) {
java.awt.Component c = super.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,column);
c.setForeground(Color.red);
return c;
}
});
Use a ListSelectionModel instead of a MouseListener on the JList. (The JList will somewhere along the line be using the MouseListener to change state. Does it happen before or after you listener is fired? Depends. May change on implementation and even at runtime (which is really confusing).)
Switch the object in the table [model] from Character to be of a new type containing both the text and the foreground colour. In the table cell renderer, cast the cell object to the correct type, and use its colour in setForeground.
I am New in java, I have a JTable that can read records from a txt file and show they perfectly.
I want to add a new book to my JFrame that when user select a row on table and clicked the "delete" button, that row should delete and that deleted row records must delete from txt file,too.
my code is this, but it has errors and not seen JTable! :
public class CopyOfAllUserTable extends AbstractTableModel {
Vector data;
Vector column;
public static void main(String[] args){
new CopyOfAllUserTable();
}
public CopyOfAllUserTable() {
String line;
data = new Vector();
column = new Vector();
try {
FileInputStream fis = new FileInputStream("D:\\AllUserRecords.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
StringTokenizer st1 = new StringTokenizer(br.readLine(), " ");
while (st1.hasMoreTokens())
column.addElement(st1.nextToken());
while ((line = br.readLine()) != null) {
StringTokenizer st2 = new StringTokenizer(line, " ");
while (st2.hasMoreTokens())
data.addElement(st2.nextToken());
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
final JFrame frame1=new JFrame();
JTable table=new JTable(data,column);
JButton button1=new JButton("Delete");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DefaultTableModel model=new DefaultTableModel(data, column);
JTable table=new JTable(model);
}
});
JPanel panel=new JPanel();
panel.add(table);
panel.add(button1);
frame1.add(panel);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setBounds(200, 80, 600, 500);
frame1.setVisible(true);
frame1.setEnabled(true);
}
public int getRowCount() {
return data.size() / getColumnCount();
}
public int getColumnCount() {
return column.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (String) data.elementAt((rowIndex * getColumnCount())
+ columnIndex);
}
}
My problem is in delete row, and read records from file to jtable are perfectly successful.
Firstly you're not adding your JTable to the content of the frame.
For containers like: frame.getContentPane() and JPanel you should add the child components by using their #add(...) method.
For example:
final JPanel panel=new JPanel(new BorderLayout());
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DefaultTableModel model=new DefaultTableModel(data, column);
JTable table=new JTable(model);
panel.add(new JScrollPane(table));
panel.revalidate();
}
});
Note that JPanel default layout is FlowLayout. Second thing - if you want to have headers and scrolling in your JTable you need to wrap it with JScrollPane.
Next - you should revalidate the panel after adding/removing/etc.
The second issue is removing rows from JTable. I usually write a method to handle it:
protected void removeRows(final int[] rows) {
int modelRows[] = new int[rows.length];
for(int i = 0; i < rows.length; ++i) {
modelRows[i] = table.convertRowIndexToModel(rows[i]);
}
Arrays.sort(modelRows);
for(int i = modelRows.length - 1; i >= 0; --i) {
int row = modelRows[i];
model.removeRow(row);
}
model.fireTableDataChanged();
}
The convertRowIndexToModel method converts index returned by JTable#getSelectedRows() or JTable#getSelectedRow() (which are the visible indices) to the model indices. If you set RowSorter for your JTable or you leave it to standard implementation:
table.setAutoCreateRowSorter(true);
You are adding table directly to the panel with out using the JScrollPane. Your table header will not be visible if you do like this,
So instead of this,
JPanel panel=new JPanel();
panel.add(table);
Do this,
JPanel panel=new JPanel();
panel.add(new JScrollPane(table));
Why to use JScrollPane? Read this.
When user selects a row and clicks on delete, then get the selected row and remove it from the table list. As you are using AbstractTableModel then you have to write your custom removeRow(..) method to perform this.
Example:
private boolean removeSelectedRow(int row) {
// Remove the row from the list that the table is using.
dataList.remove(row);
// You need to call fireXXX method to refresh the table model.
fireTableDataChanged();
return true;
// If fail return false;
}
If delete is the action then first get the selected row and then call removeSelectedRow(int) like the following,
private void deleteRow() {
int selectedRow = table.getSelectedRow();
boolean deleteStatus = removeSelectedRow(selectedRow);
// Only if the deletion is success then delete from the file.
if(deleteStatus) {
// Delete it from the file too.
}
}
first you have to make sure that something has been selected: when there is something selected than enable the delete button. please look up the JTable java source code #
http://developer.classpath.org/doc/javax/swing/JTable-source.html
and the following code:
1331: /**
1332: * Receives notification when the row selection changes and fires
1333: * appropriate property change events.
1334: *
1335: * #param event the list selection event
1336: */
1337: public void valueChanged(ListSelectionEvent event)
1338: {
1339: firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
1340: Boolean.FALSE, Boolean.TRUE);
1341: int r = getSelectedRow();
1342: int c = getSelectedColumn();
1343: if (r != lastSelectedRow || c != lastSelectedColumn)
1344: {
1345: Accessible o = getAccessibleAt(lastSelectedRow,lastSelectedColumn);
1347: Accessible n = getAccessibleAt(r, c);
1348: firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, o, n);
1350: lastSelectedRow = r;
1351: lastSelectedColumn = c;
1352: }
1353: }
You need to register for the last event to be notified when the selected rows have been changed. Add your own listener to enable the deletebutton based on whether or not a row has been selected which is as you can see in the event itself.
Please use to start with the DefaultTableModel because it will work in 90% of the cases.
And any change is applied to the tabledatamodel which will automatically propogate to the JTable View: normally you never change the view because all selection and scroll information is lost which is something you don't want.
When the delete button is fired the approach is straight forward: there is a row selected, otherwise it is impossible to click it: remove that selected row number from the defaultTableModel, and last but not least I would write simply the entire contents of the datamodel model to the designated file because the table's model hold the actual rows that are indeed displayed in the View.
So please think in terms of models models and models: Views are instantiated only once, packed scrolled etc and than you leave them as is. Models are normally also never changed: you change the contents of the models by adding and or deleting rows. One other tip: use always renderers: those that don't don't, in my humble opinion, don't understand how to work with JTables.
And yes you can leave out the first part to listen for selection changes: sure and pop up a warning to indicate the problem. And in a later stage add the functionality that listens for selection changes to enable and or disable the JButton delete row.
Hope this helps.
I create a jTable, and TableCellRenderer, TableCellEditor on it. I need to put there editable (with text/html context type) JEditorPane. When i write some text inside and resize component, text disappear. What I doing wrong? Furthermore above this component I have got buttons with text edition: for example:
JButton bold = new JButton():
bold.setAction(new StyledEditorKit.BoldAction());
It is part of my custom model:
private JEditorPane editorTxtPane = new JEditorPane("text/html", "");
private JEditorPane rendererTxtPane = new JEditorPane("text/html", "");
private final JPanel editorPanel = new JPanel();
private final JPanel rendererPanel = new JPanel();
private final ArrayList<FocusListener> editorFocusListeners = new ArrayList<FocusListener>();
public SampleModel() {
super();
rendererTxtPane.setContentType("text/html");
editorTxtPane.setContentType("text/html");
rendererPanel.add(initCellControls(rendererPanel, rendererLabel));
rendererPanel.add(rendererTxtPane);
editorPanel.add(initCellControls(editorPanel, editorLabel));
JScrollPane sp = new JScrollPane(editorTxtPane);
sp.setBorder(null);
editorPanel.add(sp);
editorTxtPane.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
super.focusGained(e);
e.setSource(editorTxtPane);
for (int i = editorFocusListeners.size() - 1; i >= 0; i--) {
editorFocusListeners.get(i).focusGained(e);
}
}
#Override
public void focusLost(FocusEvent e) {
super.focusLost(e);
e.setSource(editorTxtPane);
for (int i = editorFocusListeners.size() - 1; i >= 0; i--) {
editorFocusListeners.get(i).focusLost(e);
}
}
});
}
It is my editor and renderer methods:
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Comment c = data.get(row);
rendererTxtPane.setText(c.getComment());
return rendererPanel;
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
Comment c = data.get(row);
c.setNeedSave(true);
editorTxtPane.setText(c.getComment());
return editorPanel;
}
This is not how Editors and Renderers work. In particular, the editor is only valid while the cell is being edited. Your TableModel should store each row's Document. After editing concludes, your model will be updated with the revised Document, as described here. You might compare what your doing with the example, which could form the basis of your sscce.
This may be the result of one of the two conditions below:
The text component that you are embedding in your resisable
component is being shifted out through the process of resising
mechanism, so when you are resising it, the inner text component is
disappearing
Whenever the resising process is happening, the standard Swing
repaint process is not being called by he platform, in the right moment, so you can
call repaint manually through coding. The SWING platfrom usually
calls the repaint method automatically whenever it notices a change in
the overall GUI, but it's schedulled to be run after some other
processes to complete, in this case calling repaint manually is
inevitble
I have a JTable displaying rows from an SQL database. The table is relatively small (only 4 columns and up to 1000 rows).
I would like to give the user the opportunity to edit any cells in the table but want to avoid restricting it so much so that they must use an edit dialog box (this makes for far easier error checking and validation but is less intuitive)
I have tried a few different ways of controlling edit selections using the valueChanged method of my JTable but haven't had much luck.
I would like each row to be edited and written to the database at the conclusion of editing. I would like that once a cell has been clicked to start the editing of that row, no other rows can be selected until the user has finished editing the row (other rows are grayed out). After editing each cell and pressing enter, the edit selection should jump to the next column in the same row.
Can anyone give pointers on how I can achieve this?
// Create table with database data
table = new JTable(new DefaultTableModel(data, columnNames)) {
public Class getColumnClass(int column) {
for (int row = 0; row < getRowCount(); row++) {
Object o = getValueAt(row, column);
if (o != null){
return o.getClass();
}
}
return Object.class;
}
#Override
public boolean isCellEditable(int row, int col){
return true;
}
#Override
public boolean editCellAt(int row, int column) {
boolean ans = super.editCellAt(row, column);
if (ans) {
Component editor = table.getEditorComponent();
editor.requestFocusInWindow();
}
return ans;
}
#Override
public void valueChanged(ListSelectionEvent source) {
super.valueChanged(source);
if (table!=null)
table.changeSelection(getSelectedRow(), getSelectedColumn()+1, false, false);
}
};
Edit - custom cell editor with table pointer seems to be a start
public class ExchangeTableCellEditor extends AbstractCellEditor implements TableCellEditor {
private JTable table;
JComponent component = new JTextField();
public ExchangeTableCellEditor(JTable table) {
this.table = table;
}
public boolean stopCellEditing() {
boolean ans = super.stopCellEditing();
//now we want to increment the cell count
table.editCellAt(table.getSelectedRow(), table.getSelectedColumn()+1);
return ans;
}
#Override
public void cancelCellEditing() {
//do nothing... must accept cell changes
}
#Override
public Object getCellEditorValue() {
return ((JTextField)component).getText();
}
#Override
public Component getTableCellEditorComponent(JTable arg0, Object value,
boolean arg2, int arg3, int arg4) {
((JTextField)component).setText((String)value);
return component;
}
}
The default renderer and editor is typically adequate for most data types, but you can define custom renderers and editors as needed.
Addendum: I'm unfamiliar with the approach shown in your fragment. Instead, register a TableModelListener with your model, as shown below, and update the database with whatever granularity is warranted. See also How to Use Tables: Listening for Data Changes.
Addendum: #kleopatra is correct about your TableCellEditor. One convenient way to notify listeners is to invoke the super implementation, as shown here. Note that the delegate invokes fireEditingStopped().
/** #see https://stackoverflow.com/questions/9155596 */
public class NewJavaGUI extends JPanel {
private final JTable table;
public NewJavaGUI() {
String[] colNames = {"C1", "C2", "C3"};
DefaultTableModel model = new DefaultTableModel(colNames, 0) {
#Override
public boolean isCellEditable(int row, int col) {
// return your actual criteria
return true;
}
#Override
public Class getColumnClass(int col) {
// return your actual type tokens
return getValueAt(0, col).getClass();
}
};
// Add data; note auto-boxing
model.addRow(new Object[]{"A1", "A2", 42});
model.addRow(new Object[]{"B1", "B2", 42d});
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
// DML as indicated
}
});
table = new JTable(model);
this.add(table);
}
private void display() {
JFrame f = new JFrame("NewJavaGUI");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new NewJavaGUI().display();
}
});
}
}
The behaviour you mention can be achieved by forcing your table to start editing again.
First make sure you now yourRow and Column and that you add your own tablecelleditor that extands from the AbstractCellEditor
then add this to your stopCellEditing method:
EventQueue.invokeLater(new Runnable()
{
public void run()
{
yourTable.editCellAt( yourRow, yourColumn+1);
}
});
I have a JTable Component. In one column I insert HTML code and if a line in this column is longer than column width, the text is wrapped. Is it possible to switch off wrap?
#sandlex
public Object getValueAt(int row, int col) {
return new String(
"<html>dgdfsgsdfg dfgdsfg sdfgs dfgsdfgsdfgsdfgsd afsdf asdfasd</html>");
}
Check your code with this function. It will display text in few lines - not in one.
Is it possible to take a look at your code?
For example in this simplest example nothing is wrapped:
public class TableTest {
public static void main(String[] args) {
JFrame frm = new JFrame();
TableModel dataModel = new AbstractTableModel() {
public int getColumnCount() {
return 10;
}
public int getRowCount() {
return 10;
}
public Object getValueAt(int row, int col) {
return new String(
"row*col*1000000000");
}
};
JTable table = new JTable(dataModel);
table.getColumnModel().getColumn(2).setMaxWidth(120);
table.getColumnModel().getColumn(2).setPreferredWidth(120);
table.getColumnModel().getColumn(2).setMinWidth(120);
frm.getContentPane().add(table);
frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frm.setPreferredSize(new Dimension(300, 300));
frm.pack();
frm.setVisible(true);
}
}
Edited #latata
You can try to create your own CellRenderer and play around with it applying to needed column:
table.getColumnModel().getColumn(0).setCellRenderer(new NonWrappedCellRenderer());
Renderer may look like this:
class NonWrappedCellRenderer extends JTextArea implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(
JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
this.setText((String)value);
this.setLineWrap(false);
return this;
}
}
Here line won't be wrapped cuz JTextArea by default use
setLineWrap(false)
The problem is JTextArea cannot display html tags properly as I can see. So you will solve this somehow. You can try JTextPane or something else that can deal with tags and check if this has wrapping property.
Alternatively you can calculate font metrics, cell width and trim text by hands.