update jscrollpane / viewable area when click on JButton

Hi all,

I have a JTable inside a JScrollPane. I use some JButtons to do some row selections on the JTable.

I would like the viewable area / scroll pane to increment when the row selections are changed using the JButtons. By doing this I wish to see, at the bottom of the scrollpane the row that was selected by the JButton. (i.e. I want the behaviour you get if you select a row then use the arrow keys to move up and down the rows).

Below is a single class which sets up the table buttons etc. A lot of the length comes from the table's data so don't worry.

If you run it and click on the "Next team member" button you will see what it currently does.

package ui;

import java.awt.BorderLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.Vector;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTable;

import javax.swing.ListSelectionModel;

import javax.swing.event.ListSelectionEvent;

import javax.swing.event.ListSelectionListener;

import javax.swing.table.AbstractTableModel;

/*

* Class Window displays a frame in which there is a table and some buttons to

* manipulate the table. It is used as an example of manipulating table data.

*/

publicclass Windowextends JFrame

{

/*

* Data members.

* ========================================================================

*/

private JTable table;

private JScrollPane scroller;

privatefinalstaticint noButtons= 4;

privatefinalstatic String[] buttonNames=

{

"Pick for team","Unpick","Next team member","Next non-team member"

};

private JButton[] buttons=new JButton[noButtons];

/*

* End of Data members.

* ========================================================================

*/

/*

* Constructor.

* ========================================================================

*/

public Window()

{

setTitle("Testing table manipulation");

setSize(1024,300);

setDefaultCloseOperation(EXIT_ON_CLOSE);

setLayout(new BorderLayout());

// Make the table.

table= makeTable();

// Create the scroll pane.

scroller=new JScrollPane(table);

scroller.setSize(800,300);

// Create buttons to manipulate the data.

for (int i= 0; i < noButtons; i++)

{

buttons[i]=new JButton(buttonNames[i]);

buttons[i].addActionListener(new ButtonHandler(buttonNames[i]));

}

// A panel for the buttons.

JPanel buttonPanel=new JPanel();

buttonPanel.setSize(224,300);

for (int i= 0; i < noButtons; i++)

buttonPanel.add(buttons[i]);

// Add everything to the frame.

JPanel contentPane= (JPanel)this.getContentPane();

contentPane.add(scroller,BorderLayout.WEST);

contentPane.add(buttonPanel,BorderLayout.EAST);

// Make visible.

setVisible(true);

}

/*

* End of Constructor.

* ========================================================================

*/

/*

* Methods.

* ========================================================================

*/

// Makes a table.

private JTable makeTable()

{

// The table model.

TableModel tm=new TableModel();

// The actual table.

JTable jt=new JTable(tm);

// The table's selection model.

jt.getSelectionModel().addListSelectionListener(new RowSelectionListener());

return jt;

}

/*

* End of Methods.

* ========================================================================

*/

/*

* Inner classes

* ========================================================================

*/

/*

* Inner class TableModel manages the table model for the table in Window.

* This class contains the real data and methods to manipulate that data.

*/

privateclass TableModelextends AbstractTableModel

{

// Data.

Vector<String> columns;

Vector<Vector> rows;

public TableModel()

{

columns=new Vector<String>();

rows=new Vector<Vector>();

columns.add("Surname");

columns.add("Firstname");

columns.add("SquadNo");

columns.add("Position");

columns.add("In team?");

Vector<Object> v=new Vector<Object>();

v.add("McGeady");

v.add("Aiden");

v.add(46);

v.add("AM RLC");

v.add(true);

rows.add(v);

v=new Vector<Object>();

v.add("McGovern");

v.add("Michael");

v.add(47);

v.add("GK");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Zurawski");

v.add("Maciej");

v.add(7);

v.add("F C");

v.add(true);

rows.add(v);

v=new Vector<Object>();

v.add("McManus");

v.add("Stephen");

v.add(44);

v.add("D LC");

v.add(true);

rows.add(v);

v=new Vector<Object>();

v.add("Virgo");

v.add("Adam");

v.add(4);

v.add("D/F RC");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Feguson");

v.add("Barry");

v.add(6);

v.add("DM C");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Boyd");

v.add("Kris");

v.add(19);

v.add("S C");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Prso");

v.add("Dado");

v.add(9);

v.add("S C");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Gordon");

v.add("Craig");

v.add(1);

v.add("GK");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Pressley");

v.add("Steven");

v.add(4);

v.add("D RC");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Hartley");

v.add("Paul");

v.add(7);

v.add("AM RC");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

rows.add(v);

v=new Vector<Object>();

v.add("Smith");

v.add("John");

v.add(10);

v.add("AM L");

v.add(false);

}

// AbstractTableModel methods.

// The number of rows.

publicint getRowCount()

{

return rows.size();

}

// The number of columns.

publicint getColumnCount()

{

return columns.size();

}

// The value at row, column.

public Object getValueAt(int row,int column)

{

return (rows.elementAt(row)).elementAt(column);

}

// Allows the column names to be set.

// Also obtains the column name at column.

public String getColumnName(int column)

{

return columns.elementAt(column);

}

// Allows boolean columns to be displayed as checkboxes.

public Class getColumnClass(int columnIndex)

{

return getValueAt(0,columnIndex).getClass();

}

// Is the cell at row, column editable?

publicboolean isCellEditable(int row,int column)

{

returntrue;

}

// Allows the data to be changed.

publicvoid setValueAt(Object o,int row,int column)

{

(rows.elementAt(row)).setElementAt(o, column);

fireTableDataChanged();// Very important.

}

}

/*

* Inner class RowSelectionListener handles selection events on a Window

* instance's table rows.

*/

privateclass RowSelectionListenerimplements ListSelectionListener

{

publicvoid valueChanged(ListSelectionEvent e)

{

//Ignore extra messages.

if (e.getValueIsAdjusting())

return;

ListSelectionModel lsm = (ListSelectionModel)e.getSource();

if (!lsm.isSelectionEmpty())

{

int selectedRow = lsm.getMinSelectionIndex();

String s="";

for (int i= 0; i < table.getColumnCount(); i++)

{

s+= (table.getValueAt(selectedRow, i)).toString() +" ";

}

System.out.println(s);// Would be appended to the textarea.

}

}

}

/*

* Inner class buttonHandler handles events from the buttons.

*/

privateclass ButtonHandlerimplements ActionListener

{

// Data members.

private String name;

// Constructor.

// Sets the name.

public ButtonHandler(String nm)

{

name= nm;

}

publicvoid actionPerformed(ActionEvent ae)

{

if (name.equals(buttonNames[0]))// "Pick for team"

{

changeStatus(true);

}

elseif (name.equals(buttonNames[1]))// "Unpick"

{

changeStatus(false);

}

elseif (name.equals(buttonNames[2]))// "Next team member"

{

int row;

if (table.getSelectionModel().isSelectionEmpty())

row= -1;

else

row= table.getSelectedRow();

// From next row until end of rows, look for correct value.

while (true)

{

for (int i= (row+1); i < table.getRowCount(); i++)

{

// The in team? column is the last one.

int inTeamColumn= table.getColumnCount()-1;

if (((Boolean)table.getValueAt(i,inTeamColumn)).booleanValue() ==true)

{

table.getSelectionModel().setSelectionInterval(i, i);

return;

}

}

// Go back to the start.

row= -1;

}

}

elseif (name.equals(buttonNames[3]))// "Next non-team member"

{

int row;

if (table.getSelectionModel().isSelectionEmpty())

row= -1;

else

row= table.getSelectedRow();

// From next row until end of rows, look for correct value.

while (true)

{

for (int i= (row+1); i < table.getRowCount(); i++)

{

// The in team? column is the last one.

int inTeamColumn= table.getColumnCount()-1;

if (((Boolean)table.getValueAt(i,inTeamColumn)).booleanValue() ==false)

{

table.getSelectionModel().setSelectionInterval(i, i);

return;

}

}

// Go back to the start.

row= -1;

}

}

}

// Changes whether they were picked or unpicked.

// picked = true means they were picked.

privatevoid changeStatus(boolean picked)

{

int row= table.getSelectedRow();

if (row < 0)

{

// Should be a dialogue (or do nothing?)

System.out.println("Error! No row was selected.");

}

else

{

for (int i= 0; i < table.getColumnCount(); i++)

{

if (table.getColumnName(i).equals("In team?"))

{

boolean boolVal= (Boolean)table.getValueAt(row, i);

if (boolVal == !picked)

table.setValueAt(picked, row, i);

}

}

// Reselect the row.

table.getSelectionModel().setSelectionInterval(row, row);

}

}

// Find the next one who has value picked.

privatevoid findNext(boolean picked)

{

}

}

/*

* End of Inner Classes.

* ========================================================================

*/

/*

* Test program.

* ========================================================================

*/

publicstaticvoid main(String[] args)

{

new Window();

}

/*

* End of Test program.

* ========================================================================

*/

}

