I have a dialog where additional controls cause the dialog to resize when they appear. One day I will probably find a way to animate that, but for now I'm content with it just resizing. Problem is, it flickers.
I reduced the problem to a test:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
/**
* Shows flickering when resizing a dialog.
*/
public class FlickerTest extends FakeJDialog
{
public FlickerTest()
{
super((Window) null, "Flicker Test");
JButton button = new JButton("Bigger!");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent event)
{
Window window = SwingUtilities.getWindowAncestor((Component) event.getSource());
window.setSize(window.getWidth(), window.getHeight() + 20);
}
});
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setOpaque(true);
contentPane.add(button, BorderLayout.PAGE_START);
JRootPane rootPane = new JRootPane();
rootPane.setContentPane(contentPane);
add(rootPane);
setResizable(false);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) throws Exception
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new FlickerTest().setVisible(true);
}
});
}
}
Each time I click the button, the window changes size. For a noticeable amount of time, the bottom of the dialog goes black. By recording my screen, I was able to get a screenshot demonstrating it:
How can I avoid this?
Further investigation:
The following subclass of Dialog exhibits the same flickering as JDialog:
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Window;
import javax.swing.JLayeredPane;
import javax.swing.JRootPane;
import javax.swing.RootPaneContainer;
/**
* Minimal subclass of Dialog required to cause the flickering.
* If you comment out "implements RootPaneContainer", the flickering goes away.
*/
public class FakeJDialog extends Dialog implements RootPaneContainer
{
public FakeJDialog(Window owner, String title)
{
super(owner, title, Dialog.ModalityType.MODELESS);
}
public JRootPane getRootPane()
{
throw new UnsupportedOperationException();
}
public Container getContentPane()
{
throw new UnsupportedOperationException();
}
public void setContentPane(Container contentPane)
{
throw new UnsupportedOperationException();
}
public JLayeredPane getLayeredPane()
{
throw new UnsupportedOperationException();
}
public void setLayeredPane(JLayeredPane layeredPane)
{
throw new UnsupportedOperationException();
}
public Component getGlassPane()
{
throw new UnsupportedOperationException();
}
public void setGlassPane(Component glassPane)
{
throw new UnsupportedOperationException();
}
}
I find this kind of interesting, because merely commenting out the implements RootPaneContainer is somehow enough to completely change the behaviour. Something in Swing or AWT is obviously looking for this interface and treating those components specially. So this suggests that no subclass of JDialog would avoid the issue.
I don't believe there is a way around this with JDialog. However, java.awt.Dialog doesn't have this issue.
Try this, i have made an example which removes flickering almost completely
in addition u will get well-marked resize corner
/*
* resizing swing trick in Win7+Aero demo
* #author: s1w_
*/
import java.awt.event.*;
import java.awt.*;
import javax.swing.event.*;
import javax.swing.*;
class ResizeHookDemo extends JDialog {
private final static int width = 580, height = 350;
private final JFileChooser fc;
private java.awt.geom.GeneralPath gp;
public ResizeHookDemo() {
super((JDialog)null, "Choose File", true);
fc = new JFileChooser() {
#Override
public void paint(Graphics g) {
super.paint(g);
int w = getWidth();
int h = getHeight();
g.setColor(new Color(150, 150, 150, 200));
g.drawLine(w-7, h, w, h-7);
g.drawLine(w-11, h, w, h-11);
g.drawLine(w-15, h, w, h-15);
gp = new java.awt.geom.GeneralPath();
gp.moveTo(w-17, h);
gp.lineTo(w, h-17);
gp.lineTo(w, h);
gp.closePath();
}
};
fc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("CancelSelection")) {
setVisible(false);
// action...
}
else if (e.getActionCommand().equals("ApproveSelection")) {
setVisible(false);
// action...
}
}
});
MouseInputListener resizeHook = new MouseInputAdapter() {
private Point startPos = null;
public void mousePressed(MouseEvent e) {
if (gp.contains(e.getPoint()))
startPos = new Point(getWidth()-e.getX(), getHeight()-e.getY());
}
public void mouseReleased(MouseEvent mouseEvent) {
startPos = null;
}
public void mouseMoved(MouseEvent e) {
if (gp.contains(e.getPoint()))
setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
else
setCursor(Cursor.getDefaultCursor());
}
public void mouseDragged(MouseEvent e) {
if (startPos != null) {
int dx = e.getX() + startPos.x;
int dy = e.getY() + startPos.y;
setSize(dx, dy);
repaint();
}
}
};
fc.addMouseMotionListener(resizeHook);
fc.addMouseListener(resizeHook);
fc.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 20));
add(fc);
setResizable(false);
setMinimumSize(new Dimension(width, height));
setDefaultCloseOperation(HIDE_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String args[]) {
System.out.println("Starting demo...");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ResizeHookDemo().setVisible(true);
}
});
}
}
Related
I am trying to make elements of an array to be displayed in different locations with random time periods (with fade in and fade out effect).
What i ve done so far, made an array of text labels. Made transitions. But i can't figure out, how to create a for loop that will display other labels in different locations on JFrame. And they should not appear all at the same time but one after another.
Please, help out?
Here is my code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class FadingLabel {
private int alpha = 255;
private int increment = -5;
public JLabel label = new JLabel("Fading Label");
public JLabel label2 = new JLabel("Fading Label 2");
public JLabel label3 = new JLabel("Fading Label 3");
public JLabel label4 = new JLabel("Fading Label 4");
JLabel labels[] = new JLabel[]{ label, label2, label3 };
Dimension size = label.getPreferredSize();
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new FadingLabel().makeUI();
}
});
}
public void makeUI() {
new Timer(80, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i <= 3; i++){
alpha += increment;
if (alpha >= 255) {
alpha = 255;
increment = -increment;
}
if (alpha <= 0) {
try {
Thread.sleep(100);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
alpha = 0;
increment = -increment;
}
label3.setForeground(new Color(0, 0, 0, alpha));
label3.setLocation(50,60);
}
}
}).start();
JFrame frame = new JFrame();
frame.add(labels[2]);
frame.setPreferredSize(new Dimension(700,500));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Animation is hard, good animation is harder.
A stepped animation (like you've done) is not particularly efficient and can suffer from interruptions (from the OS or other parts of the system) and can be difficult to scale.
Instead, you should aim for a "duration" based animation. Where something happens over a given period of time, this way, you can more easily drop frames you can't render.
One of the difficult concepts to get around is the idea that you can't perform long running or blocking operations in the "main thread" of the GUI, but neither can you update UI from outside of the "main thread" (in Swing this is known as the Event Dispatching Thread).
So, instead, you need some way monitor each label and as it fades out, start the next label fading in. This is where a good observer pattern (AKA listener) comes in.
import java.awt.AlphaComposite;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.time.Instant;
import java.util.EventListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private FadableLabel[] labels = new FadableLabel[]{
new FadableLabel("A long time ago"),
new FadableLabel("in a galaxy far, far, away..."),
new FadableLabel("It is a period of civil war."),
new FadableLabel("Rebel spaceships striking from a hidden base,"),
new FadableLabel("have won their first victory against the evil Galactic Empire"),
new FadableLabel("During the battle,"),
new FadableLabel("Rebel spies managed to steal secret plans to the Empire's ultimate weapon,"),
new FadableLabel("the Death Star")
};
private int labelIndex = -1;
public TestPane() {
setBorder(new EmptyBorder(50, 50, 50, 50));
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
for (FadableLabel label : labels) {
label.setAlpha(0);
add(label, gbc);
}
}
#Override
public void addNotify() {
super.addNotify();
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
nextLabel();
}
});
}
#Override
public void removeNotify() {
super.removeNotify();
}
protected void nextLabel() {
labelIndex++;
if (labelIndex >= labels.length) {
return;
}
FadableLabel label = labels[labelIndex];
label.addFadableLableListener(new FadableLableListener() {
#Override
public void didFadeLabelIn(FadableLabel label) {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.fadeOut();
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public void didFadeLabelOut(FadableLabel label) {
label.removeFadableLableListener(this);
nextLabel();
}
});
label.fadeIn();
}
}
public interface FadableLableListener extends EventListener {
public void didFadeLabelIn(FadableLabel label);
public void didFadeLabelOut(FadableLabel label);
}
public class FadableLabel extends JLabel {
private float alpha = 1.0f;
private Timer fadeTimer;
private FadeRange fadeRange;
private Instant fadeStartedAt;
private Duration desiredFadeTime = Duration.ofMillis(1000);
public FadableLabel() {
super();
}
public FadableLabel(String text) {
super(text);
}
public void addFadableLableListener(FadableLableListener listener) {
listenerList.add(FadableLableListener.class, listener);
}
public void removeFadableLableListener(FadableLableListener listener) {
listenerList.remove(FadableLableListener.class, listener);
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
repaint();
}
protected void fireDidFadeOut() {
FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
if (listeners.length == 0) {
return;
}
for (FadableLableListener listener : listeners) {
listener.didFadeLabelOut(this);
}
}
protected void fireDidFadeIn() {
FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
if (listeners.length == 0) {
return;
}
for (FadableLableListener listener : listeners) {
listener.didFadeLabelIn(this);
}
}
protected void stopFadeTimer() {
if (fadeTimer != null) {
fadeStartedAt = null;
fadeTimer.stop();
}
}
protected void startFadeTimer() {
if (fadeRange == null) {
throw new RuntimeException("Fade range can not be null when starting animation");
}
fadeStartedAt = Instant.now();
fadeTimer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Duration runTime = Duration.between(fadeStartedAt, Instant.now());
double progress = Math.min(1d, Math.max(0d, runTime.toMillis() / (double) desiredFadeTime.toMillis()));
setAlpha(fadeRange.valueAt(progress));
if (progress >= 1.0) {
stopFadeTimer();
if (getAlpha() >= 1.0) {
fireDidFadeIn();
} else {
fireDidFadeOut();
}
}
}
});
fadeTimer.start();
}
public void fadeIn() {
stopFadeTimer();
if (alpha < 1.0) {
fadeRange = new FadeRange(alpha, 1.0f);
startFadeTimer();
}
}
public void fadeOut() {
stopFadeTimer();
if (alpha > 0.0) {
fadeRange = new FadeRange(alpha, 0);
startFadeTimer();
}
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
super.paintComponent(g2d);
g2d.dispose();
}
protected class FadeRange {
private float from;
private float to;
public FadeRange(float from, float to) {
this.from = from;
this.to = to;
}
public float getFrom() {
return from;
}
public float getTo() {
return to;
}
public float getDistance() {
return getTo() - getFrom();
}
public float valueAt(double progress) {
double distance = getDistance();
double value = distance * progress;
value += getFrom();
return (float) value;
}
}
}
}
Now, to your next problem. A poor approach might be to use an "absolute" or "null" layout
A better choice would be to either make use of something like GridBagLayout (and possibly EmptyLayout) and randomise the Insets of the GridBagConstraints. Alternatively, you could create your own layout manager to do the job for you
See Absolute Positioning Graphic JPanel Inside JFrame Blocked by Blank Sections and Moving JPasswordField to absolute position for some ideas
A cray example
When I say animation is "hard" and it can become complicated very fast, I'm not kidding. To that I end I wrote myself an animation library, which does all the things I keep finding myself doing.
https://github.com/RustyKnight/SuperSimpleSwingAnimationFramework
So, this is an example, based on the above library, which moves the label across a random range, while it's been faded in/out. Without the above library, this kind of work would be, a lot.
import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import java.util.EventListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import org.kaizen.animation.Animatable;
import org.kaizen.animation.AnimatableAdapter;
import org.kaizen.animation.DefaultAnimatableDuration;
import org.kaizen.animation.curves.AnimationCurve;
import org.kaizen.animation.curves.Curves;
import org.kaizen.animation.ranges.AnimatableRange;
import org.kaizen.animation.ranges.FloatAnimatableRange;
import org.kaizen.animation.ranges.FloatRange;
import org.kaizen.animation.ranges.PointAnimatableRange;
import org.kaizen.animation.ranges.PointRange;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private String[] textValues = new String[] {
"A long time ago",
"in a galaxy far, far, away...",
"It is a period of civil war.",
"Rebel spaceships striking from a hidden base,",
"have won their first victory against the evil Galactic Empire",
"During the battle,",
"Rebel spies managed to steal secret plans to the Empire's ultimate weapon,",
"the Death Star"
};
private int labelIndex = -1;
// You'd need two if you wanted to do cross fades
private FadableLabel label;
private Random rnd = new Random();
// The desired duration of the animation, 1 second for fade in,
// 1 second for fade out and 1 second for delay between swicthing state
private Duration desiredDuration = Duration.ofSeconds(3);
// The desired animation curve (ease in/out)
private AnimationCurve curve = Curves.SINE_IN_OUT.getCurve();
// The movement animator
private DefaultAnimatableDuration animator;
public TestPane() {
setLayout(null);
label = new FadableLabel();
label.setAlpha(0);
add(label);
label.addFadableLableListener(new FadableLableListener() {
#Override
public void didFadeLabelIn(FadableLabel label) {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.fadeOut();
}
});
timer.setRepeats(false);
timer.start();
}
#Override
public void didFadeLabelOut(FadableLabel label) {
nextText();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 400);
}
#Override
public void addNotify() {
super.addNotify();
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
nextText();
}
});
}
#Override
public void removeNotify() {
stopAnimation();
super.removeNotify();
}
protected void stopAnimation() {
if (animator != null) {
animator.stop();
}
}
protected void nextText() {
stopAnimation();
labelIndex++;
if (labelIndex >= textValues.length) {
return;
}
String text = textValues[labelIndex];
label.setText(text);
label.setSize(label.getPreferredSize());
// Randomise the from and to locations
Point from = new Point(rnd.nextInt(getWidth() - label.getSize().width), rnd.nextInt(getHeight() - label.getSize().height));
Point to = new Point(rnd.nextInt(getWidth() - label.getSize().width), rnd.nextInt(getHeight() - label.getSize().height));
// Generate the range
PointRange range = new PointRange(from, to);
// Setup an animatable range of the PointRange
animator = new PointAnimatableRange(range, desiredDuration, curve, new AnimatableAdapter<Point>() {
#Override
public void animationChanged(AnimatableRange<Point> animatable) {
label.setLocation(animatable.getValue());
}
});
label.setLocation(from);
// Make it so
label.fadeIn();
animator.start();
}
}
public interface FadableLableListener extends EventListener {
public void didFadeLabelIn(FadableLabel label);
public void didFadeLabelOut(FadableLabel label);
}
public class FadableLabel extends JLabel {
private FloatAnimatableRange animator;
private AnimationCurve curve = Curves.SINE_IN_OUT.getCurve();
private Duration desiredDuration = Duration.ofSeconds(1);
private float alpha = 1.0f;
public FadableLabel() {
super();
}
public FadableLabel(String text) {
super(text);
}
public void addFadableLableListener(FadableLableListener listener) {
listenerList.add(FadableLableListener.class, listener);
}
public void removeFadableLableListener(FadableLableListener listener) {
listenerList.remove(FadableLableListener.class, listener);
}
public float getAlpha() {
return alpha;
}
public void setAlpha(float alpha) {
this.alpha = alpha;
repaint();
}
protected void fireDidFadeOut() {
FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
if (listeners.length == 0) {
return;
}
for (FadableLableListener listener : listeners) {
listener.didFadeLabelOut(this);
}
}
protected void fireDidFadeIn() {
FadableLableListener[] listeners = listenerList.getListeners(FadableLableListener.class);
if (listeners.length == 0) {
return;
}
for (FadableLableListener listener : listeners) {
listener.didFadeLabelIn(this);
}
}
protected void stopFadeTimer() {
if (animator != null) {
animator.stop();
}
}
protected void startFadeTimer(FloatRange range, AnimationListener animationListener) {
stopFadeTimer();
animator = new FloatAnimatableRange(range, desiredDuration, curve, new AnimatableAdapter<Float>() {
#Override
public void animationChanged(AnimatableRange<Float> animatable) {
alpha = animatable.getValue();
repaint();
}
#Override
public void animationCompleted(Animatable animator) {
if (animationListener != null) {
animationListener.animationCompleted();
}
}
});
animator.start();
}
public void fadeIn() {
stopFadeTimer();
startFadeTimer(new FloatRange(alpha, 1f), new AnimationListener() {
#Override
public void animationCompleted() {
fireDidFadeIn();
}
});
}
public void fadeOut() {
stopFadeTimer();
startFadeTimer(new FloatRange(alpha, 0f), new AnimationListener() {
#Override
public void animationCompleted() {
fireDidFadeOut();
}
});
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
super.paintComponent(g2d);
g2d.dispose();
}
protected interface AnimationListener {
public void animationCompleted();
}
}
}
The library is based on Netbeans, it wouldn't be hard to just extract the source into some other IDE.
Swing based "perform after delay"
made up silly solution, #MadProgrammer. added Thread.sleep(ms); in removeFadeableLabelListener. it works but i believe there is much brighter and smart solution. could you show please how to use delay timer for such task?
Don't, ever, use Thread.sleep from within the Event Dispatching Thread. This is going to cause a cascade of issues which will basically make you program look like it's frozen (because, essentially, it is)
Instead, you need to become familiar with the mechanisms you have available to you via the API. You could use a SwingWorker, but a simpler solution might be to just use a non-repeating Swing Timer, which is demonstrated above.
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Duration;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JButton btn = new JButton("Click me");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btn.setText("...");
SwingHelper.after(Duration.ofSeconds(1), new Runnable() {
#Override
public void run() {
btn.setEnabled(false);
btn.setText("Don't do that");
}
});
}
});
setBorder(new EmptyBorder(10, 10, 10, 10));
setLayout(new GridBagLayout());
add(btn);
}
}
public class SwingHelper {
public static void after(Duration duration, Runnable runnable) {
Timer timer = new Timer((int)duration.toMillis(), new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
runnable.run();
}
});
timer.setRepeats(false);
timer.start();
}
}
}
I am using practically the same method for moving the JFrame as Java - Custom shaped draggable JFrame
I have a class that extends JPanel. In the said class, I have the previous x and y set to variables like so:
private int pressX, pressY
Then, in the mousePressed method, I have:
pressX = e.getX();
pressY = e.getY();
Finally, in mouseDragged, I have:
mainFrame.setLocation((int) Math.round(mainFrame.getLocation().getX() + e.getX() - pressX), (int) Math.round(mainFrame.getLocation().getY() + e.getY() - pressY));
However, when dragging the window around, there is a fair amount of lag or some kind of problem. Here is a video to show what happens visually.
https://i.imgur.com/KWtbE1s.gifv
I am using the swing library and am repainting using a Timer that ticks roughly every two milliseconds.
Edit:
I modified the code such that the points were relative to the JPanel, but the problem still occurs.
dragMe = new Draggable() {
private static final long serialVersionUID = 1L;
private Point press;
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
press = SwingUtilities.convertPoint(this, e.getPoint(), mainFrame);
}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mouseDragged(MouseEvent e) {
Point drag = SwingUtilities.convertPoint(this, e.getPoint(), mainFrame);
mainFrame.setLocation((int) Math.round(mainFrame.getLocation().getX() + drag.getX() - press.getX()), (int) Math.round(mainFrame.getLocation().getY() + drag.getY() - press.getY()));
}
#Override
public void mouseMoved(MouseEvent e) {}
};
Edit 2:
Unfortunately, the example I have created here works perfectly, and I don't know why it is not working in the true code. I even tried copying the exact class I made in this example into the real application, and the problem still occurs in the true code.
It looks like I will have to look more into this.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;
public class Test extends JPanel implements ActionListener {
private static final long serialVersionUID = 1L;
private static JFrame frame;
public static void main(String[] args) {
class Draggable extends JPanel implements MouseListener, MouseMotionListener {
private static final long serialVersionUID = 1L;
private Point press;
public Draggable() {
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
press = SwingUtilities.convertPoint(this, e.getPoint(), frame);
}
#Override
public void mouseReleased(MouseEvent e) {}
#Override
public void mouseEntered(MouseEvent e) {}
#Override
public void mouseExited(MouseEvent e) {}
#Override
public void mouseDragged(MouseEvent e) {
Point drag = SwingUtilities.convertPoint(this, e.getPoint(), frame);
frame.setLocation((int) Math.round(frame.getLocation().getX() + drag.getX() - press.getX()), (int) Math.round(frame.getLocation().getY() + drag.getY() - press.getY()));
}
#Override
public void mouseMoved(MouseEvent e) {}
}
Test t = new Test();
t.setBounds(0, 0, 1200, 600);
t.setVisible(true);
Draggable drag = new Draggable();
drag.setBounds(24, 24, 24, 24);
drag.setVisible(true);
Timer repaintTimer = new Timer(2, t);
frame = new JFrame();
frame.setSize(1200, 600);
frame.add(t);
frame.add(drag);
Dimension dim = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocation((dim.width - frame.getWidth()) / 2, (dim.height - frame.getHeight()) / 2);
frame.getContentPane().setLayout(null);
frame.setAlwaysOnTop(true);
frame.setResizable(false);
repaintTimer.start();
frame.setVisible(true);
frame.requestFocus();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(24, 24, 24, 24);
}
}
Check out Moving Windows for a class that does this for you.
It is more complicated than the basic dragging logic because it supports other features which you may not need.
The basic logic (from the above class) for dragging a frame when adding the listeners to one (or more) components of the frame would be:
MouseInputAdapter mia = new MouseInputAdapter()
{
Point location;
Point pressed;
public void mousePressed(MouseEvent me)
{
pressed = me.getLocationOnScreen();
Window window = SwingUtilities.windowForComponent(me.getComponent());
location = window.getLocation();
}
public void mouseDragged(MouseEvent me)
{
Point dragged = me.getLocationOnScreen();
int x = (int)(location.x + dragged.getX() - pressed.getX());
int y = (int)(location.y + dragged.getY() - pressed.getY());
Window window = SwingUtilities.windowForComponent(me.getComponent());
window.setLocation(x, y);
}
};
panel.addMouseListener(mia);
panel.addMouseMotionListener(mia);
Edit:
Here is some more generic code that allows you to move a component directly or indirectly by specify a parent component to receive the events:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class BasicComponentMover extends MouseInputAdapter
{
/*
** A class that allows you to drag a component around a panel.
** Optionally you can specify a "parent" component, in which case
** the MouseEvents will be converted to be relative to this component
** which will then be dragged. This will allow you to drag a JFrame by
** dragging a component that has been added to the frame.
*/
private Component parent;
private Component destination;
private Point pressed;
BasicComponentMover() {}
{
}
BasicComponentMover(Component parent)
{
this.parent = parent;
}
#Override
public void mousePressed(MouseEvent me)
{
destination = (parent == null) ? me.getComponent() : parent;
pressed = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), destination);
}
#Override
public void mouseDragged(MouseEvent me)
{
Point location = destination.getLocation();
Point drag = SwingUtilities.convertPoint(me.getComponent(), me.getPoint(), destination);
int x = (int)(location.x - pressed.getX() + drag.getX());
int y = (int)(location.y - pressed.getY() + drag.getY());
destination.setLocation(x, y);
}
private static void createAndShowGUI()
{
JPanel background = new JPanel(null);
background.setPreferredSize(new Dimension(300, 300));
background.setSize(background.getPreferredSize());
background.setBackground(Color.RED);
JPanel foreground = new JPanel(null);
foreground.setPreferredSize(new Dimension(50, 50));
foreground.setSize(foreground.getPreferredSize());
foreground.setBackground(Color.BLUE);
background.add(foreground);
JFrame frame = new JFrame("BasicComponentMover");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.add(background);
frame.setSize(500, 500);
frame.setLocationByPlatform( true );
frame.setVisible( true );
// Drag frame by dragging the red background panel
BasicComponentMover bcm1 = new BasicComponentMover(frame);
background.addMouseListener(bcm1);
background.addMouseMotionListener(bcm1);
// Drag the blue forground component around the red background
BasicComponentMover bcm2 = new BasicComponentMover();
foreground.addMouseListener(bcm2);
foreground.addMouseMotionListener(bcm2);
// Or, drage the background around the frame
BasicComponentMover bcm3 = new BasicComponentMover(background);
// foreground.addMouseListener(bcm3);
// foreground.addMouseMotionListener(bcm3);
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Try dragging the blue/red components to see the difference.
I recently made a MakeDots class in java that when you drag the mouse, it makes dots(yay!). I implemented a MouseMotionListener for that. It worked fine. Then I added some buttons to change the colors of the dots. I added ActionListeners for that. Once I did that, the color-changing worked, but I could only draw in the very tiny borders of the buttons! Why does this happen? How do I fix it? If you need me to post my code just say in the comments, and I will do so.
CODE:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
public class MakeDots extends Applet implements MouseMotionListener{
private Graphics graphics = null;
private Color dotColor = Color.red;
private Button setRed;
private Button setPink;
private Button setPurple;
private Button clrBtn;
public void init() {
setRed = new Button("Red");
setPink = new Button("Pink");
setPurple = new Button("Purple");
clrBtn = new Button("Clear");
this.addMouseMotionListener(this);
this.add(setRed);
this.add(setPink);
this.add(setPurple);
this.add(clrBtn);
setRed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dotColor = Color.red;
}
});
setPink.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dotColor = Color.pink;
}
});
setPurple.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dotColor = new Color(80, 0, 80);
}
});
clrBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
clear();
}
});
}
public void paint(Graphics g) {
this.setSize(new Dimension(800, 600));
graphics = g.create();
clear();
}
public void drawDot(int x, int y) {
graphics.setColor(dotColor);
graphics.fillOval(x, y, 10, 10);
}
public void clear() {
Dimension d = this.getSize();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, d.width, d.height);
}
#Override
public void mouseDragged(MouseEvent e) {
int mouseX = e.getX();
int mouseY = e.getY();
drawDot(mouseX, mouseY);
}
#Override
public void mouseMoved(MouseEvent arg0) {
// TODO Auto-generated method stub
}
}
There are few things you might want to do. Lose the practice of writing actionPerformed(...) everytime you add an actionListener(). Just use getActionCommand() to check which button triggered the event. Also,create a global object of Color() and update it in actionPerformed()
I'm fairly new to java, so i don't think I have this fairly close to right, but I can seem to find any other help with this. Basically, I'm trying to animate a jPanel's background color so that it's hue (I'm using an hsb color model) changes. sort of like this: https://kahoot.it/#/ notice how the color kinda floats from one to another. Here is my code that I have so far:
public void animate(){
for(float i=.001f;i<1f;i+=.001f){
jPanel1.setBackground(Color.getHSBColor(i, .53f, .97f));
try{
Thread.sleep(5L);
}catch(InterruptedException ex){
}
System.out.println(i);
}
}
now i know this probably isn't right, but the loop works fine, the only problem is that the jPanel doesn't "update" until the loop is finished. Sorry all for being a huge noob at stuff like this, and thanks for any responses
The problem is that you are blocking the event dispatch thread, so no drawing can happen. Use a swing Timer instead of sleeping. A running example of changing HSB colors:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ColorCycle {
private static class ColorPanel extends JPanel {
private final float stepSize;
private final Timer timer;
private int index;
ColorPanel(final int steps, int fps) {
stepSize = 1f / steps;
timer = new Timer(1000 / fps, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
index++;
if (index > steps) {
index = 0;
}
repaint();
}
});
}
void start() {
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.getHSBColor(index * stepSize, 1f, 1f));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Colors");
ColorPanel panel = new ColorPanel(300, 20);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
panel.start();
}
});
}
}
Works for me...
import java.awt.Color;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class HsbBackground {
public static void main(String[] args) throws Exception {
new HsbBackground();
}
private JPanel jPanel1 = new JPanel();
public HsbBackground() throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
JFrame jFrame = new JFrame();
jFrame.setContentPane(jPanel1);
jFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jFrame.setSize(400, 300);
jFrame.setVisible(true);
}
});
animate();
}
public void animate() {
for (float i = .001f; i < 1f; i += .001f) {
final float j = i;
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jPanel1.setBackground(Color.getHSBColor(j, .53f, .97f));
}
});
try {
Thread.sleep(5L);
} catch (InterruptedException ex) {
}
System.out.println(i);
}
}
}
Make sure, that you call setVisible of your frame before you start your loop.
PS: I updated the code, so that GUI changes are all made from the event dispatching thread. The code still works fine.
Can't seem to figure out how to only show one circle. Was trying to //g.clearRect(0, 0, 400, 400); but that clears the background too. Was also trying to just fill the background with yellow again but couldn't get that working either. Any suggestions?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JMouseFrame extends JFrame
implements MouseListener {
Container con = null;
int x, y;
int size;
public JMouseFrame() {
setTitle("JMouseFrame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
con = this.getContentPane();
addMouseListener(this);
}
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}
public void mouseClicked(MouseEvent e) {
int whichButton = e.getButton();
if (whichButton == MouseEvent.BUTTON1) {
size = 15;
} else if (whichButton == MouseEvent.BUTTON3) {
size = 4;
}
repaint();
}
public void mouseEntered(MouseEvent e) {
con.setBackground(Color.yellow);
}
public void mouseExited(MouseEvent e) {
con.setBackground(Color.black);
}
public void paint(Graphics g) {
//g.clearRect(0, 0, 400, 400);
g.drawOval(x - size, y - size, size * 3, size * 3);
}
public static void main(String[] args) {
JMouseFrame mFrame = new JMouseFrame();
mFrame.setSize(400, 400);
mFrame.setVisible(true);
}
public void mouseReleased(MouseEvent e) {
}
}
It looks like you're mixing AWT and Swing painting. Instead, override paintComponent(), as suggested in the example below. See Painting in AWT and Swing for details.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/3898775 */
public class MousePanel extends JPanel {
private static final int SIZE = 20;
Point p = new Point(Short.MAX_VALUE, Short.MAX_VALUE);
public MousePanel() {
this.setPreferredSize(new Dimension(400, 400));
this.setBackground(Color.yellow);
this.addMouseListener(new MouseHandler());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.drawOval(p.x - SIZE, p.y - SIZE, SIZE * 2, SIZE * 2);
}
private final class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
repaint();
}
}
private void display() {
JFrame f = new JFrame("MousePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MousePanel().display();
}
});
}
}
Addendum:
I can't figure out how to make the code you posted work with what I have...
You can restore your mouse methods to the MouseHandler almost verbatim. The only difference is the need to qualify this, e.g.
#Override
public void mouseClicked(MouseEvent e) {
int whichButton = e.getButton();
if (whichButton == MouseEvent.BUTTON1) {
MousePanel.this.size = 15;
} else if (whichButton == MouseEvent.BUTTON3) {
MousePanel.this.size = 4;
}
repaint();
}
#Override
public void mouseEntered(MouseEvent e) {
MousePanel.this.setBackground(Color.yellow);
}
#Override
public void mouseExited(MouseEvent e) {
MousePanel.this.setBackground(Color.black);
}