Create JTable rendering method which can be called by another class - java

I have a JFrame which has a JTable and it can represent data from database. Everything is fine but I want to load or refresh it from another class after a delete operation.
I have already done the delete operation but I can not load the JTable from another class. My code is below:
scrollPane = new JScrollPane();
add(scrollPane, BorderLayout.CENTER);
DefaultTableModel model=null;
try {
model = makeTableModel();
} catch (SQLException | IOException e) {
e.printStackTrace();
}
table = new JTable(model);
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
int row = table.getSelectedRow();
String getvalue = (table.getModel().getValueAt(row, 4).toString());
PopulatePhotographerClass pp=new PopulatePhotographerClass(getvalue);
}
});
table.setRowHeight(200);
scrollPane.setViewportView(table);
Here is my makeTableModel method:
public static DefaultTableModel makeTableModel() throws SQLException, IOException {
DefaultTableModel model = new DefaultTableModel(new String[]{"Image", "Name","Address","mobile-Number","NID"}, 0) {
#Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Icon.class : super.getColumnClass(columnIndex);
}
};
String cmd = "select * from photographer_lookup";
try (Connection con =database.DbConnect.getconnection()) {
try (PreparedStatement stmt = con.prepareStatement(cmd)) {
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
String name = rs.getString(3);
Blob blob = rs.getBlob(1);
String address=rs.getString("address");
String mobile=rs.getString("mobile_number");
String nid=rs.getString("Nid");
ImageIcon icon = null;
try (InputStream is = blob.getBinaryStream()) {
BufferedImage img = ImageIO.read(is);
icon = new ImageIcon(img);
}
model.addRow(new Object[]{icon,name,address,mobile,nid});
}
}}}
return model;
}
This is my code that I wrote. Now I want to define a method that does all work which is mentioned above and also it will be called by another class.

First off, you are making a mistake in using view indices to index the model. Listeners and all methods from JTable that return row or column indices will report view indices (except convertXXXIndexToModel methods).
As the table is sorted or columns are moved around, view indices will differ from model indices. JTable will not sort the model or rearrange columns in the model, rather it will change its mapping to the model.
If what you have is view indices and you want to look up cell values, either
Use JTable.getValueAt, which takes view indices
First convert view indices to model indices using JTable.convertRowIndexToModel and JTable.convertColumnIndexToModel before indexing in the model (JTable.getModel()).
Your mouse listener should read:
table.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
int row = table.getSelectedRow();
if( row < 0 ) return; // check if a row is selected first!
String getvalue = table.getValueAt(row, table.convertColumnIndexToView( 4 ) ).toString(); // use table.getValueAt, this getter takes view indices! Use convertColumnIndexToView to get a view index from a model index!
PopulatePhotographerClass pp=new PopulatePhotographerClass(getvalue);
}
});
Better still would be to implement a ListSelectionListener to listen for selection events, rather than using a MouseListener to act on selection events (thanks #mKorbel for pointing that out). This way you would be notified directly on list selection changes.
If you want operations to be done from another class, write a public method in your class that extends JFrame that does this work. If you have an instance to this class in another class, simply call this newly created public method.
Suppose your JFrame class is called MyFrameWithJTable
public class MyFrameWithJTable extends JFrame {
public void doSomeWork( /*parameters required in the operation*/ ) {
// Does the work you want to call from another class
// Eg the updates you want done in the JTable's model
}
}
Then in another class, if you have an instance of the MyFrameWithJTable class you can do
public class AnotherClass {
private MyFrameWithJTable instance;
public AnotherClass( MyFrameWithJTable instance ) {
this.instance = instance;
}
public void someMethod( ) {
instance.doSomeWork( /*supply parameters*/ );
}
}

Related

Why we extends AbstractTabelModel when set values into JTabel?

Im developing a app for ordeing system and i have to set data into JTabels.
And this code is successfully working.I wanted to know what the importance of and whats happen in this class?
Why we need to import AbstractTabelModel.class?
OrderTabelModel Class:-
public class OrderTableModel extends AbstractTableModel{
protected static final String[] COLUMN_NAMES={"Item","Qty","Amount"};
private List<Order> rows;
public OrderTableModel(List<Order> rows){
this.rows = new ArrayList<>(rows);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return COLUMN_NAMES.length;
}
#Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value = null;
Order row = rows.get(rowIndex);
switch (columnIndex) {
case 0:
value = row.getItem();
break;
case 1:
value = row.getQty();
break;
case 2:
value = row.getAmount();
break;
}
return value;
}
}
this is other class
private void tblOrderListMouseClicked(java.awt.event.MouseEvent evt) {
int raw = tblOrderList.getSelectedRow();
Order or;
String item;
Double qty,amount,total;
ArrayList<Order> arrOrder = new ArrayList<Order>();
String selectedRaw = tblOrderList.getModel().getValueAt(raw, 0).toString();
System.out.println("order id : "+selectedRaw);
String sql = "select item,qty,amount from orderdetails where orderid='"+selectedRaw+"'";
con = new DBconnector().connect();
try {
Statement ps =con.createStatement();
ResultSet rs2 = ps.executeQuery(sql);
while(rs2.next()){
or = new Order();
or.setItem(rs2.getString("item"));
System.out.println("Item :" +rs2.getString("item"));
or.setQty(rs2.getDouble("qty"));
or.setAmount(rs2.getDouble("amount"));
arrOrder.add(or);
}
rs2.close();
ps.close();
OrderTableModel tblModel = new OrderTableModel(arrOrder);
tblOrderItems.setModel(tblModel);
} catch (Exception e) {
e.printStackTrace();
}
}
Can some one explain me the process of this please?
It is not always mandatory to extend the AbstractTableModel. You can simply extend the DefaultTableModel and only override the getValueAt() method if you have to.
But most of the time for simple usages it is not even needed to override the getValueAt() method either.
By using the DefaultTableModel, you have a limitation for the converting you data (imported from DB) to an object[][] or Vector types which may be a little boring.
But you asked what is the importance of using AbstractTabelModel?
In this case I can say when JTable is started to being rendered, it needs to determine the number of rows and number of the columns and also it needs to know which data should be renedered in each cell and so on. Based on this, JTable ask for this Information from the underlying TableModel. So it is needed for your TableModel(any child or implementation of TableModel) to have those methods which are used by JTable to retrieve the needed information.
Hope this would be helpful.
Good Luck.

