I have to implement a JTable in which exactly one row has to be selected (always). Empty selection is not allowed. I'm selecting the first row during initialization:
table.setRowSelectionInterval(0, 0);
additionally, I'm using
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
But user can still deselect one row using CLick + Ctrl.
What is the easiest way ensure, that one (exaclty) row is always selected in the table ?
If you have a JTable instance created, just use:
jTable1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Now, you could add MouseListeners, SelectionListeners, KeyListeners and key bindings to try and solve this is issue. Or, you could go to the heart of the problem.
The ListSelectionModel is responsible for managing the selection details.
You could simply supply your own ListSelectionModel for the row selection
public class ForcedListSelectionModel extends DefaultListSelectionModel {
public ForcedListSelectionModel () {
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
#Override
public void clearSelection() {
}
#Override
public void removeSelectionInterval(int index0, int index1) {
}
}
And simply set it to your table...
table.setSelectionModel(new ForcedListSelectionModel());
What is the easiest way ensure, that one (exaclty) row is always
selected in the table ?
there are three (basically) selection types
JTable.setRowSelectionAllowed(boolean);
JTable.setColumnSelectionAllowed(boolean);
JTable.setCellSelectionAllowed(boolean);
edit
works for me too
int row = table.getSelectedRow();
if ((row > -1)) {
table.setRowSelectionInterval(row, row);
}
}
I would use the JTable#setRowSelectionAllowed as it will ensure that a row can be selected.
This code changes it right back to the desired index and it gives the appearance to the user that it never updates.
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
table.setRowSelectionInterval(0,0);
}
);
First do
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setRowSelectionInterval(0, 0);
then set ListSelectionModel
Related
Ok so I need to make a listener that by clicking on a row it will retrieve me the data from one of the columns, the ID of the element on this row, but I have no clue on how to retrieve that. So far I made a listselectionlistener but it only gets the number of the row selected, not the data in the ID column. This is the listener method. Anyone can help me with that?
//This is the listener
ListSelectionModel model = NPC_Table.getSelectionModel();
model.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if(!model.isSelectionEmpty()) {
int selectedRow = model.getMinSelectionIndex();
}
}
});
Use the parameter to method valueChanged. It contains details of the actual event, i.e. the action performed by the user in order to select a row in the JTable.
int idColumn = 2; // Just a guess. I assume you know the correct column index.
ListSelectionModel model = NPC_Table.getSelectionModel();
model.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
int row = e.getFirstIndex();
Object requestedValue = NPC_Table.getValueAt(row, idColumn);
}
}
});
Refer to How to Write a List Selection Listener.
Also consider adopting java naming conventions. Use npcTable instead of NPC_Table. Using conventions makes it easier for others to read and understand your code. After all, you are asking for help with your code, so you should make the effort to make your code as clear as possible for others to read.
I've investigated lots of different questions and answers around this, but can't find one that seems to work.
I'm new to Java, but have experience in a variety of different languages and, so far (in context to what I'm experimenting with), it's feeling a bit like VBA except with having to build up the actions/functions that you feel should be already there. This is, I expect, just down to my own inexperience though.
I'm using Netbeans IDE 8.2 with Java Runtime 1.80.
I have created jFrame that contains a jTable. The jTable is built with data like so:
public void showTasks(Boolean removeID) {
ArrayList<Tasks> list = tasksList("SELECT * FROM tasks");
JTable table = jTable1;
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
model.addRow(row);
}
// Remove the 'id' column from the table view
if(removeID) { table.removeColumn(table.getColumnModel().getColumn(0)); }
}
The background behind this is less relevant, but essentially tasksList is a function that applies the query to an SQL statement, returning an ArrayList. I build up my rows with 4 columns, then remove the first column so 'ID' is available but not visible (this final action has been segregated through testing/experimentation).
I have another area of code that opens a jDialog when a row is clicked, in which it is possible to update the MySQL DB.
Problem
I'm trying to throw in a function call so that the table data 'refreshes' when the jDialog is closed. I have temporarily added in a button to the jFrame (where the jTable lives) to test/debug this function.
I can't seem to get this to work, though. The closest I have achieved is to re-call showTasks(false), but this obvious just adds rows with updated data, rather than replacing the dataset. I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
As I'm new to Java, and may still be looking at it from a flawed method of thinking, I'm finding it difficult to apply any other examples to that of my own. I also can't seem to find a way to implement fireTableDataChanged().
Surely this is a simple concept I'm over-thinking?
Edit - based on below answer
Is there a reason why something like this would be considered incorrect, if deleting all rows and adding them back in is okay?
public void refreshTasks() {
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
int row_total = model.getRowCount();
for(int i= row_total -1;i>=0;i--) {
model.removeRow(i);
}
showTasks(false);
}
Edit: Button to invoke data update
Now works correctly (if not improperly) with the following:
private DefaultTableModel parentTable; // To store the parent 'Task' table model
public void setStart(int user,DefaultTableModel table) {
this.txt_taskID.setText(Integer.toString(user)); // Converts the ID to a string
addData(user); // Populates the fields
parentTable = table; // Sets parent TableModel to a variable
}
The above code is called from the Parent jFrame when the Dialog is opened, and passes the Table model and the 'ID' of the row I'm looking to edit. The table model is stored in parentTable.
There's also a 'Save' button, and a 'Cancel' button. I'm yet to separate these, and currently 'Save' does just that (SQL update and so on). My 'Cancel' button closes the dialog and refreshes the jTable, as per the below function:
private void btn_CancelActionPerformed(java.awt.event.ActionEvent evt) {
this.setVisible(false); // Hide the dialog
Menu menu = new Menu(); // for accessing the tasksList function
parentTable.setRowCount(0); // Clears the jTable data
// jTable data is then 'rebuilt' using the new data
ArrayList<Tasks> list = menu.tasksList("SELECT * FROM tasks");
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
parentTable.addRow(row);
}
}
I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
Yes that is probably the best practice.
The only other approach is to create a completely new TableModel and add it to the table using the setModel() method. The problem with this approach is that it will reset any custom renderers/editors you may have set on the table.
The easiest way to remove all the rows from the DefaultTableModel is to just use:
model.setRowCount(0);
I'm not sure how you want to do it but I'm gonna give you simple example for deleting and refreshing JTable maybe it's help you.
This following btnDelete Jbutton added to JFrame for deleting rows from table:
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowIndex = table.getSelectedRow();
if(rowIndex > -1) {
int x = (Integer) table.getModel().getValueAt(rowIndex, 0);
if (conn.removeContact(x) == true) { //here add your own code like removeID
model.removeRow(rowIndex);
lblInfo.setText("Contact deleted successfully.");
model.fireTableDataChanged();
} else {
lblInfo.setText("Cannot remove at this time!");
}
}else {
lblInfo.setText("At first you need select a row with single click!");
return;
}
}
});
and these following codes for refreshing table in primitive way :
btnRefresh.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowCount = model.getRowCount();
for (int i = rowCount - 1; i >= 0; i--) {//remove all rows
model.removeRow(i);
}
lblInfo.setText("Table datas updated successfully.");
for (Person p : conn.readAllContacts()) {//add all row from scratch
model.addRow(new Object[] { p.getId(), p.getName(), p.getLastName(), p.getPhone(), p.getEmail() });
}
}
});
My question is that how to get first row value of jTable and display it in a textfield, but when ever the first row value changes, the value in the text field should change.
First off - your table has a TableModel.
You can access by calling
TableModel tm = table.getModel();
That TableModel has a method getValueAt(int row, int column) - use this to collect data from your first row (index 0).
The TableModel further allows for a TableModelListener to be added. That TableModelListener in turn receives TableModelEvents.
Use the event data to figure out if the first row was affected by your change and then apply the changed data to your textField:
public void tableModelChanged(TableModelEvent te) {
if(te.getFirstRow() == 0) { //First Row changed
//Receive Data and update TextField Here
}
}
Without knowing your specific case, I think this sounds like an application for using a TableCellListener, which will keep track of changes in your cell. Your jTable will fire a PropertyChangeEvent which is used by the listener. You might take a look at here and the code provided there to get the idea. Hope this helps in any way.
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
node n=new node(Integer.parseInt(push.getText()));
q.push(n);
model=(DefaultTableModel) jTable1.getModel();
model.addRow(new Object[]{n.getele()});
push.setText(null);
}
Here is the code for my jtable, how do I get to the value of first row to the text field?
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
node n=new node(Integer.parseInt(push.getText()));
q.push(n);
model=(DefaultTableModel) jTable1.getModel();
model.addRow(new Object[]{n.getele()});
push.setText(null);
}
I want to prevent column from being resized larger than 10px. I want it to be small because it contains only checkbox.
I instantiate JTable here:
private void initDataSearchResultTable()
{
dataSearchResultTable = new JTable();
dataSearchResultTable.setSelectionBackground(new Color(0xaaaaff));
dataSearchResultTable.setFillsViewportHeight(true);
dataSearchResultTable.setRowSelectionAllowed(true);
dataSearchResultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
dataSearchResultTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
I set Table model for table(when user clicks button) here:
void updateDataSearchTableModel(CachedRowSet cachedRowSet)
{
TableModel tableModel = dataSearchResultTable.getModel();
if (tableModel instanceof DataSearchTableModel) {
((DataSearchTableModel) tableModel).updateModel(cachedRowSet);
} else {
tableModel = new DataSearchTableModel(cachedRowSet);
dataSearchResultTable.setModel(tableModel);
this.setCellRendererForDataSearchTable();
((DataSearchTableModel) tableModel).structureChanged();
}
}
In method structureChanged() I call method fireTableStructureChanged(). This ensures that boolean columns are rendered as check boxes.
Next I try to set width of 0 and 1 columns and make them resizable. It doesn't work:
private void setCellRendererForDataSearchTable()
{
DataSearchResultTableCellRenderer cellRenderer = new DataSearchResultTableCellRenderer();
TableColumnModel columnModel = dataSearchResultTable.getColumnModel();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
TableColumn column = columnModel.getColumn(i);
if (i == 0) {
column.setMaxWidth(15);
column.setResizable(false);
} else if (i == 1) {
column.setMaxWidth(30);
column.setResizable(false);
}
column.setCellRenderer(cellRenderer);
}
}
My renderer sets only border for selected cells.
My table model is more interesting:
public DataSearchTableModel(CachedRowSet cachedRowSet)
{
this.cachedRowSet = cachedRowSet;
updateModel(this.cachedRowSet);
}
public void updateModel(CachedRowSet cachedRowSet)
{
try {
metaData = this.cachedRowSet.getMetaData();
this.numOfCols = metaData.getColumnCount();
// Retrieve the number of rows.
this.cachedRowSet.beforeFirst();
this.numOfRows = 0;
while (this.cachedRowSet.next()) {
this.numOfRows++;
}
this.cachedRowSet.beforeFirst();
} catch (SQLException e) {
e.printStackTrace();
}
}
But this doesn't work, those columns are still resized and with wrong width?
Thanks!
In method structureChanged() I call method
fireTableStructureChanged(). This ensures that boolean columns are
rendered as check boxes.
(and...............)
But this doesn't work, those columns are still resized and with wrong
width?
with JTable.setModel(model) call or upon receiving the event fired by fireTableStructureChanged function call, if autoCreateColumnsFromModel is set(true) JTable will discards any table columns that it had prior to these function call and reallocates default columns in the order they appear in the model. Which means all the options related to column(width, renderer, etc) you set to the column previously will be lost. Setting autoCreateColumnsFromModel flag to false should be sufficient. To set or unset this flag use:
JTable.setAutoCreateColumnsFromModel(boolean)
Or, you may need to reset all the options to column after these function call.
Reference:
public void fireTableStructureChanged()
You can use:
public void resizeColumn(JTable tbl, int column) {
tbl.getColumnModel().getColumn(column).setPrefferedSize(new Dimension(/*width*/, /*height*/));
}
I found solution.
You should never call fireTableStructureChanged after you customize your TableColumns.
But to update table after its model and renderer was set you need fireTableStructureChanged.
So at first set cell renderers then call fireTableStructureChanged to make boolean columns displaed with checkboxes and only then change your TableColumns.
It will work.
I am developing a JTable with different rows. I would like to associate an event to the selection of a row in this table. I have used the following selection class to provide behaviour to the table selection:
public class TableSelectionListener implements ListSelectionListener{
public Integer item;
public TableSelectionListener(Integer item){
this.dialog = item;
}
public void valueChanged(ListSelectionEvent e) {
System.out.println("The row clicked is "+item);
}
}
When I create an instance of this table, sai tabletest, I have added the following piece of code:
tabletest.getSelectionModel().addListSelectionListener(new TableSelectionListener(tabletest.getSelectedRow());
The problem is that when I click on one row once, instead of retrieving the related message once, I retrieve the same message several times, suggesting that the actions repeated several times. For example:
The row clicked is 0
The row clicked is 0
The row clicked is 0
The row clicked is 0
Does anyone know where the problem may be?
Well, that's just normal.
Your selection listener is created with the value of tabletest.getSelectedRow() at its creation table (which is zero). And, as you never change the value of item in your listener, this listener fcan only display 0as a result.
If I were you, I wouold replace the valueChanged() method by something like (although it's untested and I remember strange things happens sometimes when mixing view and model row values) this :
public void valueChanged(ListSelectionEvent e) {
if(!e.getValueIsAdjusting()) // added as sometimes, multiple events are fired while selection changes
System.out.println("The row clicked is "+e.getFirstIndex());
}
Firstly, it's perfectly normal to get multiple ListSelectionEvents, while the selection is being changed. You can use the getValueIsAdjusting method to determine when selection has ended (it will return false).
Secondly, there's no need to construct your TableSelectionListener with a row number. When your valueChanged method is called, you can get the index of the first/last selected row (remember it's possibly to select multiple rows in the table, unless you disable that) using e.getFirstIndex() and e.getLastIndex() respectively.
An easier way of doing it, is as follows:
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
System.out.println("e...."+table.getSelectedRow());
}
});