[24608 byte] By [johnmcparlalda] at [2007-10-3 3:42:10]
# 1

Here's a one-line solution:

NOTE: I corrected a little bug ending in an endless loop when no team member was selected and [Next team member] was clicked:

int iter = 0;

while (iter < 2) {

for (int i = (row + 1); i < table.getRowCount(); i++) {

// The in team? column is the last one.

int inTeamColumn = table.getColumnCount() - 1;

if (((Boolean) table.getValueAt(i, inTeamColumn))

.booleanValue() == member) {

table.getSelectionModel().setSelectionInterval(i, i);

table.scrollRectToVisible(table.getCellRect(i, 0, false));

return;

}

}

// Go back to the start.

row = -1;

iter++;

}

Franck_Lefevrea at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...
# 2

1) Don't use Window as your class name. Window is an AWT component name in the standard JDK.

2) Why are you creating your own TableModel? The DefaultTableModel will work perfectly well for you and its a lot less work.

Anyway, I don't use JDK1.5 so I can't test your code to see whats happening because of your generics and custom TableModel.

The only advice I have about scrolling is you can use the scrollRectToVisible(...) method to do this. JTable has a method that can help you get the rectangle of a cell.

camickra at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...
# 3
> [...] > The only advice I have about scrolling is you can use the scrollRectToVisible(...) method to do this.> JTable has a method that can help you get the rectangle of a cell.Pfiew... happy to see that my post meets your suggestion ;-)
Franck_Lefevrea at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...
# 4
> Pfiew... happy to see that my post meets your suggestion ;-) Guess, you replied while I was busy trying to get the program to compile. Anyway how does the saying go..."Great minds think a like ... fools seldom differ"We will find out from the OP where we belong
camickra at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...
# 5