Not able to sort CellTable Column

Trying to make my CellTable Colum sortable but I'm not getting it to work. I'm having an MVP application which gets data from a rest service. To show the data within the table works fine but to sort is doesn't work.
public class LicenseUsageUserViewImpl<T> extends Composite implements LicenseUsageUserView<T> {
#UiTemplate("LicenseUsageUserView.ui.xml")
interface LicenseDataViewUiBinder extends UiBinder<ScrollPanel,LicenseUsageUserViewImpl> {}
private static LicenseDataViewUiBinder uiBinder = GWT.create(LicenseDataViewUiBinder.class);
#UiField
CellTable<GWTLicenseUser> licenseUserCellTable;
List<GWTLicenseUser> licenseUsers;
ListDataProvider<GWTLicenseUser> dataProvider;
public List<GWTLicenseUser> getLicenseUsers() {
return licenseUsers;
}
public void setLicenseUsers(List<GWTLicenseUser> licenseUsers) {
this.licenseUsers = licenseUsers;
}
#UiField Label header;
ListHandler<GWTLicenseUser> sortHandler;
public LicenseUsageUserViewImpl() {
initWidget(uiBinder.createAndBindUi(this));
initCellTable();
}
#Override
public void setLicenseUsersTable(List<GWTLicenseUser> tmpLicenseUsers) {
if (tmpLicenseUsers.isEmpty()) {
licenseUserCellTable.setVisible(false);
} else {
setLicenseUsers(tmpLicenseUsers);
licenseUserCellTable.setWidth("100%");
licenseUserCellTable.setVisible(true);
licenseUserCellTable.setPageSize(getLicenseUsers().size());
licenseUserCellTable.setRowCount(getLicenseUsers().size(), false);
licenseUserCellTable.setRowData(0, getLicenseUsers());
licenseUserCellTable.setVisibleRange(new Range(0, licenseUserCellTable.getRowCount()));
sortHandler.setList(getLicenseUsers());
dataProvider.getList().clear();
dataProvider.getList().addAll(getLicenseUsers());
}
}
#Override
public void initCellTable() {
sortHandler = new ListHandler<GWTLicenseUser>(getLicenseUsers());
licenseUserCellTable.addColumnSortHandler(sortHandler);
licenseUserCellTable.setWidth("100%");
licenseUserCellTable.setVisible(true);
licenseUserCellTable.setVisibleRange(new Range(0, licenseUserCellTable.getRowCount()));
// Create a data provider.
dataProvider = new ListDataProvider<GWTLicenseUser>();
// Connect the table to the data provider.
dataProvider.addDataDisplay(licenseUserCellTable);
licenseUserCellTable.setWidth("100%");
licenseUserCellTable.setAutoHeaderRefreshDisabled(true);
licenseUserCellTable.setAutoFooterRefreshDisabled(true);
// userID
TextColumn<GWTLicenseUser> userIdColumn = new TextColumn<GWTLicenseUser>() {
#Override
public String getValue(GWTLicenseUser object) {
if (object != null ){
return object.getUserId();
} else {
return "NULL";
}
}
};
userIdColumn.setSortable(true);
sortHandler.setComparator(userIdColumn, new Comparator<GWTLicenseUser>() {
#Override
public int compare(GWTLicenseUser o1, GWTLicenseUser o2) {
return o1.getUserId().compareTo(o2.getUserId());
}
});
licenseUserCellTable.addColumn(userIdColumn, "User ID");
// more column entries
licenseUserCellTable.getColumnSortList().push(userIdColumn);
licenseUserCellTable.getColumnSortList().push(countColumn);
licenseUserCellTable.addColumnSortHandler(sortHandler);
}
}
setLicenseUsersTable is called from my activity with the response list of my users. When I start my application and make a rest call my data is provide and put into my list also shown within the CellTable but its not sortable, but I have this sort icon before my colum name. I figured I post the whole code because I think its know easier to see what I'm trying to do.
Thanks for any help.
Remove this line:
sortHandler.setList(getLicenseUsers());
You already passed a List into the SortHandler constructor in
sortHandler = new ListHandler<GWTLicenseUser>(getLicenseUsers());
Also, instead of
setLicenseUsers(tmpLicenseUsers);
you may need to use
licenseUsers.addAll(tmpLicenseUsers);
I hope one of them fixes the problem.

