Problems with JScrollPane -- Trying to Update it on Model Changes - java

I'm writing a fairly complex program, so I'll try to explain it only in terms of where the problem is occurring.
In my view, I create a JScrollPane to display a list of courses that a student is registered for:
registeredPane = new JScrollPane(); registeredPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
registeredPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
c.gridx = 2;
c.gridwidth = 1;
c.gridheight = 2;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
layout.setConstraints(registeredPane, c);
Then I, in my controller, change the model to reflect the courses the currently active student is registered in by calling this function in my model:
public void updateCurrentStudentCourses() {
ArrayList<String> courseNames = new ArrayList<String>();
for (Course c: currentStudent.getRegCourses()) {
courseNames.add("" + c.getDepartment().getId() + c.getCode());
}
System.out.println(courseNames);
}
Then I, again in my controller, update the view to reflect these changes by adding the
ArrayList to the JScrollPane:
public void updateView() {
view.getNameField().setText(model.getCurrentStudent().getName());
view.getRegisteredPane().removeAll();
view.getRegisteredPane().getViewport().add(model.getCurrentStudentCourses());
view.getRegisteredPane().repaint();
}
The scrollbars disappear, but that's it. The list items (which I know are in the ArrayList) are not displayed. What am I doing wrong?

EDIT
If model.getCurrentStudentCourses() is returning an ArrayList then you can put the contents of ArrayList within JTextArea and then set the viewport of JScrollPane to be that JTextArea. You can proceed as follows:
your updateView() method should be like this:
JTextArea ta = new JTextArea(30,100);
public void updateView() {
ta.setText("");
for (String course : model.getCurrentStudentCourses())
ta.append(course+"\n");
view.getNameField().setText(model.getCurrentStudent().getName());
view.getRegisteredPane().setViewportView(ta);
}