Franck,

thanks for your informative reply. This indeed fix my problem.

Camickr,

1. It doesn't matter that there is already a Window class in AWT. You may have noticed that my Window and AWT's window are in different packages. You should read about packages - they let you give classes the same name if they are in different packages.

2. Created my own TableModel because this program is a mock up of a more complicated one. I implement / extend the same classes and leave out the algorithms in my mock ups.

Why don't you use JDK 1.5 - why not download it? Why not use both 1.4 and 1.5? Why not harness the power of Generics? I need generics in my app (booleans, strings and numbers in my vectors) so I use them.

You could have just posted that ( setScroll....

) - why do you make lots of points that don't help before answering the question?

Franck and Camickr,

great minds do think alike.

Camickr,

who is OP?

johnmcparlalda at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...
# 6
> Anyway how does the saying go...> > "Great minds think a like ... fools seldom differ"> > We will find out from the OP where we belong :-)To my opinion, no doubt about where you belong... I'll try my best to reach you there.
Franck_Lefevrea at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...
# 7

> why do you make lots of points that don't help before answering the question?

They may not help with the immediate problem, but they may help with future problems, if you take the time to understand what I am saying.

If I make the points after answering the question, then you probably won't read them.

> You should read about packages - they let you give classes the same name if they are in different packages.

Just because you can do something doesn't mean you should

A class name should be descriptive. Window is not very descriptive.

Even for a simple text case I would use something like WindowTest.

If I am just reading your source code and I see a line like the following:

Window window = new Window();

The first thing that comes to mind is that you are trying to create an AWT Window. How do I know that you've given your class a package name? I should not need to look at the package or import statments to know what class you are talking about. Any time you can reduce confusion the better.

> Created my own TableModel because this program is a mock up of a more complicated one

Why go to all this trouble for a simple demo program? Your question is about scrolling, not the data in the table. I would use:

JTable table = new JTable(30, 5);

The less clutter you have in your demo program. The easier it is for us to see whats happening.

I mention the DefaultTableModel, because the majority of people who post table related questions on the forum don't fully understand how TableModels work or the flexibility of the DefaultTableModel and end up getting themselves into trouble by trying to unnecessarily extend AbstractTableModel. Remember your last posting was about an incorrect implementation of a TableModel when you couldn't get the column names to work.

> Why don't you use JDK 1.5 - why not download it?

JDK1.4 does everthing I want.

> I need generics in my app (booleans, strings and numbers in my vectors) so I use them.

You don't need to use Generics. You just decided you wanted to use them. The TableModel still stores Integers and Booleans etc. The compiler just does the conversion for you.

Another design is to create a class to hold your data and then create a TableModel based on this class. For example here is an example of a TableModel that holds Stock Securities:

import java.util.*;

import javax.swing.table.*;

/**

*

*/

public class SecurityTableModel extends AbstractTableModel

{

List list;

private static String[] columnNames =

{

"Security",

"Symbol",

"Exchange",

"Type",

"Asset Class",

"Goal"

};

public SecurityTableModel()

{

SecurityManager manager = SecurityManager.getSharedInstance();

list = manager.getList();

Collections.sort( list );

}

public int getColumnCount()

{

return columnNames.length;

}

public int getRowCount()

{

return list.size();

}

public String getColumnName(int col)

{

return columnNames[col];

}

public Object getValueAt(int row, int col)

{

Security security = (Security)list.get( row );

switch (col)

{

case 0: return security.getName();

case 1: return security.getSymbol();

case 2: return security.getExchange();

case 3: return security.getType();

case 4: return security.getAssetClass();

case 5: return security.getGoal();

}

return null;

}

public Class getColumnClass(int c)

{

return String.class;

}

public boolean isCellEditable(int row, int col)

{

return false;

}

public void xxxsetValueAt(Object value, int row, int col)

{

}

}

Your getter and setter of the Security class then convert the data as required.

> who is OP?

Original Poster

camickra at 2007-7-14 21:38:03 > top of Java-index,Desktop,Core GUI APIs...