I needed a UI that collects similar pieces of data over and over again which can best be described as a batch entry.To input the batch, i needed a scrollpane that adjusts to whatever size of data the user is inputting. To do this smoothly,I relied on the jtable attached to a jscrollpane, and the row to be a jpanel. I am facing some troubles with this solution, that is
I dont know how to access the textfields in my jpanel
I dont know how to add more jpanels as rows on my jtable.
I may add that i jumped to this solution(which may not necessarily be the best) after trying to add many jpanel to one jscrollpane over and over again but this did not work well.This alternative failed as it seems adding a jpanel through the setviewportview was not designed to accept a new panel to expand the view.A variant of the same is one jpanel attached to one scrollpane, then add many jpanels on the mainpanel attached to the scrollpane but the view did not expand and the scrollpane stayed the same.I have checked out oracle's scrollpane tutorialand seen the dynamic changing of a client and how to revalidate the client but am not sure how this will apply in my case where am adding a new jpanels(unless i add them on one jpanel which i keep setting a new clients preferred size as i add a new jpanel.)
NB:
The jpanel i keep adding is not really new as i am iterating it.
Another third solution i had tried is using a flexible gridlayout which its row is a variable 'x' which the user can choose and the a number of 'x' jpanels can be added, problem was,and this is the main problem, the scrollpane was not gracefull to allow expansion so neither did it adjust to a new view.
Note that the scrollpane i need should not be attached to the JFrame but to a an inner view (like a scrollpane for North or Center only in a borderLayout UI) within the JFrame.
I know that the jpanel as a row can be replaced by a normal row where each cell on the row can act either as a jtextfield,jlabel or whatever component i need, but this approach was taken for a particular need to make the JTable look like an actual physical document.
How do I make a good scrollpane that expands dynamically to addition of jpanels ?
The code below shows two attempts of adding jpanels in the hope of expanding the scrollpane but both fail.
Solution one renders each cell as a jpanel but i cannot access the textfield on the jpanel thus i cannot get the data from the user and i cannot also add another row to create a batch.
public class JTablePanelRow extends JFrame implements ActionListener{
JButton addRow = new JButton("Add Row");
JTable table = new JTable();
JScrollPane spane = new JScrollPane();
JPanel mainPanel = new JPanel(new BorderLayout());
JPanel panelRow = new JPanel();
JLabel lblName = new JLabel("NAME");
JTextField txName = new JTextField();
JLabel lblAge = new JLabel("AGE");
JTextField txAge = new JTextField();
TblModel tblmodel = new TblModel();
public static void main(String[] args) {
JTablePanelRow tblPane = new JTablePanelRow();
tblPane.init();
}
void init(){
panelRow.setLayout(new GridLayout(2,2,4,4));
panelRow.add(lblName);
panelRow.add(txName);
panelRow.add(lblAge);
panelRow.add(txAge);
table.setModel(tblmodel);
table.setRowHeight(50);
table.getColumn("A").setCellRenderer(new PanelRenderer(panelRow));
spane.setViewportView(table);
addRow.addActionListener(this);
mainPanel.add(addRow,"North");
mainPanel.add(spane,"Center");
this.getContentPane().add(mainPanel);
this.setVisible(true);
this.setSize(new Dimension(400,500));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==addRow){
tblmodel.addNewRow();
}
}
}
class TblModel extends AbstractTableModel{
#Override
public int getRowCount() {
return 1;
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return "";
}
public void addNewRow(){
//how to add the jpanel again
}
}
class PanelRenderer extends DefaultTableCellRenderer{
JPanel entryPn = new JPanel();
public PanelRenderer(JPanel entryPn){
this.entryPn = entryPn;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
entryPn.setPreferredSize(new Dimension(200,50));
entryPn.setBackground(Color.pink);
return entryPn;}
}
Solution two tries to add jpanels through a flexible gridlayout but the jpanels are not added or not scrollable.
public class GridLayoutTrick extends JFrame{
JPanel mainPanel = new JPanel(new BorderLayout());
JScrollPane scroll = new JScrollPane();
JPanel entryPanel = new JPanel();
JPanel rowPanel = new JPanel();
int batchNumber = 10;
JPanel northPanel = new JPanel();
public static void main(String[] args) {
GridLayoutTrick glt = new GridLayoutTrick();
glt.init();
}
void init(){
rowPanel.setBackground(Color.PINK);
rowPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK,5));
rowPanel.setPreferredSize(new Dimension(100,50));
entryPanel.setLayout(new GridLayout(batchNumber,1,4,4));
for(int i = 0;i < batchNumber;i++){
entryPanel.add(rowPanel);
}
entryPanel.setBackground(Color.BLUE);
scroll.setViewportView(entryPanel);
mainPanel.add(northPanel,"North");
mainPanel.add(scroll,"Center");
this.getContentPane().add(mainPanel);
this.setVisible(true);
this.setSize(new Dimension(400,400));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Solution two tries to add jpanels through a flexible gridlayout but the jpanels are not added or not scrollable.
for(int i = 0;i < batchNumber;i++){
entryPanel.add(rowPanel);
}
You only have 1 "rowPanel".
You need to create a separate instance of the "rowPanel" every time you want to add it the the "entryPanel".
mainPanel.add(northPanel,"North");
mainPanel.add(scroll,"Center");
Don't use magic constants. Use the fields from the API:
BorderLayout.PAGE_START
BorderLayout.CENTER
Although I really think the suggestions by MadProgrammer are a better solution.
I'm left scratching my head why you wouldn't just use the editable capabilities of the JTable alone, you're just making life more difficult for yourself by trying to use a JPanel as the cells renderer/editor
You should start by taking a closer look at How to use tables to get a better understanding of how tables are suppose to be used
The following is an overly simplified example which allows you to add any number of rows and edit the values at each cell in the table...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private DefaultTableModel model;
public TestPane() {
setLayout(new BorderLayout());
model = new DefaultTableModel(new Object[]{"Name", "Age"}, 0);
JTable table = new JTable(model);
add(new JScrollPane(table));
JButton btn = new JButton("Add");
add(btn, BorderLayout.SOUTH);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.addRow(new Object[] {"", ""});
table.editCellAt(model.getRowCount() - 1, 0);
table.getEditorComponent().requestFocusInWindow();
}
});
}
}
}
Personally, I'd set up a container object to hold the data and manage it through a custom TableModel, but that's me.
I'd also look at implementing a continuous editing model to allow the user to move easily through the table in a "continuous" editing mode
Related
I have sub-classed JPanel to provide a generic JPanel container that contains options for a filter selected from a JComboBox.
When the JComboBox is changed from one filter to another, I have a switch statement that checks which filter is now selected and reassigns the "options" JPanel to a new instance of the options class associated with that filter:
public void setFilterOptions(String choice){
switch(choice){
case "Gaussian": options = new GaussianFilterOptions();break;
case "Sobel": options = new SobelFilterOptions();System.out.println("?");break;
}
}
The problem is that the JPanel "options" does not get refreshed in the GUI after setFilterOptions is called. Whichever filter is set to show by default appears upon startup and remains even if I switch the JComboBox selection. I have tried repainting, revalidating, and validating "options" as well as the JPanel containing "options" and the JFrame enclosing the entire application.
I added print statements in each case to verify that they were working when the combo box is switched and not falling through, so I'm sure that is not the problem.
You're confusing variable with object. You have likely originally placed a JPanel object that options referred to into your GUI, but understand, you didn't place the options variable into the GUI, but rather (and again) the JPanel object that it referred to into the GUI.
If later you change the JPanel that the options variable refers to, this will have no effect on the GUI, since it still holds the same original JPanel object that it held before. If you want to change the JPanel displayed, you have to do that directly by swapping out JPanels in the GUI. This is best accomplished by using a CardLayout.
e.g.,
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SwapPanels extends JPanel {
private static final String GAUSSIAN = "Gaussian";
private static final String SOBEL = "Sobel";
private static final String[] FILTER_OPTIONS = {GAUSSIAN, SOBEL};
private CardLayout cardLayout = new CardLayout();
private JPanel cardHolderPanel = new JPanel(cardLayout);
private JPanel gaussianPanel = new JPanel();
private JPanel sobelPanel = new JPanel();
private JComboBox<String> filterCombo = new JComboBox<>(FILTER_OPTIONS);
public SwapPanels() {
JPanel comboPanel = new JPanel();
comboPanel.add(filterCombo);
filterCombo.addActionListener(new ComboListener());
gaussianPanel.add(new JLabel("Gaussian Filtering Done Here"));
sobelPanel.add(new JLabel("Sobel Filtering Done Here"));
cardHolderPanel.add(gaussianPanel, GAUSSIAN);
cardHolderPanel.add(sobelPanel, SOBEL);
int gap = 50;
cardHolderPanel.setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout());
add(cardHolderPanel, BorderLayout.CENTER);
add(comboPanel, BorderLayout.PAGE_END);
}
private class ComboListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
String key = (String) filterCombo.getSelectedItem();
cardLayout.show(cardHolderPanel, key);
}
}
private static void createAndShowGui() {
SwapPanels mainPanel = new SwapPanels();
JFrame frame = new JFrame("SwapPanels");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Also you could do it like this instead of the switch
public void setFilterOptions(String choice){
options = (choice.equals("Gaussian"))? new GaussianFilterOptions():
new SobelFilterOptions();
}
}
I have created my own version of a panel so I can create some dragable tables but at the moment nothing is being added on to the panel I have created
panel class:
import javax.swing.*;
import java.awt.event.*;
import java.awt.Dimension;
import java.awt.Point;
public class Workspace extends JPanel implements MouseListener,MouseMotionListener{
private JTable t;
private DatabaseHandler d;
public Workspace(DatabaseHandler d ){
super();
this.d = d;
setPreferredSize(new Dimension(1000, 1000));
this.setLayout(null);
addMouseListener(this);
addMouseMotionListener(this);
}
public void load(String table){
t = new JTable(d.getTable(table));
//JScrollPane js=new JScrollPane(t);
this.add(t);
}
}
the code which calls it:
public class Display{
private JPanel leftPanel = new JPanel(new BorderLayout());
public JList list;
public JFrame frame;
private DatabaseHandler d = new DatabaseHandler("imdb");
private Workspace w = new Workspace(d);
public Display(){
//create the window
frame = new JFrame("FYP - Database Refactoring");
frame.getContentPane().add(w, BorderLayout.CENTER);
frame.setSize(1000,1000);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
list = new JList(d.getTableNames());
list.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
JList l = (JList)e.getSource();
w.load((String)l.getSelectedValue());
frame.setVisible(true);
}
});
leftPanel.add(list);
JLabel l = new JLabel("workbench");
w.add(l);
frame.getContentPane().add(leftPanel, BorderLayout.LINE_START);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Display();
}
}
Any help would be appricated
Avoid null layouts, and in fact this one is messing you up, since with null layouts, you must fully specify call component sizes and locations. Just don't do it.
Don't re-add JTables in the MouseListener. Rather give your JPanel a JTable, and change its model from the MouseListener. Otherwise you're adding multiple JTables which doesn't make sense.
Put your JTable into a JScrollPane
And best to have your JPanel use BorderLayout and put the JScrollPane into the BorderLayout.CENTER position.
so I can create some dragable tables
If you need the ability to drag a JTable I would suggest that you should be using a JDesktopPane with JInternalFrames. You can easily drag an internal frame around the desktop. Then you just add the JTable\JScrollPane to the internal frame like you would to a normal JFrame.
Read the section from the Swing tutorial on How to Use Internal Frames for more information and working examples.
When you choose a state, the frame's content pane removes its components. Then depending on the state you chose, another class takes the content pane and adds onto it. After doing so, the frame gets packed to resize accordingly.
I want free control over whats in the Frame, such as being able to put panels side by side, above one another, ect.. so I really don't want to use CardLayout. (I'd much rather have 1 panel handle both loginscreen and chat. Then, be able to display another panel next to that one).
I'm using the JFrame's content pane for my login and chat screen, but when I run my code, I get a small frame (has SOME size, but hardly any) that's white on the inside.
show frame
switch to chat
remove everything on pane (currently nothing)
add components onto pane
pack frame so it can size accordingly to the pane
revalidate if needed (not sure when I need to revalidate or not)
Please tell me what I'm doing wrong, and maybe guide me in the right direction.
PS: There are no errors
EDIT: The only thing I can think of is that since I'm passing frame.getContentPane() through the method, and methods are pass-by-value, the actual reference to frame.getContentPane() might not be noticing the changes I'm asking for. But then I don't know why the inside of the frame would be white (as if my JTextArea tried rendering), and there's padding on the inside of the frame, so there has to be something happening..
Main.java:
package main;
import ui.Frame;
public class Main {
public static Frame frame = new Frame();
public static void main(String[] args) {
frame.show();
frame.switchState(State.chat);
}
public static enum State {
login, chat;
}
}
Frame.java:
package ui;
import main.Main.State;
import javax.swing.JFrame;
public class Frame {
private Panel currentpanel; //from package ui, not AWT
private ChatPanel chatpanel = new ChatPanel();
private JFrame frame;
public Frame() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
public void show() {
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void switchState(State state) {
frame.removeAll();
switch(state) {
case chat:
currentpanel = chatpanel;
currentpanel.addComponentsTo(frame.getContentPane());
break;
}
frame.pack();
frame.revalidate();
}
}
Panel.java:
package ui;
import java.awt.Container;
public interface Panel {
public void addComponentsTo(Container pane);
}
ChatPanel.java:
package ui;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JTextArea;
public class ChatPanel implements Panel {
private JTextArea toparea = new JTextArea();
private JTextArea bottomarea = new JTextArea();
#Override
public void addComponentsTo(Container pane) {
pane.setPreferredSize(new Dimension(500, 500));
pane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
gbc.gridx = 0;
gbc.ipadx = 450;
gbc.ipady = 350;
pane.add(toparea, gbc);
gbc.gridy = 1;
gbc.ipady = 100;
pane.add(bottomarea);
}
}
I know that can be quite frustrating.
have you tried calling
pack(); or repaint();
I found the problem. It was calling frame.removeAll(); before adding anything to it.
When I tried if(frame.getComponents().length > 0), it still triggered removeAll(), but the problem wasn't fixed. Seeing how I haven't added anything yet, I checked to see what the component was (by printing out the object), and it was a JRootPane.
After that, I tried printing out frame.getContentPane().getComponents().length, it gave me 0 as expected.
Long story short: This is how switchPanel(State state) should look:
public void switchState(State state) {
if(frame.getContentPane().getComponents().length > 0)
frame.removeAll();
switch(state) {
case chat:
currentpanel = chatpanel;
currentpanel.addComponentsTo(frame.getContentPane());
break;
}
frame.pack();
frame.revalidate();
}
NOTE: I still recommend CardLayout, but if you insists in dynamically setting the frame's content pane the here it is.
The frame class
public class SwitchingFrame extends JFrame {
public static enum State {ONE, TWO}
private PanelONE panel1 = new PanelONE();
private PanelTWO panel2 = new PanelTWO();
public SwitchingFrame() {
getContentPane().setLayout(new BorderLayout());
pack();
setVisible(true);
}
public void switchState(State state) {
setVisible(false);
getContentPane().removeAll();
if (state.equals(State.ONE))
getContentPane().add(panel1, BorderLayout.CENTER);
else
getContentPane().add(panel2, BorderLayout.CENTER);
pack();
setVisible(true);
}
}
The two panel classes which are switched
public class PanelONE extends JPanel {
public PanelONE() {
add(new JLabel("ONE"));
}
}
public class PanelONE extends JPanel {
public PanelTWO() {
add(new JLabel("TWO"));
}
}
The main method which includes buttons to simulate changing the panels
public class TestSwitchingFrame {
public static void main(String[] args) {
final SwitchingFrame sframe = new SwitchingFrame();
JFrame frame = new JFrame();
JButton b1 = new JButton("ONE");
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sframe.switchState(SwitchingFrame.State.ONE);
}
});
JButton b2 = new JButton("TWO");
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sframe.switchState(SwitchingFrame.State.TWO);
}
});
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(b1);
frame.getContentPane().add(b2);
frame.pack();
frame.setVisible(true);
}
}
You do not need (not should) write your own interface (Panel). Your two panels should extend JPanel and set within the frames content pane. Your frame should extend JFrame and does not need to override its show method (let Swing do it for you). The specific implementation of the switchState function should eventually depend on the end result you want. There are similar ways to accomplish almost the same result.
I've looked around a while and also played around trying to add multiple panels to a JTabbedPane.
My question is: Is it possible to add the same Jpanel to multiple TabbedPanes. Everything way that I tried, it doesn't seem to work correctly. This is how it it works.
public MainGUI() {
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
getContentPane().add(tabbedPane, BorderLayout.CENTER);
JEditorPane instructionalEditorPane = new JEditorPane();
tabbedPane.addTab("Instructional", instructionalEditorPane);
JPanel codePanel = new JPanel();
JPanel drawPanel = new JPanel();
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, codePanel, drawPanel);
splitPane.setResizeWeight(0.75);
tabbedPane.addTab("Code Panel", splitPane);
JEditorPane unifiedInstPane = new JEditorPane();
JPanel unifiedCodePanel = new JPanel();
JPanel unifiedDrawPanel = new JPanel();
JSplitPane unifiedSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, unifiedCodePanel, unifiedDrawPanel);
unifiedSplitPane.setResizeWeight(0.75);
JSplitPane unifiedPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT,unifiedInstPane, unifiedSplitPane);
unifiedPanel.setResizeWeight(0.40);
tabbedPane.addTab("Unified Tab", unifiedPanel);
}
What I would like to do is just add the instructionalEditorPane and the splitPane to multiple tabbedPanes but when I do I loose the original Individual tabbedPanes. If I have to I can do it this way but I would then have to write to both the unifiedInstPane & the instructionalEditorPane to keep them updated. I would also have to do this for the 2 splitPanes which have the codePanel and drawPanels embedded. This will make it harder to keep all the panels in sync.
Any suggestions?
"Is it possible to add the same Jpanel to multiple TabbedPanes." -- no. You can only add a component to one container at a time. Your JPanels should share models but use unique components. The model will likely be a non-GUI class of your creation.
For example, here's a very simplistic rendering of my recommendations:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
public class MainGui2 extends JPanel {
private static final int TAB_COUNT = 3;
private JTabbedPane tabbedPane = new JTabbedPane();
private PlainDocument doc = new PlainDocument();
private Action btnAction = new ButtonAction("Button");
public MainGui2() {
for (int i = 0; i < TAB_COUNT; i++) {
tabbedPane.add("Tab " + (i + 1), createPanel(doc, btnAction));
}
setLayout(new BorderLayout());
add(tabbedPane);
}
private JPanel createPanel(PlainDocument doc, Action action) {
JTextArea textArea = new JTextArea(doc);
textArea.setColumns(40);
textArea.setRows(20);
JPanel panel = new JPanel();
panel.add(new JScrollPane(textArea));
panel.add(new JButton(action));
return panel;
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String title) {
super(title);
}
#Override
public void actionPerformed(ActionEvent evt) {
try {
String text = "Button Pressed!\n";
doc.insertString(doc.getLength(), text, null);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MainGui2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MainGui2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Better would be to create a formal model class that gets injected into each view, each tabbed pane's individual panes.
Edit
You state in comment:
Yes I can fix that by making calls to the instances but then I'm back to my original problem of having to make calls to each instance to affect a change in all the panel. Say for example I have a drawing panel and I need to call repaint(), I would have to make a call to 2 different instances to get both tabbedPanes to update. Is there any way around this?
Yes, the solution is to use an MVC, or model-view-control, structure. Your model holds your overall program logic, the views are what the user sees, and the control interacts between the two.
Consider having your model notify either the control or the views that its been changed, and then this stimulates a repaint an all observer views.
I have a situation like this.
I have scrollpane whose viewportView is a JPanel
And that JPanel has the layout as BoxLayout. In this panel I add one class which extends JPanel and that class contains JComponents.
So while running an application, the JComponents are shown in the JScrollPane.
This is how my ScrollPane is formed.
The problem here is, When the data exceeds more than around 750 rows, The scrollbar starts giving problems.
When scrolling up or down by mouse wheel, scroll doesnot move smoothly, It suddenly stops in the middle and again starts, say it has a jerky movement.
my Question is how can i get the smooth mouse movement in this scenario.
My scrollPane is like this
public JScrollPane getScrollPane() {
if (scrollPane == null) {
scrollPane = new JScrollPane();
scrollPane.setSize(new Dimension(1000, 433));
scrollPane.setLocation(new Point(10, 10));
scrollPane.setColumnHeaderView(getHeaderOfRowPanel());
scrollPane
.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setViewportView(getScrollPanel());
scrollPane
.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.getVerticalScrollBar().setUnitIncrement(
unitIncrement);
}
return scrollPane;
}
private JPanel getScrollPanel() {
if (scrollPanel == null) {
scrollPanel = new JPanel();
scrollPanel.setBorder(null);
scrollPanel.setLayout(new BoxLayout(getScrollPanel(),
BoxLayout.Y_AXIS));
}
return scrollPanel;
}
private class RowPanel extends JPanel {
//My components are here ..
//I add this Panel in scrollPanel
}
Have look at JScrollBar.setUnitIncrement, beacuse bunch of JPanels in the JScollPane has un_natural scrolling in compare with JList, JTable or JTextArea
example
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JScrollBarUnitIncrement {
public static void main(String[] args) {
final JFrame f = new JFrame("");
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2000, 1));
for (int i = 0; i != 2000; i++) {
JButton btn = new JButton("Button 2");
panel.add(btn);
}
final JScrollPane sPane = new JScrollPane(panel);
final int increment = 50;
sPane.getVerticalScrollBar().setUnitIncrement(increment);
KeyStroke kUp = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0);
KeyStroke kDown = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0);
sPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(kUp, "actionWhenKeyUp");
sPane.getActionMap().put("actionWhenKeyUp", new AbstractAction("keyUpAction") {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
final JScrollBar bar = sPane.getVerticalScrollBar();
int currentValue = bar.getValue();
bar.setValue(currentValue - increment);
}
});
sPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(kDown, "actionWhenKeyDown");
sPane.getActionMap().put("actionWhenKeyDown", new AbstractAction("keyDownAction") {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
final JScrollBar bar = sPane.getVerticalScrollBar();
int currentValue = bar.getValue();
bar.setValue(currentValue + increment);
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(sPane);
f.pack();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setVisible(true);
}
});
}
private JScrollBarUnitIncrement() {
}
}
It is never good to populate such huge no. of rows in JScrollPane. Because, the visible portion is only around let's say 20 to 30 rows in viewport depending on the height of the scrollpane and the height of your RowPanel. So, why to populate such huge rows at once ? The problem with the smoothness is because there might be exception (see the console ). So, resolve this, I see two options for you. One is to use pagination and another is to allow users to enter some search criteria to filter out the unwanted records.
As #mKorbel notes, both JTable and JList implement Scrollable for convenient scroll increments, and they both use the flyweight pattern for rendering speed. If you can't use either component directly, you can still use the patterns. The tutorial includes Scrollable examples, and there's a CellRendererPane example here.