To set the view of a scrollpane, use setViewportView(). Manipulating the child components of the scrollpane directly will cause problems (for example, you're removing the scroll bars as well).
Scrollpanes have their own layout manager which only knows about the components created by the scrollpane (the various viewports, the scrollbars, the corners). So, any component you add will not appear unless you manually set its position.

Related

How to Drill Down to a JList within a JScrollPane from another function [duplicate]

This question already has answers here:
Variable cannot be resolved
(6 answers)
Closed 5 years ago.
I am creating a JList like so:
JScrollPane scrollPane = new JScrollPane();
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.insets = new Insets(0, 0, 5, 5);
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.gridx = 0;
gbc_scrollPane.gridy = 1;
contentPanel.add(scrollPane, gbc_scrollPane);
{
JList listAvail = new JList();
listAvail.setBorder(new TitledBorder(null, "", TitledBorder.LEADING, TitledBorder.TOP, null, null));
scrollPane.setViewportView(listAvail);
}
and I'm trying to access it when a button is clicked:
JButton btnAdd = new JButton("Add ->");
btnAdd.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent arg0)
{
if (!listAvail.isSelectionEmpty())
{
int[] selects = listAvail.getSelectedIndices();
for (int i = selects.length - 1; i >= 0; i--)
{
appliedM.add(0, availM.getElementAt(selects[i]));
appliedFeatures.add(0, availFeatures.get(selects[i]));
availFeatures.remove(availFeatures.get(selects[i]));
availM.remove(selects[i]);
}
}
}
});
Of course, I'm getting an error saying "listAvail cannot be resolved" since it's out of the button's scope. Is there a getter method or a way to drill down to a specific component? I'm thinking something along the lines of:
contentPanel.scrollPane.listAvail
There must be something I'm missing but I haven't seen anything like this in the docs.
Edit: I am asking specifically about drilling down to a swing component. This is not a generic variable scoping question and not an exact duplicate of the referenced question. This question has yet to be answered at the time of this edit.
Your question boils down to -- how do I access an object reference when it is assigned to a very locally scoped variable, here the object being the JList, and the variable it's being assigned to is listAvail, a variable buried within its own set of curly braces (why?) within a method or constructor.
The solution -- get the variable declaration out of the local scope and make it instead an instance field of the class. e.g.,
public class MyClass {
private JList listAvail = new JList(); // should be generic btw
Another issue is here:
btnAdd.addMouseListener(new MouseAdapter() {....
Don't use a MouseListener with a JButton where an ActionListener is much more appropriate. This has ramifications if you want the button's actions to be disabled when the button itself is disabled.

Jtable dynamical fixed columns problems

I created a class that can dynamically to lock and unlock columns .
In my program i create two tables with the same tablemodel.
One is in the Jviewport of the scrollpane, the other in the RowHeaderView.
The problem is when you unlock all the locked columns
and you want to start to lock again, doesn't work. There are no errors but it's like the event doesn't answer.
Steps to produce the problem:
Try the code,
put all the columns in the fixed table,
then unlock with right double click,
then start again to lock, and unlock
Do this procedure and you can see that the mouse event doesnt answer anymore
public class Prova extends JFrame{
private JTable mainTable,fixedTable;
private JScrollPane scrollPane;
private JTableHeader mainTableHeader;
private TableColumnModel originalColumnModel,mainColumnModel,fixedColumnModel;
private TableColumn[] columns;
private int ncols,counter;
public Prova(){
counter = 0;
TableModel mainTableModel = new DefaultTableModel(5, 10);
scrollPane = new JScrollPane();
mainTable = new JTable(mainTableModel);
mainColumnModel = mainTable.getColumnModel();
fixedTable = new JTable();
fixedTable.setAutoCreateColumnsFromModel(false);
fixedTable.setModel(mainTable.getModel() );
ncols = mainTableModel.getColumnCount();
columns = new TableColumn[ncols];
for (int i=0;i<ncols;i++){
columns[i] = mainColumnModel.getColumn(i);
}
mainColumnModel = mainTable.getColumnModel();
fixedColumnModel = fixedTable.getColumnModel();
mainTableHeader = mainTable.getTableHeader();
mainTableHeader.addMouseListener( new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent me){
if (SwingUtilities.isRightMouseButton(me)){
if (ncols - counter>1){
counter ++;
int col = mainTable.columnAtPoint(me.getPoint());
TableColumn column = mainColumnModel.getColumn(col);
mainColumnModel.removeColumn(column);
fixedTable.getColumnModel().addColumn(column);
scrollPane.setRowHeaderView(fixedTable);
scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, fixedTable.getTableHeader());
}
}
}
});
fixedTable.getTableHeader().addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me){
if (SwingUtilities.isRightMouseButton(me) && me.getClickCount()== 2 ){
while (mainColumnModel.getColumnCount() > 0){
mainColumnModel.removeColumn(mainColumnModel.getColumn(0));
}
while (fixedColumnModel.getColumnCount() > 0){
fixedColumnModel.removeColumn(fixedColumnModel.getColumn(0));
}
for(int i=0;i<ncols;i++){
mainColumnModel.addColumn(columns[i]);
}
scrollPane.setRowHeaderView(null);
}
}
});
scrollPane.setViewportView(mainTable);
add(scrollPane, BorderLayout.CENTER);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Prova().setVisible(true);
}
});
}
}
A few pointers when posting a SSCCE:
for (int i=0;i<ncols;i++){
Don't be afraid to use whitespace in you code to make it more readable be separating the 3 statements of the for statement.
for (int i = 0; i < ncols; i++){
Keep the code simple and directly related to the problem:
TableModel mainTableModel = new EmployeeTableModel(listEmployees);
You question is about "moving columns", not about the data in the table so there is no need for a special TableModel and the Employee class. Just use the DefaultTableModel:
TableModel mainTableModel = new DefaultTableModel(5, 10);
Your current code won't compile because you didn't include the Employee class. By using JDK classes the code is smaller and easier to read.
The problem is when you unlock all the locked columns and you want to start to lock again, doesnt work
Your looping code is wrong. I didn't bother to figure out what was wrong. Instead I made the code simpler:
//for(int i=0;i<(ncols-counter);i++){
while (mainColumnModel.getColumnCount() > 0)
{
mainColumnModel.removeColumn(mainColumnModel.getColumn(0));
}
//for(int i=0;i<counter;i++){
while (fixedColumnModel.getColumnCount() > 0)
{
fixedColumnModel.removeColumn(fixedColumnModel.getColumn(0));
}
Another problem is your fixed table doesn't have a header so you don't know what the columns are. This is fixed by using:
scrollPane.setRowHeaderView(fixedTable);
scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, fixedTable.getTableHeader());
Now that you have a header you need to add the MouseListener to the header, not the scrollpane:
//scrollPane.addMouseListener(new MouseAdapter() {
fixedTable.getTableHeader().addMouseListener(new MouseAdapter() {
Edit:
You have a similar problem to what I fixed above. That is don't keep using variable to track values when you can use the component itself.
if (ncols - counter>1){
You never reset the value of the counter so the if condition won't be true the second time.
As I did above just use the value from the column model:
//if (ncols - counter>1){
if (mainColumnModel.getColumnCount() > 1) {
This is just basic problem solving. Put a display statement in the block of code to see if it executes when you have problems.

How to create dynamically JButton value in DesignGridLayout library?

i create grid layout using DesignGridLayout java library (here).
in the sampe if create 3 column layout. using this code :
layout.row().add(new JButton("Button 1")).add(new JButton("Button 2")).add(new JButton("Button 3"));
or using method that return object :
layout.row().add(button()).add(button()).add(button());
...
...
public JButton button() {
return new JButton("Button");
}
The question is, how to create dynamically JButton value? May be name,icon or anything?
I already try my own code like this :
for (int i=0; i<4; i++) {
JButton button = new JButton();
layout.row().add(button).add(button).add(button);
}
it return :
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Do not add the same component twice
My purpose for different value in each component which added in panel is, i want to create gallery that populate
different image, and i load that images using looping, like this :
for(int i=0; i<files.length; i++) {
...
ImageIcon imgSource = new ImageIcon(new File(myPath));
JLabel labelGallery = new JLabel(imgSource);
...
}
Any solution?
Thanks before :)
In your example,
layout.row().add(button).add(button).add(button);
has the effect of attempting to add the same JButton instance to the row repeatedly.
In the example cited,
layout.row().grid().add(button()).add(button());
invokes an auxiliary method, button(), to create a new instance each time it appears:
public static JButton button() {
return new JButton("Button");
}
As mentioned by #trashgod, Swing does not allow to add the same component twice to a panel.
If you want to add several components, created within a loop, to the same row, you can do it as follows:
IRow row = layout.row().grid();
for (int i = 0; i < n; i++) {
JButton button = createButton(i);
row.add(button);
}
That will create only one row with n buttons inside.

Object with better efficiency than JTable

I would like to create visualization of database. It's a full-desktop application, and it looks similar to Excel. When i put into my JTable database visualization 100 rows, each one with 6 columns, the application is crushing down. Is there a better class for such a task? Or some other smarter way?
Thats the way i do it:
import PodklasyInterfejsu.Menu;
import javax.swing.*;
import java.awt.*;
public class OknoGlowne extends JFrame
{
public Okno()
{
// ustawienie rozmiaru okna na 100%
JFrame Okno = new JFrame();
Okno.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Okno.setTitle("Archiwum Stomatologiczne");
Toolkit zestaw = Toolkit.getDefaultToolkit();
Dimension rozmiarEkranu = zestaw.getScreenSize();
int wysEkranu = rozmiarEkranu.height;
int szerEkranu = rozmiarEkranu.width;
Okno.setSize(szerEkranu, wysEkranu - 60);
Container powZawartosci = getContentPane();
// Panel Górnego Menu:
Menu GorneMenu = new Menu();
Okno.setJMenuBar(GorneMenu);
// Wizualizacja bazy w tabeli:
JTable tabela = new JTable(komorki, nazwyKolumn);
tabela.setAutoCreateRowSorter(true);
Okno.add(new JScrollPane(tabela), BorderLayout.CENTER);
Okno.setVisible(true);
}
private Object[][] komorki = new Object [10][];
private String[] nazwyKolumn = {"Nazwisko", "Imię", "Pesel", "Płeć", "Data urodzenia", "Adres", "Kontakt"};
}
One problem in the code above is that your data 2-d Object[10][] array, komorki, doesn't match your column String[] array, nazwyKolumn. You have 7 columns and need 7 as the first array index for your Object array. Consider changing this:
private Object[][] komorki = new Object[10][];
private String[] nazwyKolumn = { "Nazwisko", "Imię", "Pesel", "Płeć",
"Data urodzenia", "Adres", "Kontakt" };
to this:
// !! private Object[][] komorki = new Object[10][];
private Object[][] komorki = new Object[10][7]; //!!
private String[] nazwyKolumn = { "Nazwisko", "Imię", "Pesel", "Płeć",
"Data urodzenia", "Adres", "Kontakt" };
for starters.
As mentioned by others: have you tried profiling ? I personally have good experiences with JProfiler.
Although we do not yet know whether the JTable is the actual problem, I had performance problems with JXTables (note: the SwingX version of JTable) in combination with large TableModels where the table would iterate over all elements to determine the column size before painting it.
This was solved by setting a prototype value for each column (using TableColumnExt#setPrototypeValue). I am not sure whether a regular JTable contains this logic as well, but it might be worth a try to replace your JTable by a JXTable and set the prototype.

JScrollBar visible

Is there some way to know if a JScrollBar is visible or not inside a JPanel?
I mean, some times, my panel has many rectangles (think of it as buttons) and needs a scrollbar and some times it doesn't need it. I'd like to know if I can know when it is being shown.
If you extend the JPanel and add yourself the JScrollbars (horizontal and/or vertical), then you can control when they must be visible or invisible
(you can check if they are currently visible with the isvisible() function)
You can find two example of such classes that determine the need for visible scrollbar depending on their content:
JGraphPanel (its callback actionPerformed(Event e) will adjust the visibility based on a zoom factor)
Plane (its function adjustComponents() will call setVisible() on the JScrollBar if needed)
Assuming you have a reference to a JScrollPane, you should be able to just call
yourJScrollPane.getHorizontalScrollBar().isVisible()
or
yourJScrollPane.getVerticalScrollBar().isVisible()
If you need also to be notified about visibility changes than you can use a code as follows:
final JScrollPane scroll = new JScrollPane(createMyPanel());
scroll.getVerticalScrollBar().addHierarchyListener(new HierarchyListener() {
#Override
public void hierarchyChanged(HierarchyEvent e) {
if (e.getID() == HierarchyEvent.HIERARCHY_CHANGED &&
(e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
System.out.println(scroll.getVerticalScrollBar().isVisible());
}
}
});
Further to the answers by VonC and Joshua, it's worth noting that isVisible() is a method on the super class Component. Also, the javadoc states:
Determines whether this component should be visible when its parent is visible.
Components are initially visible, with the exception of top level components such as Frame objects.
What this means is that until the JScrollPane is added to a sized frame, calling isVisible() on the JScrollBar will always return true.
Consider the following SSCCE:
public static void main(String[] args) {
// creates a small table in a larger scroll pane
int size = 5;
JTable table = new JTable(makeData(size), makeHeadings(size));
JScrollPane pane = new JScrollPane(table);
pane.setPreferredSize(new Dimension(200, 200));
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints true
JFrame frame = new JFrame("JScrollPane Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(pane);
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints true
frame.pack();
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints false
frame.setVisible(true);
System.out.println(pane.getVerticalScrollBar().isVisible()); // prints false
}
private static Object[] makeHeadings(int size) {
Object[] headings = new Object[size];
for (int i=0; i<size; i++){
headings[i] = i;
}
return headings;
}
private static Object[][] makeData(int size) {
Object[][] data = new Object[size][size];
for (int i=0; i<size; i++){
for (int j=0; j<size; j++){
data[i][j] = i*j;
}
}
return data;
}
Similarly, it's worth adding that if you're adding the JScrollPane to an internal frame, then scrollBar.isVisible() will only work once the internal frame has been added to another component.

Categories