automatic dynamic expansion / contraction of JTextArea in Java - java

I start off by creating a JTextArea of a specific size. The user can add text within it but it will get cut off if it becomes too long (vertically or horizontally). I want the JTextArea to automatically expand or contract (for deletion of text).
I may allow users to change font and font size in the future, so it would be good if I could avoid making things increase/decrease by a certain size.
I am currently using bounds to size my JTextArea. Perhaps I should size by rows and columns and use a listener and act appropriately?
thanks in advance!

I can't imagine why you'd want to do this, why not put a JTextArea in a JScrollPane, but ok, i'll play along... Maybe something like this:
import java.awt.Container;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
public class Expander extends JFrame {
private final JTextArea area;
private int hSize = 1;
private int vSize = 1;
public Expander() {
Container cp = getContentPane();
cp.setLayout(new BoxLayout(cp, BoxLayout.Y_AXIS));
cp.add(Box.createHorizontalGlue());
area = new JTextArea(vSize, hSize);
cp.add(area);
cp.add(Box.createHorizontalGlue());
area.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
adjust();
}
#Override
public void removeUpdate(DocumentEvent e) {
adjust();
}
#Override
public void changedUpdate(DocumentEvent e) {
adjust();
}
});
pack();
}
private void adjust() {
int maxColumns = getMaxColumns();
if ((area.getLineCount() != vSize) || (maxColumns != hSize)) {
hSize = maxColumns;
vSize = area.getLineCount();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
area.setColumns(hSize);
area.setRows(vSize);
Expander.this.doLayout();
Expander.this.pack();
}
});
}
}
private int getMaxColumns() {
int startOffset = 0;
int maxColumns = 0;
for (int i = 0; i < area.getLineCount(); i++) {
try {
int endOffset = area.getLineEndOffset(i);
int lineSize = endOffset - startOffset;
if (lineSize > maxColumns) {
maxColumns = lineSize;
}
startOffset = endOffset;
} catch (BadLocationException ble) {
}
}
return maxColumns;
}
public static void main(String[] args) {
Expander e = new Expander();
e.setLocationRelativeTo(null);
e.setVisible(true);
}
}

