Can somebody tell me why this doesn't work? The line System.out.println(table.getSelectedRow()); is red for some reason! I want each time I click a row to show me which row is selected. Am I missing something here?
table = new JTable(data, columnNames) {
public Class getColumnClass(int column) {
for (int row = 0; row < getRowCount(); row++) {
Object obj = getValueAt(row, column);
if (obj != null) {
return obj.getClass();
}
}
return Object.class;
}
};
JScrollPane scroll = new JScrollPane(table);
getContentPane().add(scroll);
JPanel panel = new JPanel();
getContentPane().add(panel, BorderLayout.SOUTH);
table.addMouseListener(new MouseListener() {
public void mousePressed(MouseEvent e) {
System.out.println(table.getSelectedRow());
}
});
To elaborate on what #BlackBox said, table isn't a class variable, but the mouse listener you've added is technically a new inner class. That new class doesn't have access to the table variable unless it is marked as final or unless the class that contains both table and the mouse listener has a reference to table.
Declare the table variable as final.
For more information, see Final and inner classes on this wiki page:
http://en.wikipedia.org/wiki/Final_%28Java%29#Final_variables
Essentially, marking a variable as final tells the compiler the value will never change. This indication allows the compiler to capture and store values for inner classes it otherwise wouldn't have been able to do at run time.
Related
I'm working on Reflections and Swings for my project. Using reflections I need to get the information of a particular method, and populate a JTable based on its structure.
The table has the following feature:
Some columns will be named based on the parameter type present in the selected method.
Its the same structre for all the primitive data types(int,float,double,String,long,boolean), only the column names get Changed based on the data type.
The problem I face is when there is a User Defined object inside the method parameter.
In that scenario , I want a JButton instead of empty cell in the row (under that parameter type).
I tried learningTableCellRenderer and CellEditor but nothing helped me because all the tutorials i have seen are based on a Static data(rows and columns). In my case both rows and columns have to be generated dynamically and I need to create the JTable based on my data(dynamically).
I'm trying the following code:
rowData = new Object[1][colData.length];
rowData[0][0] = "";
rowData[0][colData.length - 1] = "";
int i = 1;
for (Class tempClass : paramType) {
if (tempClass.getSimpleName().equals("int")
//Primitives
|| tempClass.getSimpleName().equals("float")
|| tempClass.getSimpleName().equals("long")
|| tempClass.getSimpleName().equals("double")
|| tempClass.getSimpleName().equals("boolean")
|| tempClass.getSimpleName().equals("String")){
rowData[0][i] = "";
}
else{
//User Defined obj
rowData[0][i] = new JButton();
}
i++;
}
But the output i'm getting is something like this:
Kindly help me and provide me a hint or kind of tutorial so that i can proceed with this problem. I'm working on the swings for the first time.
The pictures you posted don't help me since the text to too small for me to read so I'm not sure what you are trying to demonstrate. Make sure the data is readable when you post a question is the data is in fact important to the question.
nothing helped me because all the tutorials i have seen are based on a Static data(rows and columns).
Maybe this example will help. It shows how to dynamically determine the renderer/editor for a cell based on the class of the data in the cell:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TablePropertyEditor extends JFrame
{
public TablePropertyEditor()
{
String[] columnNames = {"Type", "Value"};
Object[][] data =
{
{"String", "I'm a string"},
{"Date", new Date()},
{"Integer", new Integer(123)},
{"Double", new Double(123.45)},
{"Boolean", Boolean.TRUE}
};
JTable table = new JTable(data, columnNames)
{
private Class editingClass;
public TableCellRenderer getCellRenderer(int row, int column)
{
editingClass = null;
int modelColumn = convertColumnIndexToModel(column);
if (modelColumn == 1)
{
Class rowClass = getModel().getValueAt(row, modelColumn).getClass();
return getDefaultRenderer( rowClass );
}
else
return super.getCellRenderer(row, column);
}
public TableCellEditor getCellEditor(int row, int column)
{
editingClass = null;
int modelColumn = convertColumnIndexToModel(column);
if (modelColumn == 1)
{
editingClass = getModel().getValueAt(row, modelColumn).getClass();
return getDefaultEditor( editingClass );
}
else
return super.getCellEditor(row, column);
}
// This method is also invoked by the editor when the value in the editor
// component is saved in the TableModel. The class was saved when the
// editor was invoked so the proper class can be created.
public Class getColumnClass(int column)
{
return editingClass != null ? editingClass : super.getColumnClass(column);
}
};
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane( table );
getContentPane().add( scrollPane );
}
public static void main(String[] args)
{
TablePropertyEditor frame = new TablePropertyEditor();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
Edit:
instead to get a Button, what should i keep
There is no default renderer/editor for a button, so you will need to store a custom object and create a custom renderer/editor.
Read the section from the Swing tutorial on Editors and Renderers for more information.
Then you have to tell the table about your custom objects with code like:
table.setDefaultRenderer(CustomObject.class, new CustomRenderer());
table.setDefaultEditor(CustomObject.class, new CustomRenderer());
You might be able to use the Table Button Column as the renderer/editor.
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);
}
}
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'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