Add table column with BeanItemContainer

I should add a column to a table that has a BeanItemContainer datasource.
This is my situation:
I hava an entity bean
#Entity
public class MyBean implements {
#Id
private Long id;
//other properties
}
Then in my vaadin panel i have this method
private Table makeTable(){
Table table = new Table();
tableContainer = new BeanItemContainer<MyBean>(MyBean.class);
table.setContainerDataSource(tableContainer);
table.setHeight("100px");
table.setSelectable(true);
return table;
}
Now, I want to add a column that should give me the ability to delete an item in this container.
How can i do?
You could create a ColumnGenerator which creates the button for you.
Have a look here.
Example:
Let's say we have a MyBean class:
public class MyBean {
private String sDesignation;
private int iValue;
public MyBean() {
}
public MyBean(String sDesignation, int iValue) {
this.sDesignation = sDesignation;
this.iValue = iValue;
}
public String getDesignation() {
return sDesignation;
}
public int getValue() {
return iValue;
}
}
We then can create a table with a generated column giving a button to delete the current item.
Table table = new Table();
BeanItemContainer<MyBean> itemContainer = new BeanItemContainer<MyBean>(MyBean.class);
table.setContainerDataSource(itemContainer);
table.addItem(new MyBean("A", 1));
table.addItem(new MyBean("B", 2));
table.addGeneratedColumn("Action", new ColumnGenerator() { // or instead of "Action" you can add ""
#Override
public Object generateCell(final Table source, final Object itemId, Object columnId) {
Button btn = new Button("Delete");
btn.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
source.removeItem(itemId);
}
});
return btn;
}
});
table.setVisibleColumns(new Object[]{"designation", "value", "Action"}); // if you added "" instead of "Action" replace it by ""
I would recommend using shourtcut instead:
table.addShortcutListener(new ShortcutListener("Delete", KeyCode.DELETE, null) {
#Override
public void handleAction(final Object sender,
final Object target) {
if (table.getValue() != null) {
// here send event to your presenter to remove it physically in database
// and then refresh the table
// or just call tableContainer.removeItem(itemId)
}
}
});
if you don't want shourtcuts you would need add the column, eg:
table.addContainerProperty("Delete", Button.class, null);
and then put there the button that would do the same action.

Making an addRows() method for a Custom JTable TableModel