I second the advice to simply put the JTextArea in a JScrollPane and let this take care of extra text. Also and perhaps most importantly, don't set the bounds of the JTextArea because if you do this, you constrain it to be a certain size and that's not what you want to have happen. Instead initialize your JTextArea with two int constants to represent the number of rows and columns that should be visualized and then place it in a JScrollPane. Also be sure to read up on using the layout managers so you can avoid setting the size of your JScrollPane too!
Edit: on testing, it seems that setPreferredSize is more dangerous to a JTextArea than setSize.
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.*;
public class ConstrainedTextArea extends JPanel {
private JTextArea textArea1, textArea2, textArea3;
public ConstrainedTextArea() {
textArea1 = new JTextArea(20, 30);
textArea2 = new JTextArea();
textArea3 = new JTextArea();
textArea2.setPreferredSize(new Dimension(300, 300));
textArea3.setSize(textArea3.getPreferredSize());
setLayout(new GridLayout(1, 0));
add(new JScrollPane(textArea1));
add(new JScrollPane(textArea2));
add(new JScrollPane(textArea3));
}
private static void createAndShowUI() {
JFrame frame = new JFrame("ConstrainedTextArea");
frame.getContentPane().add(new ConstrainedTextArea());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}

also, see GrowingTextAreaExample

Related

Refresh JTextArea in method

I have the following problem:
in my JTextArea I inserted a default string, which must be updated with the new wording once the file is uploaded.
I have the problem that a live refresh of JTextArea is not done, but if I log out and log in I will see the changed string.
public void createWindow()
{
// some code...
JTextArea textArea = new JTextArea(1,1);
String all = "Nothing Infractions";
try {
all = new Scanner (file).useDelimiter("\\A").next();
textArea =new JTextArea(100,1);
} catch (FileNotFoundException e1) {
textArea =new JTextArea(1,1);
}
JScrollPane scroll = new
JScrollPane(textArea,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
textArea.setText(all);
frmUser.getContentPane().add(textArea);
Update:
the text area was thought to have been written without infraction, then passed the program going on randomly and assigning it to each user, the problem and that when assigning them all the logged in user does not automatically update that part of text where no infraction was written.
I Use Java 8
Use revalidate() and repaint().
Here is an MCVE (Minimal, Complete and Verifiable example, see https://stackoverflow.com/help/mcve) from which you cut and paste as needed for your question. The example below is not fundamentally different from your question, but it allows other users on StackOverflow to replicate the problem and pass along suggestions or solutions.
In addition to modifying the question, please indicate which version of Java you are running.
Based on what you have said, you will probably need to implement some type of listener to determine when the contents of the file change -- or does it get created and never changed?
190418 1646Z: Added a refresh button per your last comment. Let me know if you would prefer the window to update without having to click a button.
package javaapplication7;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class FileWatcher extends JFrame {
static final File WATCH_FILE = new File("c:\\temp\\java7.txt");
static final String DELIMITER = "\n";
private JPanel panel = new JPanel();
private JTextArea textArea = new JTextArea(20, 20);
public FileWatcher() {
JFrame frame = new JFrame();
frame.setSize(600, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setTitle("File Watcher");
frame.add(createPanel());
frame.pack();
}
private JPanel createPanel() {
// some code...
JPanel tempPanel = getPanel();
GridBagConstraints gbc = new GridBagConstraints();
tempPanel.setLayout(new GridBagLayout());
JButton button = new JButton("Refresh");
button.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
getUpdatedText();
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
gbc.anchor = GridBagConstraints.NORTH;
getPanel().add(button, gbc);
getTextArea().setFont(new Font("Verdana", Font.BOLD, 16));
getTextArea().setBorder(BorderFactory.createEtchedBorder());
getTextArea().setLineWrap(true);
getTextArea().setWrapStyleWord(true);
getTextArea().setOpaque(true);
getTextArea().setVisible(true);
getUpdatedText();
JScrollPane scroll = new JScrollPane(getTextArea(), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setBorder(BorderFactory.createLineBorder(Color.blue));
scroll.setVisible(true);
// frmUser.getContentPane().add(textArea);
gbc.gridy = 1;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
tempPanel.add(scroll, gbc);
return tempPanel;
}
public void getUpdatedText() {
String all = new String();
try (Scanner scanner = new Scanner(WATCH_FILE).useDelimiter(DELIMITER)) {
while (scanner.hasNext()) {
all = all.concat(scanner.next()).concat(DELIMITER);
}
} catch (FileNotFoundException ex) {
// swallow, next line covers it
}
if (all.isEmpty()) {
all = "No Infractions";
}
getTextArea().setText(all);
}
public JPanel getPanel() {
return panel;
}
public void setPanel(JPanel panel) {
this.panel = panel;
}
public JTextArea getTextArea() {
return textArea;
}
public void setTextArea(JTextArea textArea) {
this.textArea = textArea;
}
public static void main(String[] args) {
FileWatcher javaApplication7 = new FileWatcher();
}
}

label unclear text when change its text

unfortunately I can't handle the change of txt when the button is clicked, I try to write a txt and overtime that I click the button, this txt value should change and allotting seems right, the only problem is that the printed number is not obvious and it seems some part of previous txt remains with it.
package berGenerator;
import java.awt.EventQueue;
public class sscce {
private JFrame frame;
private final Action action = new SwingAction();
private static int i = 555;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
sscce window = new sscce();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public sscce() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 550, 401);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JButton Next = new JButton("Next");
Next.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
Next.setAction(action);
Next.setBounds(167, 290, 198, 64);
frame.getContentPane().add(Next);
}
private class SwingAction extends AbstractAction {
public SwingAction() {
putValue(NAME, "Next Timeslot/scheduler");
putValue(SHORT_DESCRIPTION, "Some short description");
}
public void actionPerformed(ActionEvent e) {
i = i+1;
frame.getContentPane().validate();
frame.getContentPane().repaint();
String from = String.valueOf(i);
System.out.println("sw is: "+from);
JTextArea TextArea11 = new JTextArea("");
TextArea11.setText(from);
TextArea11.setForeground(Color.GREEN);
TextArea11.setBounds(6, 66, 87, 16);
frame.getContentPane().add(TextArea11);
}
}
}
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify.
Layout managers are fundamental to the Swing API, you should make the time to learn how to use them, they will solve more problems than you think they create.
See Laying Out Components Within a Container for more details.
You're creating multiple instances of JTextArea and adding to the frame, but you're not removing any, you're running into a potential z-ordering problem at best and a major resource management issue at worst.
Instead, simply create a single instance of JTextArea, add it to the frame (just like you did your button) and simply update it when the Action is triggered, for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import static javax.swing.Action.NAME;
import static javax.swing.Action.SHORT_DESCRIPTION;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Test {
private JFrame frame;
private final Action action = new SwingAction();
private static int i = 555;
private JTextArea textArea;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Test window = new Test();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Test() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textArea = new JTextArea(10, 20);
textArea.setText(String.valueOf(i));
frame.add(new JScrollPane(textArea));
JButton next = new JButton("Next");
next.setAction(action);
frame.getContentPane().add(next, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
}
private class SwingAction extends AbstractAction {
public SwingAction() {
putValue(NAME, "Next Timeslot/scheduler");
putValue(SHORT_DESCRIPTION, "Some short description");
}
public void actionPerformed(ActionEvent e) {
i = i + 1;
String from = String.valueOf(i);
System.out.println("sw is: " + from);
textArea.setText(from);
textArea.setForeground(Color.GREEN);
}
}
}

Add multiple instances of slider component to Jpanel

Working on mixer app with multiple sliders. I want to create multiple instances of the same slider side by side for as many channels as I determine (1-16). This will very so I want to just create new side by side instances of VolumeControl from left to right. For now I just they can all work from the same inputs, listeners, etc. I'm just working on getting them to display. Here's what I have in order to create a single instance of JSlider (mostly taken from a demo). NOTE: I'm coming from Android development so I could be going the complete wrong, complicated way!
public class VolumeControl extends JPanel implements ChangeListener {
...
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(VolumeControl::createAndShowGUI);
}
public VolumeControl () {
super(new BorderLayout());
...
JSlider volumeControl = new JSlider(JSlider.VERTICAL,
VOLUME_MIN, VOLUME_MAX, currentVolume);
volumeControl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
add(volumeControl, BorderLayout.LINE_START);
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Volume Control");
VolumeControl animator = new VolumeControl();
frame.add(animator, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Simply create one or more factory methods which you can use to create and configure the sliders the way you want...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
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 VolmeControlPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class VolmeControlPane extends JPanel {
protected static final int VOLUME_MIN = 0;
protected static final int VOLUME_MAX = 100;
protected static final String SLIDER_CONTROL_KEY = "Slider.controlKey";
public VolmeControlPane() {
setLayout(new GridBagLayout());
JSlider[] sliders = makeSliders(16);
for (JSlider slider : sliders) {
add(slider);
}
}
public JSlider[] makeSliders(int count) {
JSlider[] sliders = new JSlider[count];
for (int index = 0; index < count; index++) {
sliders[index] = makeSlider(VOLUME_MIN, VOLUME_MAX, VOLUME_MIN + (VOLUME_MAX / 2));
sliders[index].putClientProperty(SLIDER_CONTROL_KEY, index);
}
return sliders;
}
public JSlider makeSlider(int min, int max, int value) {
JSlider volumeControl = new JSlider(JSlider.VERTICAL,
min, max, value);
volumeControl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
return volumeControl;
}
}
}
One little trick I added was using putClientProperty to provide a key to identify the slider, you could also use setName. The idea here is when you attach a listener, you can interegate the slider for the key and identify which channel it represents, as a an idea

JTable model listener detects inserted rows too soon (before they are drawn)

I have a JTable which can have rows dynamically added by the user. It sits in a JScrollPane, so as the number of rows gets large enough, the scroller becomes active. My desire is that when the user adds a new row, the scroller moves all the way to the bottom, so that the new row is visible in the scrollpane. I'm currently (SSCCE below) trying to use a table model listener to detect when the row is inserted, and force the scrollbar all the way down when the detection is made. However, it seems this detection is "too early," as the model has updated but the new row has not actually been painted yet, so what happens is the scroller moves all the way to the bottom just before the new row is inserted, and then the new row is inserted just below the end of the pane (out of visibility).
Obviously this approach is wrong somehow. What is the correct approach?
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class TableListenerTest {
private JFrame frame;
private JScrollPane scrollPane;
private JTable table;
private DefaultTableModel tableModel;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TableListenerTest window = new TableListenerTest();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TableListenerTest() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
JSplitPane splitPane = new JSplitPane();
frame.getContentPane().add(splitPane);
scrollPane = new JScrollPane();
scrollPane.setPreferredSize(new Dimension(100, 2));
splitPane.setLeftComponent(scrollPane);
tableModel = new DefaultTableModel(new Object[]{"Stuff"},0);
table = new JTable(tableModel);
scrollPane.setViewportView(table);
table.getModel().addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
JScrollBar scrollBar = scrollPane.getVerticalScrollBar();
scrollBar.setValue(scrollBar.getMaximum());
}
}
});
JButton btnAddRow = new JButton("Add Row");
btnAddRow.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
tableModel.addRow(new Object[]{"new row"});
}
});
splitPane.setRightComponent(btnAddRow);
}
}
EDIT: Updated SSCCE below based on trashgod's request. This version still does not work, however, if I move the scrolling logic from the table model listener to the button listener as he did, then it does work!
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class TableListenerTest {
private JFrame frame;
private JScrollPane scrollPane;
private JTable table;
private DefaultTableModel tableModel;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TableListenerTest window = new TableListenerTest();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TableListenerTest() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));
JSplitPane splitPane = new JSplitPane();
frame.getContentPane().add(splitPane);
scrollPane = new JScrollPane();
scrollPane.setPreferredSize(new Dimension(100, 2));
splitPane.setLeftComponent(scrollPane);
tableModel = new DefaultTableModel(new Object[]{"Stuff"},0);
table = new JTable(tableModel);
scrollPane.setViewportView(table);
table.getModel().addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
int last = table.getModel().getRowCount() - 1;
Rectangle r = table.getCellRect(last, 0, true);
table.scrollRectToVisible(r);
}
}
});
JButton btnAddRow = new JButton("Add Row");
btnAddRow.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
tableModel.addRow(new Object[]{"new row"});
}
});
splitPane.setRightComponent(btnAddRow);
}
}
This example uses scrollRectToVisible() to (conditionally) scroll to the last cell rectangle. As a feature you can click on the thumb to suspend scrolling and release to resume.
private void scrollToLast() {
if (isAutoScroll) {
int last = table.getModel().getRowCount() - 1;
Rectangle r = table.getCellRect(last, 0, true);
table.scrollRectToVisible(r);
}
}
Addendum: I tried scrollRectToVisible in my SSCCE, and it still exhibits the same problem.
This Action provides both mouse and keyboard control:
JButton btnAddRow = new JButton(new AbstractAction("Add Row") {
#Override
public void actionPerformed(ActionEvent e) {
tableModel.addRow(new Object[]{"new row"});
int last = table.getModel().getRowCount() - 1;
Rectangle r = table.getCellRect(last, 0, true);
table.scrollRectToVisible(r);
}
});
Addendum: Here's a variation on your example that illustrates a revised layout strategy.
/** #see https://stackoverflow.com/a/14429388/230513 */
public class TableListenerTest {
private static final int N = 8;
private JFrame frame;
private JScrollPane scrollPane;
private JTable table;
private DefaultTableModel tableModel;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TableListenerTest window = new TableListenerTest();
window.frame.setVisible(true);
}
});
}
public TableListenerTest() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.X_AXIS));
tableModel = new DefaultTableModel(new Object[]{"Stuff"}, 0);
for (int i = 0; i < N; i++) {
tableModel.addRow(new Object[]{"new row"});
}
table = new JTable(tableModel) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, table.getRowHeight() * N);
}
};
scrollPane = new JScrollPane();
scrollPane.setViewportView(table);
JButton btnAddRow = new JButton(new AbstractAction("Add Row") {
#Override
public void actionPerformed(ActionEvent e) {
tableModel.addRow(new Object[]{"new row"});
int last = table.getModel().getRowCount() - 1;
Rectangle r = table.getCellRect(last, 0, true);
table.scrollRectToVisible(r);
}
});
frame.add(scrollPane);
frame.add(btnAddRow);
frame.pack();
}
}
However, it seems this detection is "too early,"
For emphasis (#trashgod already mentioned it in a comment): that's true - and expected and a fairly general issue in Swing :-)
The table - and any other view with any model, not only data but also selection, adjustment, ... - is listening to the model to update itself. So a custom listener is just yet another listener in the line (with serving sequence undefined). If it wants to do something that depends on the view state it has to make sure to do so after all internal update is ready (in this concrete context, the internal update includes the update of the adjustmentModel of the vertical scrollBar) Postponing the custom processing until the internals are done, is achieved by wrapping SwingUtilities.invokeLater:
TableModelListener l = new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
invokeScroll();
}
}
protected void invokeScroll() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int last = table.getModel().getRowCount() - 1;
Rectangle r = table.getCellRect(last, 0, true);
table.scrollRectToVisible(r);
}
});
}
};
table.getModel().addTableModelListener(l);

