How to make JScrollPane (In BorderLayout, containing JPanel) smoothly autoscroll
You can set the value of the horizontal scrollbar to control what is currently visible:
JScrollBar horizontal = scroll.getHorizontalScrollBar();
horizontal.setValue( horizontal.getValue() + ??? );
You would need to use a Swing Timer to schedule the scrolling at an appropriate interval.
Simple example of using a Timer
to scroll text:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class TimerTest extends JPanel implements ActionListener
{
JLabel timeLabel;
JLabel scrollLabel;
public TimerTest()
{
setLayout( new BorderLayout() );
timeLabel = new JLabel( new Date().toString() );
add(timeLabel, BorderLayout.NORTH);
scrollLabel = new JLabel( "Some continuously scrolling text!! " );
add(scrollLabel, BorderLayout.SOUTH);
int time = 1000;
javax.swing.Timer timer = new javax.swing.Timer(time, this);
timer.setInitialDelay(1);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
timeLabel.setText( new Date().toString() );
String oldText = scrollLabel.getText();
// Scroll right to left
String newText = oldText.substring(1) + oldText.substring(0, 1);
// Scroll left to right
// int length = oldText.length();
// String newText = oldText.substring(length-1, length)
// + oldText.substring(0, length-1);
scrollLabel.setText( newText );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new TimerTest() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
How to continuously scroll with JScrollPane
Check out the setAutoScrolls(...)
method of the JComponent class.
You can just use:
panel.setAutoScrolls( true );
And then you use the following MouseMotionListener
:
MouseMotionListener doScrollRectToVisible = new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
((JPanel)e.getSource()).scrollRectToVisible(r);
}
};
panel.addMouseMotionListener(doScrollRectToVisible);
This concept is demonstrated in the ScollDemo
example found in the Swing tutorial on How to Use Scroll Panes.
Java move GUI panel While loop
We meet again!
I managed to implement the moving JPanel
using a Swing timer.
Since you want to move the JPanel
, you need to place it in a parent container that is large enough to allow the JPanel
to be placed in different locations within it. You also need to use a null layout manager because most layout managers will ignore calls to method setLocation()
.
More explanations after the code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.WindowConstants;
public class MovePane implements ActionListener, Runnable {
private static final String START = "Start";
private static final String STOP = "Stop";
private int diff;
private JButton startButton;
private JButton stopButton;
private JFrame frame;
private JPanel jPanel1;
private Timer timer;
public MovePane() {
diff = 10;
}
@Override // jva.awt.event.ActionListener
public void actionPerformed(ActionEvent event) {
String actionCommand = event.getActionCommand();
switch (actionCommand) {
case START:
launchTimer();
break;
case STOP:
stopTimer();
break;
default:
JOptionPane.showMessageDialog(frame,
actionCommand,
"Unhandled",
JOptionPane.WARNING_MESSAGE);
}
}
@Override // java.lang.Runnable
public void run() {
showGui();
}
private JButton createButton(String text, int mnemonic, String tooltip, boolean enabled) {
JButton button = new JButton(text);
button.setMnemonic(mnemonic);
button.setToolTipText(tooltip);
button.setEnabled(enabled);
button.addActionListener(this);
return button;
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
startButton = createButton(START, KeyEvent.VK_M, "Sets panel in motion.", true);
buttonsPanel.add(startButton);
stopButton = createButton(STOP, KeyEvent.VK_P, "Stops panel motion.", false);
buttonsPanel.add(stopButton);
return buttonsPanel;
}
private JPanel createMainPanel() {
JPanel mainPanel = new JPanel(null);
mainPanel.setPreferredSize(new Dimension(400, 400));
jPanel1 = new JPanel();
jPanel1.setBounds(10, 200, 50, 50);
jPanel1.setBorder(BorderFactory.createLineBorder(Color.RED, 2, true));
mainPanel.add(jPanel1);
return mainPanel;
}
private void launchTimer() {
if (timer == null) {
timer = new Timer(0, e -> movePanel());
timer.setDelay(500);
}
timer.start();
startButton.setEnabled(false);
stopButton.setEnabled(true);
}
private void stopTimer() {
timer.stop();
startButton.setEnabled(true);
stopButton.setEnabled(false);
}
private void movePanel() {
Point location = jPanel1.getLocation();
if (location.x > 180) {
diff = -10;
}
else if (location.x < 10) {
diff = 10;
}
location.x += diff;
jPanel1.setLocation(location);
}
private void showGui() {
frame = new JFrame("Move");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new MovePane());
}
}
Swing is event driven. Events are added to a queue and then read off the queue and performed. Setting the location of a JPanel
is an event. Calling events in a loop just floods the queue with events and some may be lost. That's why there is the Swing Timer
class. In the above code, every half second I change the location of jPanel1
.
Animating a Rectangle in a specific pattern
What you have done so far looks pretty good. You only need your action method to work properly. For that i would use a class variable called direction:
private boolean direction = true;
Now in your action method you move the rectangle either forward or backwards, depending on direction. And if it hits the end you move the rectangle down and invert direction:
public void actionPerformed(ActionEvent e) {
if (direction){
moveForward();
}
else {
moveBackwards();
}
//Check if it is at the end
if(((x >= 270) && (direction)) || ((x <= 30) && (!direction))) {
moveDown();
direction = !direction;
}
}
The if clause is a little bit complicated, but you can split it up, if you want it to be more readable.
JScrollPane doesn't truncate JLabel text
When I add the JPanel to the Frame it truncates the JLabels text, if it exceeds the bounds of the frame
Correct, because the layout manager forces the components to be sized in the space available.
However when I place the JPanel with the rows inside the JScrollPane it doesn't truncate the JLabel text, which then exceeds the JFrame
Correct, because the point of using a JScrollPane is to allow each component to be displayed at its preferred size. If the component size doesn't fit then scrolling will result.
If you want to control the width of the panel to fit the width of the scrollpane then you need to implement the Scrollable
interface on your panel and override the getScrollableTracksViewportWidth()
method to return Boolean.TRUE
. Read the API for more information about the Scrollable
interface.
An easy way to do this is to use the Scrollable Panel which has methods to allow you to control the scrollable properties.
Scroll JScrollPane by dragging mouse (Java swing)
Okay, that ended up been much simpler then I though it would be...
First, don't mess with the JViewport
, instead, use JComponent#scrollRectToVisible
directly on the component which is acting as the contents of the JScrollPane
, onto which the MouseListener
should be attached.
The following example simply calculates the difference between the point at which the user clicked and the amount they have dragged. It then applies this delta to the JViewport
's viewRect
and uses JComponent#scrollRectToVisible
to update the viewable area, simple :)
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 JLabel map;
public TestPane() {
setLayout(new BorderLayout());
try {
map = new JLabel(new ImageIcon(ImageIO.read(new File("c:/treasuremap.jpg"))));
map.setAutoscrolls(true);
add(new JScrollPane(map));
MouseAdapter ma = new MouseAdapter() {
private Point origin;
@Override
public void mousePressed(MouseEvent e) {
origin = new Point(e.getPoint());
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseDragged(MouseEvent e) {
if (origin != null) {
JViewport viewPort = (JViewport) SwingUtilities.getAncestorOfClass(JViewport.class, map);
if (viewPort != null) {
int deltaX = origin.x - e.getX();
int deltaY = origin.y - e.getY();
Rectangle view = viewPort.getViewRect();
view.x += deltaX;
view.y += deltaY;
map.scrollRectToVisible(view);
}
}
}
};
map.addMouseListener(ma);
map.addMouseMotionListener(ma);
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
Why JScrollPane does not react to mouse wheel events?
Walter beat me to analysing the issue :-)
Adding a bit of detail:
It's correct that a JScrollPane supports mouseWheelHandling. According to the rules of mouseEvent dispatching, the top-most (in z-order) component gets the event, and that's the scrollPane around the textArea. So if wheeling the textarea is not required, a simple solution might be to disable the wheel-support in its scrollPane. And JScrollPane even has api for doing it:
scrollPane.setWheelScrollingEnabled(false);
Unfortunately, that doesn't work. Reason it's not working is that this property has no effect in the event dispatch chain which ultimately calls into eventTypeEnabled:
case MouseEvent.MOUSE_WHEEL:
if ((eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0 ||
mouseWheelListener != null) {
return true;
}
This returns true if a mouseWheelListener is installed - which is done unconditionally by BasicScrollPaneUI, and not removed when the wheelEnabled property is changed (the ui doesn't even listen to that property ...) Plus the listener simply does nothing if the property is false. At least one of those facts is a bug, the ui should
- either remove/add the listener depending on wheelEnabled
- or: implement the listener such that it dispatches the event up the chain (as Walter does in his example)
The first option can be handled by application code:
scrollPane = new JScrollPane();
scrollPane.removeMouseWheelListener(scrollPane.getMouseWheelListeners()[0]);
it's a bit of a hack (as bug-workarounds always are :-), production code would have to listen to the wheelEnable to re-install if needed plus listen to LAF changes to update/re-remove the listeners installed by the ui.
Implementing the second option in slight modification (as to Walter's dispatching) by subclassing the JScrollPane and dispatch the event to parent if the wheelEnabled is false:
scrollPane = new JScrollPane() {
@Override
protected void processMouseWheelEvent(MouseWheelEvent e) {
if (!isWheelScrollingEnabled()) {
if (getParent() != null)
getParent().dispatchEvent(
SwingUtilities.convertMouseEvent(this, e, getParent()));
return;
}
super.processMouseWheelEvent(e);
}
};
scrollPane.setWheelScrollingEnabled(false);
Related Topics
What Is the Purpose of Defining a Package in a Java File
Raising a Number to a Power in Java
Reserved Words as Names or Identifiers
Java, Shifting Elements in an Array
Differencebetween Double.Parsedouble(String) and Double.Valueof(String)
What's in an Eclipse .Classpath/.Project File
How to Set Classpath When I Use Javax.Tools.Javacompiler Compile the Source
Differences Between "Java -Cp" and "Java -Jar"
Multiple Runnable Classes Inside Jar, How to Run Them
Generics:List<? Extends Animal> Is Same as List<Animal>
How to Parse Month Full Form String Using Dateformat in Java
How to Create a Java 8 Localdate from a Long Epoch Time in Milliseconds
Simpledateformat Parse Loses Timezone
Org.Glassfish.Jersey.Servlet.Servletcontainer Classnotfoundexception