My explanation below rambles, boiling down, is there a way I can add a Row without firing off an event, such that I can add multiple rows and fire an event to update all of them at once? Without having to add code to contain the table data in the custom model?
I have a custom TableModel which extends from DefaultTableModel so that I can use DefaultTableModel to keep track of data for me, whilst still having some custom methods of my own.
The issue is, I was thinking it might be faster for me to have an "addRows(String[][] val)" method, when I wish to add multiple rows. I could then fire a single event, probably fireTableDataChanged() to update the rows all at once. For example, my current method:
JTable table1 = new JTable(new dgvTableModel(new String[] {<values>},0, new String[] {<values>}));
table1.addRow(new String[] {<values here>});
table1.addRow(new String[] {<values here>});
table1.addRow(new String[] {<values here>});
I would then repeat the above as many times as necessary. The issue is, each of those will fire off a seperate event. It would be much faster (I think), if I could do this using my custom table model:
JTable table1 = new JTable(new dgvTableModel(new String[] {<values>},0, new String[] {<values>}));
table1.addRows(new String[][] {{<values1 here}, {values2 here}, . . .}});
and then in the table model:
public void addRows(String[][] values) {
for (String[] vals : values)
super.addRow(vals);
}
fireTableDataChanged();
}
I can code this in easily. The issue is again, that "super.addRow(vals);" line will fire an event each time through. Is there a way, without adding code to have my model contain the table data itself, to prevent that event being fired each time I add a row? Such that it waits for the fireTableDataChanged() call in the addRows method?
For reference, the code for my custom table model:
import java.awt.Color;
import java.util.ArrayList;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
public class dgvTableModel extends DefaultTableModel {
//private DataTable tableVals = new DataTable();
private ArrayList<Color> rowColors;
//private ArrayList<Object[]> data = new ArrayList<>();
//default constructor has no data to begin with.
private int[] editableColumnNames;
public dgvTableModel(String[] colNames, int rowCount)
{
super(colNames, rowCount);
}
public dgvTableModel(String[] colNames, int rowCount, String[] editableColNames)
{
super(colNames, rowCount);
//this.tableVals.setColNames(colNames);
if (editableColNames!=null && editableColNames.length >0)
{
editableColumnNames = new int[editableColNames.length];
int count = 0;
for (int i =0; i< editableColNames.length;i++)
{
for (String val : colNames)
{
if (val.equalsIgnoreCase(editableColNames[i]))
{
editableColumnNames[count] = i;
count+=1;
break;
}
}
}
}
}
public dgvTableModel(String[] colNames, int rowCount, String[] editableColNames, boolean colorChanges)
{
super(colNames, rowCount);
Color defColor = UIManager.getDefaults().getColor("Table.background");
if (editableColNames!=null && editableColNames.length >0)
{
editableColumnNames = new int[editableColNames.length];
int count = 0;
if (colorChanges)
{
rowColors = new ArrayList<>();
}
for (int i =0; i< colNames.length;i++)
{
if (colorChanges)
{
rowColors.add(defColor);
}
for (String val : editableColNames)
{
if (val.equalsIgnoreCase(colNames[i]))
{
editableColumnNames[count] = i;
count+=1;
break;
}
}
}
}
else if (colorChanges)
{
rowColors = new ArrayList<>();
for (String val : colNames)
{
rowColors.add(defColor);
}
}
}
#Override
public boolean isCellEditable(int row, int column)
{
if(editableColumnNames!=null && editableColumnNames.length >0)
{
for (int colID : editableColumnNames)
{
if (column==colID)
return true;
}
}
return false;
}
public void setRowColor(int row, Color c)
{
rowColors.set(row, c);
fireTableRowsUpdated(row,row);
}
public Color getRowColor(int row)
{
return rowColors.get(row);
}
#Override
public Class getColumnClass(int column)
{
return String.class;
}
#Override
public String getValueAt(int row, int column)
{
return super.getValueAt(row, column).toString();
}
}
Surely firing one event to display every row, is faster than firing one event for each row?
'AbstractTableModel.fireTableDataChanged()' is used to indicate to the model (and the JTable UI which is notified by the model) that all possible data in the table may have changed and needs to be checked. This can (with emphasis on can) be an expensive operation. If you know which rows have been added, just use the 'AbstractTableModel.fireTableRowsInserted(int firstRow, int lastRow)' method instead. This will ensure only the effect rows are seen as changed. Take a look at all the fire* methods in AbstractTableModel. You can really exercise fine grained control over which rows, cells, etc are seen as dirty.
Then again what your doing might be premature optimalization. Unless you have fiftythousand records in your JTable this is probably not going to be noticable. But if you have a massive amount of records in your JTable you might be beter of lazy loading them anyway.

ComboBox database value doesn't refresh after Adding New Data

After adding data into the database, there is no changes to the JComboBox unless I execute the program again. Do I need to call any specific methods to refresh the values? Thanks!
New Code:
class ComboModel extends AbstractListModel implements ComboBoxModel
{
// array? treeset?
public Object getElementAt(int index) {
}
public int getSize() {
}
public Object getSelectedItem() {
}
public void setSelectedItem(Object anItem) {
}
}
final JComboBox stockListComboBox = new JComboBox();
ComboModel model = new ComboModel();
stockListComboBox.setModel(model);
try
{
// Database Stuffs
}
catch(Exception e)
{
e.printStackTrace();
}
Not sure if I got second part of the question, however, regarding values added to DefaultComboBoxModel, you have to call a method to let the combobox know the model elements changed.
You can use fireContentsChanged method or fireIntervalAdded one.

Categories