Java -- How to set the keyboard scroll speed for a JScrollPane

A JPanel has a JScrollPane that contains yet another JPanel or two. My life depends on increasing the scroll speed using a keyboard's directional arrows. After careful deliberation, the powers that be decided that: sc.getVerticalScrollBar().setUnitIncrement(240); should only be applicable to a mouse, in a clever ruse to elicit minor annoyances amongst java developers. Is there anything that can be done to increase scroll speed? My life hangs in the balance.
You have to use a combination of InputMap.put and ActionMap.put to capture the keyboard events for the components contained on your JScrollPane and process the keyboard events when the JScrollPane has the focus. Since the default increment value for scrolling is 1 you should add or substract the desired increment value to the current value of the scrollbar for JScrollPane which you can get with JScrollPane.getVerticalScrollBar().getValue() and set with JScrollPane.getVerticalScrollBar().setValue(int).
An example of capturing events for the contained elements withing JScrollPane can be done with this code, I've done with buttons, but you get the point (Sorry for the bad organization of the code):
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test
{
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 = 5000;
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")
{
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")
{
public void actionPerformed(ActionEvent e)
{
final JScrollBar bar = sPane.getVerticalScrollBar();
int currentValue = bar.getValue();
bar.setValue(currentValue + increment);
}
}
);
f.add(sPane);
f.pack();
SwingUtilities.invokeLater(
new Runnable()
{
public void run()
{
f.setVisible(true);
}
}
);
}
}
We register to listen and process that event with:
sPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(kUp,"actionWhenKeyUp");
sPane.getActionMap().put("actionWhenKeyUp",
new AbstractAction("keyUpAction")
{
public void actionPerformed(ActionEvent e)
{
final JScrollBar bar = sPane.getVerticalScrollBar();
int currentValue = bar.getValue();
bar.setValue(currentValue - increment);
}
}
);
The key code that perform the value of JScrollBar increment is of the AbstractAction (in this case when the user press the up key).
public void actionPerformed(ActionEvent e)
{
final JScrollBar bar = sPane.getVerticalScrollBar();
int currentValue = bar.getValue();
bar.setValue(currentValue - increment);
}
What you should do is to complete the events when your JScrollPane has the focus, but that should be trivial.
Hope it helps to save your life :P or at least serve you as a starting point.
Probably not what you are looking for but you can use the Mouse Wheel Controller to speed up the scrolling when using a mouse.
My life depends on increasing the scroll speed using a keyboard's directional arrows.
Not sure what how you are getting the scroll pane to scroll when you use the keyboard. I can't get the scroll pane to scroll when I use the keyboard arrows. Post your SSCCE that demonstrates the problem.
Edit:
For my simple test I was just adding a JLabel to the scrollpane. Since a JLabel isn't focusable by default no component in the scrollpane had focus, so the default Actions for the scrollbars where not being invoked. By making the label focusable, the keyboard scrolling works.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.event.MouseInputAdapter;
public class testeSLider extends JFrame {
private JPanel jp;
private JScrollPane sc;
public testeSLider() {
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
setSize(new Dimension(820, 130));
setBackground(Color.BLACK);
jp = new JPanel();
sc = new JScrollPane(jp);
jp.setBackground(Color.GRAY);
sc.setBounds(0, 0, 100, 400);
sc.setBackground(Color.DARK_GRAY);
sc.getHorizontalScrollBar().setPreferredSize(new Dimension(0, 0));
sc.setBounds(50, 30, 300, 50);
getContentPane().add(sc);
int x = 0;
for (int i = 0; i < 50; i++) {
JPanel item = new JPanel();
x = (87 * i) + (i * 10);
item.setBackground(Color.getHSBColor(new Random().nextInt(255),
new Random().nextInt(255), new Random().nextInt(255)));
item.setBounds(x, 5, 0, 0);
item.setPreferredSize(new Dimension(90, 90));
jp.add(item);
addEfeito(item);
}
}
private void addEfeito(JPanel item) {
MouseInputAdapter adapter = new MouseInputAdapter() {
private JPanel panelTmp;
private int deslocamento = 3;
private int mouseStartX;
private int mouseStartY;
#Override
public void mouseDragged(MouseEvent e) {
final JScrollBar bar = sc.getHorizontalScrollBar();
int currentValue = bar.getValue();
bar.setValue(currentValue + (mouseStartX - e.getX()));
}
#Override
public void mouseEntered(MouseEvent e) {
panelTmp = ((JPanel) e.getSource());
panelTmp.setBounds(panelTmp.getX(), panelTmp.getY(),
panelTmp.getWidth() + deslocamento,
panelTmp.getHeight() + deslocamento);
}
#Override
public void mouseExited(MouseEvent e) {
panelTmp = ((JPanel) e.getSource());
panelTmp.setBounds(panelTmp.getX(), panelTmp.getY(),
panelTmp.getWidth() - deslocamento,
panelTmp.getHeight() - deslocamento);
}
#Override
public void mouseClicked(MouseEvent e) {
mouseStartX = e.getX();
mouseStartY = e.getY();
}
#Override
public void mousePressed(MouseEvent e) {
mouseStartX = e.getX();
mouseStartY = e.getY();
}
#Override
public void mouseReleased(MouseEvent e) {
}
};
item.addMouseListener(adapter);
item.addMouseMotionListener(adapter);
}
public static void main(String args[]) {
new testeSLider();
}
}

Categories