My Swing application has to show a modal dialog to the user. Sorry for not posting SSCCE.
topContainer might be JFrame or JApplet.
private class NewGameDialog extends JDialog {
public NewGameDialog () {
super(SwingUtilities.windowForComponent(topContainer), "NEW GAME", ModalityType.APPLICATION_MODAL);
//add components here
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
//TODO:
setSize(new Dimension(250, 200));
setLocation(650, 300);
}
}
I start the dialog like this on network event
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
NewGameDialog dialog = new NewGameDialog();
dialog.setVisible(true);
}
});
The problem is to set optimal location for my dialog.
1) If it is set as absolute value, and I move the app frame to the second screen, then dialog is shown on the first screen which is weird.
2) If it is set a relative value to JFrame, it might appear that user moved the app frame outside of the screen and the dialog being relatively located would not be visible to the user. And because it is modal, the game would be stuck.
What is the best solution considering two above mentioned issues?
This reminded me of a very favourite post of mine, using Window.setLocationByPlatform(true), on StackOverflow.
How to best position Swing GUIs
EDIT 1 :
You can add a FocusListener to your JDialog and on focusGained(...) method, you can use setLocationRelativeTo(null) for both the JFrame and the JDialog, so that they both come to the center of the screen no matter where they are before.
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
/**
* Created with IntelliJ IDEA.
* User: Gagandeep Bali
* Date: 1/14/13
* Time: 7:34 PM
* To change this template use File | Settings | File Templates.
*/
public class FrameFocus
{
private JFrame mainwindow;
private CustomDialog customDialog;
private void displayGUI()
{
mainwindow = new JFrame("Frame Focus Window Example");
customDialog = new CustomDialog(mainwindow, "Modal Dialog", true);
mainwindow.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
JButton mainButton = new JButton(
"Click me to open a MODAL Dialog");
mainButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (!customDialog.isShowing())
customDialog.setVisible(true);
}
});
contentPane.add(mainButton);
mainwindow.setContentPane(contentPane);
mainwindow.pack();
mainwindow.setLocationByPlatform(true);
mainwindow.setVisible(true);
}
public static void main(String... args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
new FrameFocus().displayGUI();
}
});
}
}
class CustomDialog extends JDialog
{
private JFrame mainWindow;
public CustomDialog(JFrame owner, String title, boolean modal)
{
super(owner, title, modal);
mainWindow = owner;
JPanel contentPane = new JPanel();
JLabel dialogLabel = new JLabel(
"I am a Label on JDialog.", JLabel.CENTER);
contentPane.add(dialogLabel);
setContentPane(contentPane);
pack();
addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
mainWindow.setLocationRelativeTo(null);
setLocationRelativeTo(null);
}
#Override
public void focusLost(FocusEvent e) {
/*
* Nothing written for this part yet
*/
}
});
}
}
EDIT 2 :
I searched a bit here and there, and it turns out, in my opinion, that actually on which Monitor Screen your application comes at the first instance, will determine it's GraphicsConfiguration. Though as I roamed through the API, there is only a getter method for the said GraphicsConfiguration thingy and no setter methods for the same (Still You can specify one through the constructor of any top level Window i.e. JFrame(...)/JDialog(...)).
Now you can occupy your head with this code, which can be used to determine the appropriate location, that you want to set, again, you might have to use focusGain() method in my opinion, to satisfy condition 2 of your question. Have a look at the code attached, though no need to create a new JFrame/JDialog, just watch how to get coordinates for the screen (that you can add in the focusGain() method to determine the location of the whole Application.)
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc =
gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
JFrame f = new
JFrame(gs[j].getDefaultConfiguration());
Canvas c = new Canvas(gc[i]);
Rectangle gcBounds = gc[i].getBounds();
int xoffs = gcBounds.x;
int yoffs = gcBounds.y;
f.getContentPane().add(c);
f.setLocation((i*50)+xoffs, (i*60)+yoffs);
f.show();
}
}
EDIT 3 :
Try to change this :
int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);
to just :
setLocationRelativeTo(mainWindow);
To test the above thingy, I used my FrameFocus Class as is, though I had added your changes to my CustomDialog method, as shown in this modified CustomDialog Class.
class CustomDialog extends JDialog
{
private JFrame mainWindow;
public CustomDialog(JFrame owner, String title, boolean modal)
{
super(owner, title, modal);
mainWindow = owner;
JPanel contentPane = new JPanel();
JLabel dialogLabel = new JLabel(
"I am a Label on JDialog.", JLabel.CENTER);
contentPane.add(dialogLabel);
setContentPane(contentPane);
pack();
addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
//mainWindow.setLocationRelativeTo(null);
//setLocationRelativeTo(null);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc = gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
Rectangle gcBounds = gc[i].getBounds();
Point loc = mainWindow.getLocationOnScreen();
if (gcBounds.contains(loc)) {
System.out.println("at " + j + " screen");
int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
mainWindow.setLocation(x, y);
//x = (int) (loc.getX() + (mainWindow.getWidth() - CustomDialog.this.getWidth()) / 2);
//y = (int) (loc.getY() + (mainWindow.getHeight() - CustomDialog.this.getHeight()) / 2);
//CustomDialog.this.setLocation(x, y);
CustomDialog.this.setLocationRelativeTo(mainWindow);
break;
}
}
}
}
#Override
public void focusLost(FocusEvent e) {
/*
* Nothing written for this part yet
*/
}
});
}
}
I think, best would be to center the dialog in the middle of the current screen as described here.
Toolkit toolkit = Toolkit.getDefaultToolkit();
Dimension screenSize = toolkit.getScreenSize();
int x = (screenSize.width - d.getWidth()) / 2;
int y = (screenSize.height - d.getHeight()) / 2;
d.setLocation(x, y);
This always works and how it can be invisible to the user if it is right in the center of the screen? And setLocationRelativeTo can also be used but you need to invoke it at the right time.
use JDialog.setLocation() for moving JDialog on desired Point on the screen
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
public class JDialogAtPoint {
private JFrame frame = new JFrame();
private JPanel panel = new JPanel();
private JDialog dialog;
private Point location;
public JDialogAtPoint() {
createGrid();
createDialog();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
}
private void createGrid() {
panel.setLayout(new GridLayout(3, 3, 4, 4));
int l = 0;
int row = 3;
int col = 3;
JButton buttons[][] = new JButton[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
buttons[i][j] = new JButton("");
buttons[i][j].putClientProperty("column", i + 1);
buttons[i][j].putClientProperty("row", j + 1);
buttons[i][j].setAction(updateCol());
panel.add(buttons[i][j]);
l++;
}
}
}
private void createDialog() {
dialog = new JDialog();
dialog.setAlwaysOnTop(true);
dialog.setModal(true);
dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
JPanel pane = (JPanel) dialog.getContentPane();
pane.setBorder(new EmptyBorder(20, 20, 20, 20));
dialog.pack();
}
public Action updateCol() {
return new AbstractAction("Display JDialog at Point") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
System.out.println("Locations coordinates" + btn.getLocation());
System.out.println("clicked column "
+ btn.getClientProperty("column")
+ ", row " + btn.getClientProperty("row"));
if (!dialog.isVisible()) {
showingDialog(btn.getLocationOnScreen());
}
}
};
}
private void showingDialog(final Point loc) {
dialog.setVisible(false);
location = loc;
int x = location.x;
int y = location.y;
dialog.setLocation(x, y);
Runnable doRun = new Runnable() {
#Override
public void run() {//dialog.setLocationRelativeTo(frame);
dialog.setVisible(true);
}
};
SwingUtilities.invokeLater(doRun);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JDialogAtPoint cf = new JDialogAtPoint();
}
});
}
}
With the help of all 3 answerers I have come up with code which seems exactly what I need. First, JFrame got placed in the middle of current screen and then JDialog accordingly.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int j = 0; j < gs.length; j++) {
GraphicsDevice gd = gs[j];
GraphicsConfiguration[] gc = gd.getConfigurations();
for (int i=0; i < gc.length; i++) {
Rectangle gcBounds = gc[i].getBounds();
Point loc = mainWindow.getLocationOnScreen();
if (gcBounds.contains(loc)) {
System.out.println("at " + j + " screen");
int x = gcBounds.x + (gcBounds.width - mainWindow.getWidth()) / 2;
int y = gcBounds.y + (gcBounds.height - mainWindow.getHeight()) / 2;
mainWindow.setLocation(x, y);
int x = loc.getX() + (mainWindow.getWidth() - getWidth()) / 2;
int y = loc.getY() + (mainWindow.getHeight() - getHeight()) / 2;
setLocation(x, y);
break;
}
}
}
Related
I have a problem in my Java application has so many tool tips. Sometimes tool tip covers the button and I cannot click because it covers.
Do we have a way to make tool tip transparent to mouse event? Then I can click the button even if the tool tip covers it.
Since this source demonstrates how to create tool tips for buttons that can be 'clicked through', I guess the solution to the problem is 'change whatever is different in your code'.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class TooltipButtonClickTest {
private JComponent ui = null;
TooltipButtonClickTest() {
initUI();
}
public void initUI() {
if (ui!=null) return;
ui = new JPanel(new GridLayout(0,8,2,2));
ui.setBorder(new EmptyBorder(4,4,4,4));
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("action command: " + e.getActionCommand());
}
};
int sz = 15;
Insets pad = new Insets(sz,sz,sz,sz);
for (int i=1; i<65; i++) {
JButton b = new JButton(String.valueOf(i));
b.setMargin(pad);
b.setToolTipText("This is tool tip " +String.valueOf(i));
b.addActionListener(listener);
ui.add(b);
}
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
TooltipButtonClickTest o = new TooltipButtonClickTest();
JFrame f = new JFrame("???");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
Tool tip moves little bit upwards if location not enough in the bottom.
public static Point getToolTipLocation(JComponent component)
{
Point point = component.getLocationOnScreen();
int componentHeight = component.getHeight();
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(component.getGraphicsConfiguration());
int taskBarSize = scnMax.bottom;
FontMetrics metrics = component.getFontMetrics(component.getFont());
int height = metrics.getHeight();
int lines = 1;
//Tool tip location shifted to up if screen does not have space in the bottom.
if (point.y + componentHeight + taskBarSize + (height * lines) > screenHeight)
{
int xPos = component.getWidth() / 2;
return new Point(xPos, -((height * lines) + 25));
}
return null;
}
Then override the getToolTipLocation() method in JButton and call above method works fine for me.
I'm wondering why my game objects are not showing. I am able to draw to the JPanel if I use g.drawRect outside of the for-each loop, but calling PipeObject inside of the loop doesn't seem to work for me. Am I doing something wrong here? Thanks for any help or solutions.
This is an updated version of my old question, which can be found here.
UPDATE - The pipes are drawing correctly, but not moving to the left by calling pipe.move().
Game
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
//import javax.swing.border.EmptyBorder;
import javax.swing.SwingUtilities;
public class Game {
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
final CardLayout cl = new CardLayout();
final JPanel gui = new JPanel(cl);
// remove if no border is needed
//gui.setBorder(new EmptyBorder(10,10,10,10));
JPanel menu = new JPanel(new GridBagLayout());
JButton playGame = new JButton("Play!");
ActionListener playGameListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(gui, "game");
}
};
playGame.addActionListener(playGameListener);
Insets margin = new Insets(20, 50, 20, 50);
playGame.setMargin(margin);
menu.add(playGame);
gui.add(menu);
cl.addLayoutComponent(menu, "menu");
final JPanel pipes = new Pipes();
gui.add(pipes);
cl.addLayoutComponent(pipes, "game");
JFrame f = new JFrame("PipeGame");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See https://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Pipes
import java.util.*;
import java.awt.Graphics;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Pipes extends JPanel {
boolean gameNotOver = true;
int x1 = 754;
int y1 = setHeightVal();
int y2 = setHeightVal();
int y3 = setHeightVal();
List<PipeObject> pipes = new ArrayList<PipeObject>();
public Pipes() {
pipes.add(new PipeObject(x1, y1));
pipes.add(new PipeObject(x1 + 300, y2));
pipes.add(new PipeObject(x1 + 600, y3));
}
public void drawEndlessPipes() {
if (gameNotOver) {
Timer pipeSpeed = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (PipeObject pipe : pipes) {
pipe.move();
}
}
});
pipeSpeed.start();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (PipeObject pipe : pipes) {
pipe.drawPipe(g);
}
}
public int setHeightVal() { //Get a random number and select a preset height
int num = (int)(9*Math.random() + 1);
int val = 0;
if (num == 9)
{
val = 295;
}
else if (num == 8)
{
val = 246;
}
else if (num == 7)
{
val = 216;
}
else if (num == 6)
{
val = 185;
}
else if (num == 5)
{
val = 156;
}
else if (num == 4)
{
val = 125;
}
else if (num == 3)
{
val = 96;
}
else if (num == 2)
{
val = 66;
}
else
{
val = 25;
}
return val;
}
public Dimension getPreferredSize() {
return new Dimension(751,501);
}
}
PipeObject
import java.awt.Graphics;
public class PipeObject {
//Declare and initialiaze variables
int x1;
int x2 = 75; //pipe width, total is 83
int y1 = -1; // Y should be -1
int y2;
int gap = 130; //gap height
public PipeObject(int x, int y) {
this.x1 = x;
this.y2 = y;
}
public void drawPipe(Graphics g/*, int x1, int y2*/) {
g.drawRect(x1,y1,x2,y2); //Draw part 1
g.drawRect(x1-3,y2-1,x2+6,25); //Draw part 2
g.drawRect(x1-3,y2+25+gap,x2+6,25); //Draw part 3
g.drawRect(x1,y2+25+gap+25,x2,500-y2-49-gap); //Draw part 4
}
public void move() {
x1--;
}
public int getMyX() { //To determine where the pipe is horizontally
return x1-3;
}
public int getMyY() { //To determine where the pipe is vertically
return y2+25;
}
}
You are painting the pipes beyond the visual bounds of the panel. The dimension of Pipes panel is 751x501, but the pipes begin at x1 = 754. Try changing x1 to 1 in Pipes and you should see the three pipes. Or, you can maximize the frame, and you should see the missing pipes far on the right side.
As CyberStorm said, you never called drawEndlessPipes() to start the swing timer at all.
Also I don't see a repaint() call to Pipes panel inside your timer, just changing the x value won't magically move anything, you need to call repaint() in Pipes.java to make it repaint the screen and show animation.
So here it goes:
Modified Runnable in Game.java
Runnable r = new Runnable() {
#Override
public void run() {
// the GUI as seen by the user (without frame)
final CardLayout cl = new CardLayout();
final JPanel gui = new JPanel(cl);
// remove if no border is needed
//gui.setBorder(new EmptyBorder(10,10,10,10));
JPanel menu = new JPanel(new GridBagLayout());
JButton playGame = new JButton("Play!");
ActionListener playGameListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cl.show(gui, "game");
}
};
playGame.addActionListener(playGameListener);
Insets margin = new Insets(20, 50, 20, 50);
playGame.setMargin(margin);
menu.add(playGame);
gui.add(menu);
cl.addLayoutComponent(menu, "menu");
final JPanel pipes = new Pipes();
gui.add(pipes);
cl.addLayoutComponent(pipes, "game");
JFrame f = new JFrame("PipeGame");
f.add(gui);
// Ensures JVM closes after frame(s) closed and
// all non-daemon threads are finished
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// See http://stackoverflow.com/a/7143398/418556 for demo.
f.setLocationByPlatform(true);
// ensures the frame is the minimum size it needs to be
// in order display the components within it
f.pack();
// should be done last, to avoid flickering, moving,
// resizing artifacts.
f.setVisible(true);
((Pipes) pipes).drawEndlessPipes();
}
};
Modified drawEndlessPipes() in Pipes.java
public void drawEndlessPipes() {
if (gameNotOver) {
Timer pipeSpeed = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (PipeObject pipe : pipes) {
pipe.move();
Pipes.this.repaint();
}
}
});
pipeSpeed.start();
}
}
I'm struggling with this code, I want to click on one of the cells of the grid, made by JPanel objects, and in that cell make appear a label with the index of that cell. I made a method to add final vars and return the JPanel with that label. It's not working. How can I do this?
public MyTest01(int width, int length) { //constructor
frame.setLayout(new GridLayout(width, length)); //set layout
JPanel temp = null;
JLabel l;
for (int y = 0; y < length; y++) {
for (int x = 0; x < width; x++) {
temp = new JPanel();
temp.setBorder(new LineBorder(Color.black, 1));
temp=doStuff(temp,x,y);
frame.add(temp);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); //sets appropriate size for frame
frame.setVisible(true); //makes frame visible
}
public static JPanel doStuff( final JPanel temp,final int x, final int y) {
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
JLabel l = new JLabel("("+x+" - "+y+")");
temp.add(l);
}
};
return temp;
}
You never add the listener to the JPanel.
You need to revalidate and repaint the JPanel after adding a component (JLabel);
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
JLabel l = new JLabel("(" + x + " - " + y + ")");
temp.add(l);
temp.revalidate();; <-------- revalidate
temp.repaint(); <-------- repaint
}
};
temp.addMouseListener(mouseListener); <-------- add listener
return temp;
Here is the working code
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class MyTest {
JFrame frame = new JFrame();
public MyTest(int width, int length) { //constructor
frame.setLayout(new GridLayout(width, length)); //set layout
JPanel temp = null;
JLabel l;
for (int y = 0; y < length; y++) {
for (int x = 0; x < width; x++) {
temp = new JPanel();
temp.setBorder(new LineBorder(Color.black, 1));
temp = doStuff(temp, x, y);
frame.add(temp);
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack(); //sets appropriate size for frame
frame.setVisible(true); //makes frame visible
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyTest(3, 3);
}
});
}
public static JPanel doStuff(final JPanel temp, final int x, final int y) {
MouseListener mouseListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mouseEvent) {
JLabel l = new JLabel("(" + x + " - " + y + ")");
temp.add(l);
temp.revalidate();;
temp.repaint();
}
};
temp.addMouseListener(mouseListener);
return temp;
}
}
So I have a JSplitPane, and two JPanels - one on top, one on the bottom. In both panels I overrode the paintComponent method and added my own graphics. In the bottom panel, I wanted to add an animation. When the panel does not repaint, it's fine, but as soon as the Timer (javax.swing.Timer) starts to call repaints, the bottom panel mimics the appearance of the top panel and glitches out. The actual animations are not refreshed, but rather it keeps on adding (like a dragged paintbrush instead of a moving object).
Here's the code for the Bottom Panel class:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JPanel;
import javax.swing.Timer;
public class WaitControls extends JPanel {
private int pos;
public WaitControls(){
setBackground(Color.gray);
pos = 0;
}
public void progress(){
//animation timer:
Timer timer = new Timer(30, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
pos++;
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g){
g.fillRect(pos, pos, 10, 20);
}
}
And here's the code for the Splitpane class:
//my classes (imported packages)
import rcc.controls.ControlPanel;
import rcc.controls.InitControls;
import rcc.controls.WaitControls;
import rcc.video.Screen;
import javax.swing.JSplitPane;
public class MainPanel extends JSplitPane{
public RCC rcc;
public Screen screen;
private int height;
public ControlPanel curPanel;
public MainPanel(RCC rcc, Screen screen, int height){
super(JSplitPane.VERTICAL_SPLIT);
this.rcc = rcc;
this.screen = screen;
this.height = height;
setDividerSize(2);
setEnabled(false);
setTopComponent(screen);
setToInitControls();
}
//sets the control panel to init controls ***WORKS FINE***
public void setToInitControls(){
InitControls initCtrls = new InitControls(this);
setBottomComponent(initCtrls);
curPanel = initCtrls;
setDividerLocation(height / 4 * 3);
}
//sets the control panel to wait controls (trying to connect) ***GLITCHES***
public void setToWaitControls(){
WaitControls waitCtrls = new WaitControls();
setBottomComponent(waitCtrls);
curPanel = waitCtrls;
setDividerLocation(height / 4 * 3);
waitCtrls.progress();
}
}
The top panel is a bit complicated. It involves mouse action (including a MouseEntered listener) and animates to interact with user mouse input.
The strange thing is, I have another bottom panel that was swapped out that also uses animations, and a timer, and does not have this glitch.
Any ideas what may have caused this? Thank you for all your help!
I can't imagine how your animations works,
1/ but if animation(s) depends of by any of Listener then Timer must be Timer#restart();
2/ check (example), how to pass addNotify()/removeNotify() for start/stop animatiom(s)
NOTE required fullHD monitor for better output or change code line
for (int iPanels = 0; iPanels < 3; iPanels++) {
to
for (int iPanels = 0; iPanels < 2; iPanels++) {
Example:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class AnimationBackground {
public AnimationBackground() {
Random random = new Random();
JFrame frame = new JFrame("Animation Background");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLayout(new GridLayout(0, 3, 10, 10));
for (int iPanels = 0; iPanels < 3; iPanels++) {
final MyJPanel panel = new MyJPanel();
panel.setBackground(Color.BLACK);
for (int i = 0; i < 50; i++) {
Star star = new Star(new Point(random.nextInt(490), random.nextInt(490)));
star.setColor(new Color(100 + random.nextInt(155), 100 + random.nextInt(155), 100 + random.nextInt(155)));
star.setxIncr(-3 + random.nextInt(7));
star.setyIncr(-3 + random.nextInt(7));
panel.add(star);
}
panel.setLayout(new GridLayout(10, 1));
JLabel label = new JLabel("This is a Starry background.", JLabel.CENTER);
label.setForeground(Color.WHITE);
panel.add(label);
JPanel stopPanel = new JPanel();
stopPanel.setOpaque(false);
stopPanel.add(new JButton(new AbstractAction("Stop this madness!!") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.stopAnimation();
}
}));
panel.add(stopPanel);
JPanel startPanel = new JPanel();
startPanel.setOpaque(false);
startPanel.add(new JButton(new AbstractAction("Start moving...") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
panel.startAnimation();
}
}));
panel.add(startPanel);
frame.add(panel);
}
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AnimationBackground animationBackground = new AnimationBackground();
}
});
}
class Star extends Polygon {
private static final long serialVersionUID = 1L;
private Point location = null;
private Color color = Color.YELLOW;
private int xIncr, yIncr;
static final int WIDTH = 500, HEIGHT = 500;
Star(Point location) {
int x = location.x;
int y = location.y;
this.location = location;
this.addPoint(x, y + 8);
this.addPoint(x + 8, y + 8);
this.addPoint(x + 11, y);
this.addPoint(x + 14, y + 8);
this.addPoint(x + 22, y + 8);
this.addPoint(x + 17, y + 12);
this.addPoint(x + 21, y + 20);
this.addPoint(x + 11, y + 14);
this.addPoint(x + 3, y + 20);
this.addPoint(x + 6, y + 12);
}
public void setColor(Color color) {
this.color = color;
}
public void move() {
if (location.x < 0 || location.x > WIDTH) {
xIncr = -xIncr;
}
if (location.y < 0 || location.y > WIDTH) {
yIncr = -yIncr;
}
translate(xIncr, yIncr);
location.setLocation(location.x + xIncr, location.y + yIncr);
}
public void setxIncr(int xIncr) {
this.xIncr = xIncr;
}
public void setyIncr(int yIncr) {
this.yIncr = yIncr;
}
public Color getColor() {
return color;
}
}
class MyJPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArrayList<Star> stars = new ArrayList<Star>();
private Timer timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Star star : stars) {
star.move();
}
repaint();
}
});
public void stopAnimation() {
if (timer.isRunning()) {
timer.stop();
}
}
public void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
#Override
public void addNotify() {
super.addNotify();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
MyJPanel() {
this.setPreferredSize(new Dimension(520, 520));
}
public void add(Star star) {
stars.add(star);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Star star : stars) {
g.setColor(star.getColor());
g.fillPolygon(star);
}
}
}
}
So I am trying to click and drag a JLabel around a JFrame. The following code allows a JLabel to be moved around the screen when the mouse is pressed / dragged at any point on the screen, but I am not sure how to add a second ActionListener to check if the mouse is clicking on the label, assuming that is the solution.
I would like to have multiple JLabels on the screen so that the only label being moved is the one that the mouse has clicked and is now dragging.
Thanks.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class test extends JFrame implements MouseMotionListener {
private JPanel panel = new JPanel(null);
private JLabel dragLabel = new JLabel("drag test");
private int mouseX = 200;
private int mouseY = 200;
public test() {
this.add(panel);
panel.setBackground(Color.WHITE);
panel.add(dragLabel);
dragLabel.setForeground(Color.RED);
dragLabel.setBounds(mouseX, mouseY, 100, 50);
panel.addMouseMotionListener(this);
}
#Override
public void mouseDragged(MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
dragLabel.setBounds(mouseX, mouseY, 100, 50);
}
#Override
public void mouseMoved(MouseEvent e) {}
public static void main(String[] args) {
test frame = new test();
frame.setVisible(true);
frame.setSize(600, 400);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Another way to do this is to add the JLabel to a JLayeredPane or to a JPanel held by a JLayeredPane and add a MouseAdapter as the JLayeredPane's MouseListener and MouseMotionListener. Then when clicking on the label, move it to the JLayeredPane's JLayeredPane.DRAG_LAYER so it moves on top of everything else, then place the JLabel on whichever is the most appropriate level on mouse release. I've found this to work well when moving chess pieces on a chess board, for instance, and you want to make sure that the piece you're moving is displayed above all the other pieces when dragging.
Addition: You've probably left this thread, but if you come back, or for the benefit of others, I wanted to clarify what I meant by using a JLayeredPane by posting an example.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DragLabelOnLayeredPane extends JLayeredPane {
public static final int WIDTH = 680;
public static final int HEIGHT = 480;
private static final int GRID_ROWS = 8;
private static final int GRID_COLS = 6;
private static final int GAP = 3;
private static final Dimension LAYERED_PANE_SIZE = new Dimension(WIDTH, HEIGHT);
private static final Dimension LABEL_SIZE = new Dimension(60, 40);
private GridLayout gridlayout = new GridLayout(GRID_ROWS, GRID_COLS, GAP, GAP);
private JPanel backingPanel = new JPanel(gridlayout);
private JPanel[][] panelGrid = new JPanel[GRID_ROWS][GRID_COLS];
private JLabel redLabel = new JLabel("Red", SwingConstants.CENTER);
private JLabel blueLabel = new JLabel("Blue", SwingConstants.CENTER);
public DragLabelOnLayeredPane() {
backingPanel.setSize(LAYERED_PANE_SIZE);
backingPanel.setLocation(2 * GAP, 2 * GAP);
backingPanel.setBackground(Color.black);
for (int row = 0; row < GRID_ROWS; row++) {
for (int col = 0; col < GRID_COLS; col++) {
panelGrid[row][col] = new JPanel(new GridBagLayout());
backingPanel.add(panelGrid[row][col]);
}
}
redLabel.setOpaque(true);
redLabel.setBackground(Color.red.brighter().brighter());
redLabel.setPreferredSize(LABEL_SIZE);
panelGrid[4][3].add(redLabel);
blueLabel.setOpaque(true);
blueLabel.setBackground(Color.blue.brighter().brighter());
blueLabel.setPreferredSize(LABEL_SIZE);
panelGrid[1][1].add(blueLabel);
backingPanel.setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setPreferredSize(LAYERED_PANE_SIZE);
add(backingPanel, JLayeredPane.DEFAULT_LAYER);
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
private class MyMouseAdapter extends MouseAdapter {
private JLabel dragLabel = null;
private int dragLabelWidthDiv2;
private int dragLabelHeightDiv2;
private JPanel clickedPanel = null;
#Override
public void mousePressed(MouseEvent me) {
clickedPanel = (JPanel) backingPanel.getComponentAt(me.getPoint());
Component[] components = clickedPanel.getComponents();
if (components.length == 0) {
return;
}
// if we click on jpanel that holds a jlabel
if (components[0] instanceof JLabel) {
// remove label from panel
dragLabel = (JLabel) components[0];
clickedPanel.remove(dragLabel);
clickedPanel.revalidate();
clickedPanel.repaint();
dragLabelWidthDiv2 = dragLabel.getWidth() / 2;
dragLabelHeightDiv2 = dragLabel.getHeight() / 2;
int x = me.getPoint().x - dragLabelWidthDiv2;
int y = me.getPoint().y - dragLabelHeightDiv2;
dragLabel.setLocation(x, y);
add(dragLabel, JLayeredPane.DRAG_LAYER);
repaint();
}
}
#Override
public void mouseDragged(MouseEvent me) {
if (dragLabel == null) {
return;
}
int x = me.getPoint().x - dragLabelWidthDiv2;
int y = me.getPoint().y - dragLabelHeightDiv2;
dragLabel.setLocation(x, y);
repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
if (dragLabel == null) {
return;
}
remove(dragLabel); // remove dragLabel for drag layer of JLayeredPane
JPanel droppedPanel = (JPanel) backingPanel.getComponentAt(me.getPoint());
if (droppedPanel == null) {
// if off the grid, return label to home
clickedPanel.add(dragLabel);
clickedPanel.revalidate();
} else {
int r = -1;
int c = -1;
searchPanelGrid: for (int row = 0; row < panelGrid.length; row++) {
for (int col = 0; col < panelGrid[row].length; col++) {
if (panelGrid[row][col] == droppedPanel) {
r = row;
c = col;
break searchPanelGrid;
}
}
}
if (r == -1 || c == -1) {
// if off the grid, return label to home
clickedPanel.add(dragLabel);
clickedPanel.revalidate();
} else {
droppedPanel.add(dragLabel);
droppedPanel.revalidate();
}
}
repaint();
dragLabel = null;
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("DragLabelOnLayeredPane");
frame.getContentPane().add(new DragLabelOnLayeredPane());
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();
}
});
}
}
Please feel free to post any questions, need for clarification, or corrections.
Inspired by your code and user compilex's answer, follows demonstration:
Full code:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
/**
* A demonstration of moving around labels in a panel.
* <p>
* Some labels show up layed out in a grid. Then the
* user can drag any label anywhere on the panel.
* </p>
*/
public class LabelDragger {
public static void main(final String[] args) {
final int labelRows = 5, //How many rows of labels.
labelColumns = 5, //How many columns of labels.
labelWidth = 55, //Width for each label.
labelHeight = 20; //Height for each label.
//Border colors for labels:
final Color[] colors = new Color[]{Color.BLUE, Color.GREEN, Color.BLACK, Color.GRAY};
final Random prng = new Random(); //For selecting border color for each label.
final JPanel dragP = new JPanel(null); //Nicely set to null! :D Did not know that trick.
//Creating the listener for the panel:
final MouseAdapter ma = new MouseAdapter() {
private JLabel selectedLabel = null; //Clicked label.
private Point selectedLabelLocation = null; //Location of label in panel when it was clicked.
private Point panelClickPoint = null; //Panel's click point.
//Selection of label occurs upon pressing on the panel:
#Override
public void mousePressed(final MouseEvent e) {
//Find which label is at the press point:
final Component pressedComp = dragP.findComponentAt(e.getX(), e.getY());
//If a label is pressed, store it as selected:
if (pressedComp != null && pressedComp instanceof JLabel) {
selectedLabel = (JLabel) pressedComp;
selectedLabelLocation = selectedLabel.getLocation();
panelClickPoint = e.getPoint();
//Added the following 2 lines in order to make selectedLabel
//paint over all others while it is pressed and dragged:
dragP.setComponentZOrder(selectedLabel, 0);
selectedLabel.repaint();
}
else {
selectedLabel = null;
selectedLabelLocation = null;
panelClickPoint = null;
}
}
//Moving of selected label occurs upon dragging in the panel:
#Override
public void mouseDragged(final MouseEvent e) {
if (selectedLabel != null
&& selectedLabelLocation != null
&& panelClickPoint != null) {
final Point newPanelClickPoint = e.getPoint();
//The new location is the press-location plus the length of the drag for each axis:
final int newX = selectedLabelLocation.x + (newPanelClickPoint.x - panelClickPoint.x),
newY = selectedLabelLocation.y + (newPanelClickPoint.y - panelClickPoint.y);
selectedLabel.setLocation(newX, newY);
}
}
};
dragP.addMouseMotionListener(ma); //For mouseDragged().
dragP.addMouseListener(ma); //For mousePressed().
//Filling the panel with labels:
for (int row = 0; row < labelRows; ++row)
for (int col = 0; col < labelColumns; ++col) {
//Create label for (row, col):
final JLabel lbl = new JLabel("Label" + (row * labelColumns + col));
lbl.setHorizontalAlignment(JLabel.CENTER);
//lbl.setVerticalAlignment(JLabel.CENTER);
lbl.setBounds(col * labelWidth, row * labelHeight, labelWidth, labelHeight); //Grid-like positioning.
lbl.setBorder(new LineBorder(colors[prng.nextInt(colors.length)], 2)); //Set a border for clarity.
//Add label to panel:
dragP.add(lbl);
}
//Creating and showing the main frame:
final JFrame frame = new JFrame(LabelDragger.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//The size of the content pane adds some extra room for moving the labels:
final Dimension paneSize = new Dimension((int)(1.5 * labelWidth * labelColumns),
(int)(1.5 * labelHeight * labelRows));
frame.getContentPane().setPreferredSize(paneSize);
frame.getContentPane().add(dragP);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Explanations are added as comments.
Tips:
Take a look at the documentation on Container.findComponentAt(int x, int y), if you are going to add Components on the dragP Container, other than "draggable" labels.
Also, you can instead use Container.getComponentAt(int x, int y), in this case. I suggest you read their (small) documentation first.
Add a mouse listener to the label instead of the panel. (You might still need a mouse listener on the panel for the dragging but at least the one on the label can tell you if it was selected).
Create two global variables:
int x_pressed = 0;
int y_pressed = 0;
then create two events (mousePressed and mouseDragged over JLabel):
lbl_banner.addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e) {
//catching the current values for x,y coordinates on screen
x_pressed = e.getX();
y_pressed = e.getY();
}
});
lbl_banner.addMouseMotionListener(new MouseMotionAdapter(){
#Override
public void mouseDragged(MouseEvent e){
//and when the Jlabel is dragged
setLocation(e.getXOnScreen() - x_pressed, e.getYOnScreen() - y_pressed);
}
});