Last two days I've been trying to create a data view table in java app with JTable.
Netbeans binding option doesn't work, but nevermind, I managed to create my own TableModel.
Data get shown in the table, but the headers always contain just letters (A, B, C... for each column). At one point everything worked well, but then I wanted to set another model for that particular table and it doesn't show the labels correctly anymore even if I create whole new JPanel and set it up from scratch.
This is my custom TableModel class
public class MyTableModel extends AbstractTableModel{
private ArrayList<Options> list;
String[] headers;
public MyTableModel(ArrayList<Options> list, String[] headers) {
this.list = list;
this.headers = headers;
}
#Override
public int getRowCount() {
return this.list.size();
}
#Override
public int getColumnCount() {
return headers.length;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if(columnIndex == 0) {
return list.get(rowIndex).getId();
}
if(columnIndex == 1) {
return list.get(rowIndex).getText();
}
else {
return null;
}
} }
And this is part of the code in jframe class(the one which I run)
ArrayList<Options> list;
String[] optionHeaders = {"id", "text"};
public table2frame() {
initComponents();
list = (ArrayList) zadanie_2_app.Zadanie_2_app.findAll();
JTable table2 = new JTable(new MyTableModel(list, optionHeaders));}
AbstractTableModel requires that getColumnName be overridden otherwise placeholder column names are used. Add
#Override
public String getColumnName(int column) {
return headers[column];
}
I wanted to set another model for that particular table and it doesn't show the labels correctly anymore even if I create whole new JPanel and set it up from scratch.
AFAIK understand from this desription, that JTable is container for JTable
put JTable to the JScrollPane, JTable should be placed into JScrollPane, otherwise JTableHeader isn't visible automatically
get JTableHeader from JTable, change LayoutManager for JPanel to BorderLayout, put JTable to CENTER area, JTableHeader to NOTHR area
Related
I want to create a JTable having the last column with advanced options icon. On clicking this last column in the JTable, I want a new JPanel to pop up allowing user to enter input for required 4 string input fields. This JPanel when dismissed, should return to the original JTable.
I am not sure where to save the data for 4 fields from the new JPanel. As their would be 4 string input fields per JTable row, just displayed in the JPanel.
Can my JTabel cell hold an object saving the data?
UseCase: I have a JTable with 10 columns. It is getting very cluttered so I want to move 5 columns to a new panel which will be launched on clicking an advanced options icon in the original JTable last column.
Sample code on how to associate the data from the JPanel with the row in JTable will be highly appreciated.
In order to show a pop-up when cell is clicked, you need a cell editor class. The main purpose of this class is to provide custom editors for cells, but you can use it to trigger some action when your cell is clicked:
public class InfoCellEditor extends AbstractCellEditor implements TableCellEditor {
#Override
public java.awt.Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
InfoObject info = (InfoObject) value;
editButton = new JButton(new InfoAction(info));
editButton.setText("INFO");
editButton.setEnabled(true);
}
private class InfoAction extends AbstractAction {
InfoObject info;
public InfoAction(InfoObject info) {
super();
this.info = info;
}
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, info.toString());
stopCellEditing();
}
}
}
Then, extend JTable class and implement getColumnClass and isCellEditable methods:
public class MyTable extends JTable {
public MyTable() {
super();
setDefaultEditor(InfoObject.class, new InfoCellEditor());
}
#Override
public Class getColumnClass(int columnIndex) {
if(columnIndex == 4)
return InfoObject.class;
else
return String.class;
}
#Override
public boolean isCellEditable(int row, int column) {
if(column == 4)
return true;
else
return false;
}
}
Lastly, you should make sure that InfoObject instances are inserted to 5th column. And you can also implement a TableCellRenderer for some custom visual representation of that column.
Object headers = new Object[COLUMN_COUNT];
Object cells[][] = new Object[ROW_COUNT][];
...
cells[0][4] = new InfoObject(data[0]);
cells[1][4] = new InfoObject(data[1]);
table.setModel(new DefaultTableModel(cells, headers));
table.getModel().fireTableDataChanged();
table.setVisible();
I have many different array lists. I want each index to be a new row in the JTable but I'm not sure how to do that. I made a for loop but it is not working.
Is there even a way to populate a JTable with an array list and not an array?
public TableCreator() {
super(new GridLayout(1,0));
String[] columnNames = {"Item Type",
"Description",
"Size",
"Price"};
// for(int i=0; i<ShoppingFunctions.cartType.size(); i++){
for(int i=0; i<GUI.size.size(); i++){//FIX!!!!
item = ShoppingFunctions.cartType.get(i)+"\n";
described = GUI.describe[GUI.imageNum];
sizes = GUI.size.get(i);
price = ShoppingFunctions.cartPrice.get(i)+"\n";
}//end of for
Object[][] data = {{item, described, sizes, price}};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
if (DEBUG){
table.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
printDebugData(table);
}//end of method
});//end of listener
}//end of if
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}//end of method
The simple answer is to create your own, based on something like AbstractTableModel
For example...
public abstract class AbstractGenericTableModel<R> extends AbstractTableModel {
protected ArrayList<R> rows;
protected List columnIdentifiers;
public AbstractGenericTableModel() {
this(0, 0);
}
public AbstractGenericTableModel(int rowCount, int columnCount) {
this(new ArrayList(columnCount), rowCount);
}
public AbstractGenericTableModel(List columnNames, int rowCount) {
setData(new ArrayList<>(rowCount), columnNames);
}
public AbstractGenericTableModel(List<R> data, List columnNames) {
setData(data, columnNames);
}
public List<R> getRowData() {
return rows;
}
private List nonNullVector(List v) {
return (v != null) ? v : new ArrayList();
}
public void setData(List<R> data, List columnIdentifiers) {
this.rows = new ArrayList<>(nonNullVector(data));
this.columnIdentifiers = nonNullVector(columnIdentifiers);
fireTableStructureChanged();
}
public void addRow(R rowData) {
insertRow(getRowCount(), rowData);
}
public void insertRow(int row, R rowData) {
rows.add(row, rowData);
fireTableRowsInserted(row, row);
}
/**
* Removes the row at <code>row</code> from the model. Notification of the row being removed will be sent to all the listeners.
*
* #param row the row index of the row to be removed
* #exception ArrayIndexOutOfBoundsException if the row was invalid
*/
public void removeRow(int row) {
rows.remove(row);
fireTableRowsDeleted(row, row);
}
public void setColumnIdentifiers(List columnIdentifiers) {
setData(rows, columnIdentifiers);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return columnIdentifiers.size();
}
#Override
public String getColumnName(int column) {
Object id = null;
// This test is to cover the case when
// getColumnCount has been subclassed by mistake ...
if (column < columnIdentifiers.size() && (column >= 0)) {
id = columnIdentifiers.get(column);
}
return (id == null) ? super.getColumnName(column)
: id.toString();
}
}
public class ArrayListTableModel extends AbstractGenericTableModel<ArrayList> {
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
List<ArrayList> rows = getRowData();
return rows.get(rowIndex).get(columnIndex);
}
}
This creates two classes, an "abstract generics" based model, which allows you to specify the physical data which backs a row and a ArrayListTableModel which allows you to use a ArrayList for the individual data.
What this assumes though, is each row has the same number of elements, but it makes no checks
I suggest you have a closer look at How to Use Tables for more details
You can use the List Table Model. It will support rows of data stored in an ArrayList, Vector or any other class that implements the List interface.
The ListTableModel is also based on a generic TableModel that allows you to add Objects to a row in the model. There are many row based methods that allow easy usage of the model
The ListTableModel provides methods that allow you to easily customize the model:
setColumnClass – specify the class of individual columns so the proper renderer/editor can be used by the table.
setModelEditable – specify editable property for the entire model.
setColumnEditable – specify editable property at a column level. This property will have preference over the model editable property.
The other option is to iterate through the ArrayList and copy each row of data to a Vector and then add the Vector to the DefaultTableModel using the addRow(...) method.
I'm working on a small project that involves JTable which requires the user to click a button and add a row to the table (I have named the button as addrow). I have used a custom table model (Mytablemodel) which extends Default table model.
My table is first made up of five rows and 4 columns where afterwards user can click the addrow button to add more rows
Everything in my code works fine except the addrow button which does nothing. I will appreciate any help.
public class AddingNewRows extends JFrame {
JTable mytable;
JButton addrow;
String[] columns={"Admission number","Name","School","Year"};
TableColumn tc;
int defaultrows=5;
int rows=new Mytablemodel().getRowCount(),columnscount=new Mytablemodel().getColumnCount();
List data=new ArrayList();
Mytablemodel mytbm;
//
public AddingNewRows(){
super("adding rows");
for(int initialrows=0; initialrows<5; initialrows++){
String[] items={"1","2","3","4"};
data.add(items);
}
mytbm=new Mytablemodel();
mytable=new JTable(mytbm);
JScrollPane scroll=new JScrollPane(mytable);
addrow=new JButton("ADD ROW");
//
JPanel buttonpanel=new JPanel();
buttonpanel.setLayout(new BoxLayout(buttonpanel,BoxLayout.X_AXIS));
buttonpanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
buttonpanel.add(addrow);
//
add(scroll,BorderLayout.CENTER);
add(buttonpanel,BorderLayout.SOUTH);
addrow.addActionListener(new Myactions());
}
public class Mytablemodel extends DefaultTableModel{
#Override
public String getColumnName(int column) {
return columns[column];
}
#Override
public Object getValueAt(int row, int col){
return ((String[])data.get(row))[col];
}
#Override
public boolean isCellEditable(int row, int col){
return true;
}
#Override
public void setValueAt(Object value,int row, int col){
((Object[])data.get(row))[col]=value;
fireTableCellUpdated(row,col);
}
#Override
public Class getColumnClass(int column){
return getValueAt(0,column).getClass();
}
#Override
public int getColumnCount(){
return columns.length;
}
#Override
public int getRowCount(){
return increaserows;
}
#Override
public void addRow(Object[] mynewdata){
int rownum=data.size();
System.out.println(rownum);
data.add(madata);
fireTableRowsInserted(rownum,rownum);
}
}
//
private class Myactions implements ActionListener{
#Override
public void actionPerformed(ActionEvent event){
if(event.getSource()==addrow){
Object[]newdata={"","","",""};
mytbm.addRow(newdata);
}
}
}
public static void main(String[] args) {
AddingNewRows frame=new AddingNewRows();
frame.setVisible(true);
frame.setSize(400,400);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
}
}
Some notes about your code:
You never should call any of the fireXxx() methods explicitely from the
outside. Those are intended to be called internally by
AbstractTableModel subclasses when needed. Note: IMHO those should
be protected and not public, to avoid use them incorrectly. But for
some reason they made them public.
Your addrow button seems to create a new table model that is not
attached to any JTable so it makes no sense. Your table model should
provide an addRow(...) method in order to add a new row to it. Most
likely you will have to enlarge the two-dimensions array that is the
table model's underlyinig data structure any time a row is added.
As #AndrewThompson already suggested, DefaultTableModel seems a
good match to do what your table model does.
Check rows and columnscount properties initialization. It doesn't
seem right to me.
On the other hand, you say in a comment:
I'm having trouble understanding the fireTableRowsInserted(int,int) method. the parameters themself and
where or when to call the method
This method should be called within the addRow(...) that I've suggested you to create in the second point. This method should enlarge the data structure and notify the TableModelListeners that a new row/s has/have been inserted. The parameters are the first and last indexes respectively. Tipically when you append a new single row to the end of the table model, then both first and last indexes are the same and the new size - 1 of the underlying data structure. Of course, several rows can be inserted and not necessarily at the end of the table model, so you have to figure out the appropriate indexes. See the example shown here which uses a List of custom objects as data structure.
According to your question,You want to add new rows every time the user clicks the addrow button.
Achieve your objective by using DefaultTableModel without creating your own or overriding addrow method.
in my example below,parameters in the DefaultTableModel constructor represents the initial rows(5) and columns(4) that the table will have where after execution, the user can add more rows by clicking the addrow button.
public class AddingNewRows extends JFrame {
DefaultTableModel def;
JTable mytable;
JButton addrow;
//
public AddingNewRows(){
super("adding rows");
def=new DefaultTableModel(5,4);
mytable=new JTable(def);
JScrollPane scroll=new JScrollPane(mytable);
addrow=new JButton("ADD ROW");
//
JPanel buttonpanel=new JPanel();
buttonpanel.setLayout(new BoxLayout(buttonpanel,BoxLayout.X_AXIS));
buttonpanel.add(addrow);
//
add(scroll,BorderLayout.CENTER);
add(buttonpanel,BorderLayout.SOUTH);
addrow.addActionListener(new Myactions());
}
private class Myactions implements ActionListener{
#Override
public void actionPerformed(ActionEvent event){
if(event.getSource()==addrow){
Object[]newdata={"","","",""};
def.addRow(newdata);
}
}
}
public static void main(String[] args) {
AddingNewRows frame=new AddingNewRows();
frame.setVisible(true);
frame.setSize(400,400);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
}
}
This is my TableModel, I have extended AbstractTableModel
class CustomTableModel extends AbstractTableModel
{
String[] columnNames = {"Name","Contact","eMail","Address","City","Pin","State","Type","ID"};
Vector<String[]> data = new Vector<String[]>();
CustomTableModel()
{
try
{
//Using JDBC connection//
while(rs.next())
{
String[] s=new String[9];
s[0]=rs.getString(1);
//System.out.println(s[0]);
s[1]=rs.getString(2);
s[2]=rs.getString(3);
s[3]=rs.getString(4);
s[4]=rs.getString(5);
s[5]=rs.getString(6);
s[6]=rs.getString(7);
s[7]=rs.getString(8);
s[8]=rs.getString(9);
data.add(s);
}
}
catch(Exception e)
{
System.out.println("the exception is :"+e.toString());
}
}
public int getColumnCount() {
int columnCount = columnNames.length;
return columnCount;
}
public int getRowCount() {
int rowCount = data.size();
return rowCount;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex)[columnIndex];
}
public String getColumnName(int column) {
return columnNames[column];
}
public void removeRow(int r)
{
for(int i=0;i<data.size();i++)
{
String[] s = (String[])data.get(i);
if(s[0]==getValueAt(r,0))
{
try
{
//using JDBC connections to delete the data from DB//
//also removing the value from data and also updating the view//
data.remove(data.get(i));
fireTableRowsDeleted(r, r);
}
catch (Exception e)
{
System.out.println(e.toString());
}
break;
}
}
}
//I am using the following code to update the view but it doesnot work//
public void addRow(String[] a)
{
data.add(a);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
}
I have a table class which extends CustomTableModel .
class table extends CustomTableModel
{
final JButton editButton = new JButton("Edit");
final JButton deleteButton = new JButton("Delete");
final JTable mytable = new JTable(new CustomTableModel());
.
.
.
}
I have a add button , and in its action listener i use the following code to pass the values that i wanted to add.
String[] a = {"a","b","c","d","e","f","g","h","i"};
table myTableObj = new table();
myTableObj.addRow(a);
Pls let me know where i am going wrong . Thanks
Pls let me know where i am going wrong . Thanks
String[] a = {"a","b","c","d","e","f","g","h","i"};
table myTableObj = new table();
myTableObj.addRow(a);
code lines talking about
create a new row
create a new JTable
row is added to a new JTable
result is that a new JTable is never added to visible Swing GUI
don't do that, why is a new JTable recreated on every JButtons event
add String[] a... to the CustomTableModel directly
for better help sooner post an SSCCE, short, runnable, compilable
The table class makes no sense. It is supposed to be a TableModel that shoud be set into a JTable. Instead you have JTable declared as a field inside this table class (which should be Table btw according to Java naming convention). The result is that when constructing a new table object, a JTable is constructed inside it with another CustomTableModel inside. So the tableModel you are adding rows into is not the tableModel actually used by your JTable.
You can also use the myCustomTable.fireTableStructureChanged();
I'm trying to use an empty column as a divider between pairs of columns in a JTable. Here's a picture and code for what I have so far. I know I can change the look using a custom TableCellRenderer. Before I go down that road, is there a better way to do this? Any ideas appreciated.
import javax.swing.*;
import javax.swing.table.*;
public class TablePanel extends JPanel {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("TablePanel");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(new TablePanel());
f.pack();
f.setVisible(true);
}
});
}
public TablePanel() {
TableModel dataModel = new MyModel();
JTable table = new JTable(dataModel);
table.getColumnModel().getColumn(MyModel.DIVIDER).setMaxWidth(0);
JScrollPane jsp = new JScrollPane(table);
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
this.add(jsp);
}
private static class MyModel extends AbstractTableModel {
private static final int DIVIDER = 2;
private final String[] names = { "A1", "A2", "", "B1", "B2" };
#Override
public int getRowCount() {
return 32;
}
#Override
public int getColumnCount() {
return names.length;
}
#Override
public String getColumnName(int col) {
if (col == DIVIDER) return "";
return names[col];
}
#Override
public Object getValueAt(int row, int col) {
if (col == DIVIDER) return "";
return (row + 1) / 10.0;
}
#Override
public Class<?> getColumnClass(int col) {
if (col == DIVIDER) return String.class;
return Number.class;
}
}
}
On problem with this approach it that the user will need to "tab over" the divider column. You could use the Table Tabbing suggestion to make it more user friendly.
Or if tabbing between the two tables isn't important, then maybe you can use use two tables and put whatever divider you want betweeen the two. The selection model can shared if required.
Edit:
As I suggested above sharing models is easier than writing custom listeners. To keep the scrolling in sync the code would be:
jspa.getVerticalScrollBar().setModel( jspb.getVerticalScrollBar().getModel() );
You can also do the same with the selection model so that highlighting of rows is in sync.
I kind of combined both answers: I used two tables that share one's scrollbar. This works with sorting, and it actually makes the model simpler. Tabbing doesn't matter, but comparing "A" and "B" does. I think I was trying to solve a "view" problem in the "model". I made this a separate answer, because I'd appreciate any critical comments.
public TablePanel() {
this.setLayout(new GridLayout(1, 0, 8, 0));
JTable tableA = new JTable(new MyModel("A"));
tableA.setPreferredScrollableViewportSize(new Dimension(200, 400));
final JScrollPane jspA = new JScrollPane(tableA);
jspA.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
this.add(jspA);
JTable tableB = new JTable(new MyModel("B"));
tableB.setPreferredScrollableViewportSize(new Dimension(200, 400));
final JScrollPane jspB = new JScrollPane(tableB);
jspB.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
this.add(jspB);
tableA.setSelectionModel(tableB.getSelectionModel());
jspA.getVerticalScrollBar().setModel(jspB.getVerticalScrollBar().getModel());
}
Without knowing what do you want to show in this table it's hard to tell whether you've selected good solution or not.
Regarding this solution. This column does not seem like a divider. Paint it with gray/another color, or paint divider header cell in white.
But anyway I'd prefer JScrollPane + two tables inside it instead of this solution.
Please have a look at the answer of this question, some nice new suggestion was given there: Column dividers in JTable or JXTable