I have a menu that has a variety of buttons on display, I'm able to make the buttons call their respective JPanels when clicked. The thing is I would like to make the Jpanel slide in when its called instead of instantaneously popping in. I tried using tween engine and as Java beginner, I find it really overwhelming, so I decided to use timed animation. I was able to make the Jpanel on top to slide to one side but for some reason the next panel doesn't want to display, im really tired, can someone help please! There code is below:
public class Listener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
new Timer(0, new ActionListener() {
public void actionPerformed(ActionEvent e) {
mainpane.setLocation(mainpane.getX() - 10, 0);
if (mainpane.getX() + mainpane.getWidth() == 0)
{
((Timer) e.getSource()).stop();
System.out.println("Timer stopped");
}
}
}).start();
}
}
Sliding panels can be tricky. Here is some starter code. Modify to fit
your needs. Add error checking and exception handling as necessary.
This example uses JButtons and a JTree as content but you can use just about any type of content.
Usage:
static public void main(final String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
final JFrame jFrame = new JFrame() {
{
final PanelSlider42<JFrame> slider = new PanelSlider42<JFrame>(this);
final JPanel jPanel = slider.getBasePanel();
slider.addComponent(new JButton("1"));
slider.addComponent(new JButton("22"));
slider.addComponent(new JButton("333"));
slider.addComponent(new JButton("4444"));
getContentPane().add(jPanel);
setSize(300, 300);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
}
};
}
});
}
The impl is lengthy ...
package com.java42.example.code;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
public class PanelSlider42<ParentType extends Container> {
private static final int RIGHT = 0x01;
private static final int LEFT = 0x02;
private static final int TOP = 0x03;
private static final int BOTTOM = 0x04;
private final JPanel basePanel = new JPanel();
private final ParentType parent;
private final Object lock = new Object();
private final ArrayList<Component> jPanels = new ArrayList<Component>();
private final boolean useSlideButton = true;
private boolean isSlideInProgress = false;
private final JPanel glassPane;
{
glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.addMouseListener(new MouseAdapter() {
});
glassPane.addMouseMotionListener(new MouseMotionAdapter() {
});
glassPane.addKeyListener(new KeyAdapter() {
});
}
public PanelSlider42(final ParentType parent) {
if (parent == null) {
throw new RuntimeException("ProgramCheck: Parent can not be null.");
}
if ((parent instanceof JFrame) || (parent instanceof JDialog) || (parent instanceof JWindow) || (parent instanceof JPanel)) {
}
else {
throw new RuntimeException("ProgramCheck: Parent type not supported. " + parent.getClass().getSimpleName());
}
this.parent = parent;
attach();
basePanel.setSize(parent.getSize());
basePanel.setLayout(new BorderLayout());
if (useSlideButton) {
final JPanel statusPanel = new JPanel();
basePanel.add(statusPanel, BorderLayout.SOUTH);
statusPanel.add(new JButton("Slide Left") {
private static final long serialVersionUID = 9204819004142223529L;
{
setMargin(new Insets(0, 0, 0, 0));
}
{
addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
slideLeft();
}
});
}
});
statusPanel.add(new JButton("Slide Right") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
slideRight();
}
});
}
});
statusPanel.add(new JButton("Slide Up") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
slideTop();
}
});
}
});
statusPanel.add(new JButton("Slide Down") {
{
setMargin(new Insets(0, 0, 0, 0));
}
private static final long serialVersionUID = 9204819004142223529L;
{
addActionListener(new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
slideBottom();
}
});
}
});
}
}
public JPanel getBasePanel() {
return basePanel;
}
private void attach() {
final ParentType w = this.parent;
if (w instanceof JFrame) {
final JFrame j = (JFrame) w;
if (j.getContentPane().getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.getContentPane().add(basePanel);
}
if (w instanceof JDialog) {
final JDialog j = (JDialog) w;
if (j.getContentPane().getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.getContentPane().add(basePanel);
}
if (w instanceof JWindow) {
final JWindow j = (JWindow) w;
if (j.getContentPane().getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.getContentPane().add(basePanel);
}
if (w instanceof JPanel) {
final JPanel j = (JPanel) w;
if (j.getComponents().length > 0) {
throw new RuntimeException("ProgramCheck: Parent already contains content.");
}
j.add(basePanel);
}
}
public void addComponent(final Component component) {
if (jPanels.contains(component)) {
}
else {
jPanels.add(component);
if (jPanels.size() == 1) {
basePanel.add(component);
}
component.setSize(basePanel.getSize());
component.setLocation(0, 0);
}
}
public void removeComponent(final Component component) {
if (jPanels.contains(component)) {
jPanels.remove(component);
}
}
public void slideLeft() {
slide(LEFT);
}
public void slideRight() {
slide(RIGHT);
}
public void slideTop() {
slide(TOP);
}
public void slideBottom() {
slide(BOTTOM);
}
private void enableUserInput(final ParentType w) {
if (w instanceof JFrame) {
((JFrame) w).getGlassPane().setVisible(false);
}
if (w instanceof JDialog) {
((JDialog) w).getGlassPane().setVisible(false);
}
if (w instanceof JWindow) {
((JWindow) w).getGlassPane().setVisible(false);
}
}
private void disableUserInput(final ParentType w) {
if (w instanceof JFrame) {
((JFrame) w).setGlassPane(glassPane);
}
if (w instanceof JDialog) {
((JDialog) w).setGlassPane(glassPane);
}
if (w instanceof JWindow) {
((JWindow) w).setGlassPane(glassPane);
}
glassPane.setVisible(true);
}
private void enableTransparentOverylay() {
if (parent instanceof JFrame) {
((JFrame) parent).getContentPane().setBackground(jPanels.get(0).getBackground());
parent.remove(basePanel);
parent.validate();
}
if (parent instanceof JDialog) {
((JDialog) parent).getContentPane().setBackground(jPanels.get(0).getBackground());
parent.remove(basePanel);
parent.validate();
}
if (parent instanceof JWindow) {
((JWindow) parent).getContentPane().setBackground(jPanels.get(0).getBackground());
parent.remove(basePanel);
parent.validate();
}
}
private void slide(final int slideType) {
if (!isSlideInProgress) {
isSlideInProgress = true;
final Thread t0 = new Thread(new Runnable() {
#Override
public void run() {
parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
disableUserInput(parent);
slide(true, slideType);
enableUserInput(parent);
parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
isSlideInProgress = false;
}
});
t0.setDaemon(true);
t0.start();
}
else {
Toolkit.getDefaultToolkit().beep();
}
}
private void slide(final boolean useLoop, final int slideType) {
if (jPanels.size() < 2) {
System.err.println("Not enough panels");
return;
}
synchronized (lock) {
Component componentOld = null;
Component componentNew = null;
if ((slideType == LEFT) || (slideType == TOP)) {
componentNew = jPanels.remove(jPanels.size() - 1);
componentOld = jPanels.get(0);
jPanels.add(0, componentNew);
}
if ((slideType == RIGHT) || (slideType == BOTTOM)) {
componentOld = jPanels.remove(0);
jPanels.add(componentOld);
componentNew = jPanels.get(0);
}
final int w = componentOld.getWidth();
final int h = componentOld.getHeight();
final Point p1 = componentOld.getLocation();
final Point p2 = new Point(0, 0);
if (slideType == LEFT) {
p2.x += w;
}
if (slideType == RIGHT) {
p2.x -= w;
}
if (slideType == TOP) {
p2.y += h;
}
if (slideType == BOTTOM) {
p2.y -= h;
}
componentNew.setLocation(p2);
int step = 0;
if ((slideType == LEFT) || (slideType == RIGHT)) {
step = (int) (((float) parent.getWidth() / (float) Toolkit.getDefaultToolkit().getScreenSize().width) * 40.f);
}
else {
step = (int) (((float) parent.getHeight() / (float) Toolkit.getDefaultToolkit().getScreenSize().height) * 20.f);
}
step = step < 5 ? 5 : step;
basePanel.add(componentNew);
basePanel.revalidate();
if (useLoop) {
final int max = (slideType == LEFT) || (slideType == RIGHT) ? w : h;
final long t0 = System.currentTimeMillis();
for (int i = 0; i != (max / step); i++) {
switch (slideType) {
case LEFT: {
p1.x -= step;
componentOld.setLocation(p1);
p2.x -= step;
componentNew.setLocation(p2);
break;
}
case RIGHT: {
p1.x += step;
componentOld.setLocation(p1);
p2.x += step;
componentNew.setLocation(p2);
break;
}
case TOP: {
p1.y -= step;
componentOld.setLocation(p1);
p2.y -= step;
componentNew.setLocation(p2);
break;
}
case BOTTOM: {
p1.y += step;
componentOld.setLocation(p1);
p2.y += step;
componentNew.setLocation(p2);
break;
}
default:
new RuntimeException("ProgramCheck").printStackTrace();
break;
}
try {
Thread.sleep(500 / (max / step));
} catch (final Exception e) {
e.printStackTrace();
}
}
final long t1 = System.currentTimeMillis();
}
componentOld.setLocation(-10000, -10000);
componentNew.setLocation(0, 0);
}
}
}
I have searched for that problem some time ago.I found this sample code somewhere - saved in my evernote for future reference. This is the shortest way to implement that when I googled that in the past
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SlidingPanel {
JPanel panel;
public void makeUI() {
panel = new JPanel();
panel.setBackground(Color.RED);
panel.setBounds(0, 0, 400, 400);
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
((JButton) e.getSource()).setEnabled(false);
new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent e) {
panel.setLocation(panel.getX() - 1, 0);
if (panel.getX() + panel.getWidth() == 0) {
((Timer) e.getSource()).stop();
System.out.println("Timer stopped");
}
}
}).start();
}
});
panel.add(button);
JFrame frame = new JFrame("Sliding Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setLayout(null);
frame.add(panel);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SlidingPanel().makeUI();
}
});
}
}
Related
The following code is working in jdk 1.8 update 45 and in java 1.8 update 31 but NOT in java 1.8 update 45.
The program is a button moving back and forth until the user presses the button and makes it stop and the text is changed to "MOVE". When the button is pressed again, the button starts moving and the text is changed to "STOP".
In java 8 update 45, the button does not start moving but the text changes. Why?
package mainpackage;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class mainPanel implements ActionListener {
JFrame frame1;
JPanel panel1;
JButton button = new JButton("STOP");
boolean buttonPressed = false;
boolean move = true;
// 0 = left & 1 = right
int direction = 1;
int x = 0;
public static void main(String[] args) {
new mainPanel().loadGUI();
}
public void loadGUI() {
frame1 = new JFrame("Moving button");
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setVisible(true);
frame1.setSize(300, 58);
frame1.setResizable(false);
panel1 = new JPanel(); // Xwidth=294, Yheight=272
panel1.setSize(300, 30);
panel1.setLayout(null);
frame1.add(panel1);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame1.setLocation(dim.width/2-frame1.getSize().width/2, dim.height/2-frame1.getSize().height/2);
button.setSize(80, 30); //X, Y
panel1.add(button);
button.addActionListener(this);
while(true) moveButton();
}
public void moveButton() {
while(move == true) {
switch(direction) {
// left
case 0: {
while(x > 0) {
if(move == false) break;
button.setLocation(x, 0);
x--;
panel1.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(mainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
if(buttonPressed == true) {
direction = 0;
buttonPressed = false;
} else if(buttonPressed == false) {
direction = 1;
buttonPressed = false;
}
}
// right
case 1: {
while(x < panel1.getWidth() - button.getWidth()) {
if(move == false) break;
button.setLocation(x, 0);
x++;
panel1.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(mainPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
if(buttonPressed == true) {
direction = 1;
buttonPressed=false;
} else if(buttonPressed == false) {
direction=0;
buttonPressed=false;
}
}
}
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (move == true){
move=false;
} else if (move == false) {
move=true;
}
buttonPressed = true;
if((button.getText()).equals("STOP")) {
button.setText("MOVE");
} else button.setText("STOP");
}
}
Yours is broken code to begin with since it ignores Swing threading rules, and frankly I'm surprised that it worked in previous versions of Java. You're calling a while (true) loop that will tie up any thread that it is called in. Java 8 is correctly trying to start your Swing GUI on the Swing event thread, something that all Swing programs should do. If you get rid of the while (true) loops that risk being called on the Swing event dispatch thread, and instead use a Swing Timer your code should work. The Timer will run a loop in a background thread, but all code called repeatedly in its ActionListener will be called on the Swing event thread.
For example:
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MyMainPanel extends JPanel {
private static final int PREF_W = 300;
private static final int PREF_H = 30;
private static final int TIMER_DELAY = 20;
public static final int DELTA_X = 3;
private JButton moveButton = new JButton(new MoveButtonAction("Move"));
private Timer moveTimer = new Timer(TIMER_DELAY, new MoveTimerListener());
private boolean moveRight = true;
public MyMainPanel() {
moveButton.setSize(moveButton.getPreferredSize());
int y = (getPreferredSize().height - moveButton.getPreferredSize().height) / 2;
moveButton.setLocation(0, y);
setLayout(null); // !! lord I hate this
add(moveButton);
moveTimer.start();
}
#Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int prefW = Math.max(superSz.width, PREF_W);
int prefH = Math.max(superSz.height, PREF_H);
return new Dimension(prefW, prefH);
}
private class MoveButtonAction extends AbstractAction {
public MoveButtonAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
moveRight = !moveRight;
}
}
private class MoveTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (moveRight) {
if (moveButton.getLocation().x + moveButton.getWidth() >= getWidth()) {
moveRight = false;
}
} else {
if (moveButton.getLocation().x <= 0) {
moveRight = true;
}
}
int x = moveButton.getLocation().x + (moveRight ? DELTA_X : -DELTA_X);
int y = moveButton.getLocation().y;
moveButton.setLocation(new Point(x, y));
repaint();
}
}
private static void createAndShowGui() {
MyMainPanel mainPanel = new MyMainPanel();
JFrame frame = new JFrame("GUI Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
You will want to read up on Swing thread safety to see why your program is failing. See Lesson: Concurrency in Swing to see more on this.
Edit: code that stops and starts movement:
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyMainPanel extends JPanel {
private static final String MOVE = "Move";
private static final String STOP = "Stop";
private static final int PREF_W = 300;
private static final int PREF_H = 30;
private static final int TIMER_DELAY = 20;
public static final int DELTA_X = 3;
private MoveButtonAction moveButtonAction = new MoveButtonAction(STOP);
private JButton moveButton = new JButton(moveButtonAction);
private Timer moveTimer = new Timer(TIMER_DELAY, new MoveTimerListener());
private boolean moveRight = true;
public MyMainPanel() {
moveButton.setSize(moveButton.getPreferredSize());
int y = (getPreferredSize().height - moveButton.getPreferredSize().height) / 2;
moveButton.setLocation(0, y);
setLayout(null); // !! lord I hate this
add(moveButton);
moveTimer.start();
}
#Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int prefW = Math.max(superSz.width, PREF_W);
int prefH = Math.max(superSz.height, PREF_H);
return new Dimension(prefW, prefH);
}
private class MoveButtonAction extends AbstractAction {
public MoveButtonAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
if (MOVE.equals(getValue(NAME))) {
moveTimer.start();
putValue(NAME, STOP);
int mnemonic = (int) STOP.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
} else {
moveTimer.stop();
putValue(NAME, MOVE);
int mnemonic = (int) MOVE.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
AbstractButton button = (AbstractButton) e.getSource();
button.setSize(button.getPreferredSize());
repaint();
}
}
private class MoveTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (moveRight) {
if (moveButton.getLocation().x + moveButton.getWidth() >= getWidth()) {
moveRight = false;
}
} else {
if (moveButton.getLocation().x <= 0) {
moveRight = true;
}
}
int x = moveButton.getLocation().x + (moveRight ? DELTA_X : -DELTA_X);
int y = moveButton.getLocation().y;
moveButton.setLocation(new Point(x, y));
repaint();
}
}
private static void createAndShowGui() {
MyMainPanel mainPanel = new MyMainPanel();
JFrame frame = new JFrame("GUI Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Just added some methods and fixed some lines in your code, you can try it now:
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class mainPanel implements ActionListener {
JFrame frame1;
JPanel panel1;
JButton button = new JButton("START");
boolean buttonPressed = false;
boolean move = false;
Timer timer;
int direction = 1;
int x = 0;
public static void main(String[] args) {
new mainPanel().loadGUI();
}
public void loadGUI() {
frame1 = new JFrame("Moving button");
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setVisible(true);
frame1.setSize(300, 58);
frame1.setResizable(false);
panel1 = new JPanel(); // Xwidth=294, Yheight=272
panel1.setSize(300, 30);
panel1.setLayout(null);
frame1.add(panel1);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
frame1.setLocation(dim.width / 2 - frame1.getSize().width / 2, dim.height / 2 - frame1.getSize().height / 2);
button.setSize(80, 30); //X, Y
panel1.add(button);
button.addActionListener(this);
moveButton();
}
public void moveButton() {
if (timer == null) {
initTimer();
}
move = !move;
if (move) {
button.setText("STOP");
direction = (direction == 0) ? 1 : 0;
timer.start();
} else {
button.setText("MOVE");
timer.stop();
}
}
public void initTimer() {
timer = new Timer(10, (e) -> {
switch (direction) {
case 0: // right
x++;
if (x >= panel1.getWidth() - button.getWidth()) {
direction = 1;
}
break;
case 1: // left
x--;
if (x <= 0) {
direction = 0;
}
break;
}
button.setLocation(x, 0);
panel1.repaint();
});
}
#Override
public void actionPerformed(ActionEvent e) {
moveButton();
}
}
I work on jFrame project:
I have jlabel I want to move it in animation, and then move another jlabel
in other words, I want the first jlabel to move and then when it finished,the second jlabel
moves.
I've already tried and didn't succeed. I have a function to move one jlabel
and ifIi try to use it on both jlabels, both jlabels move at the same time, and I don't want this to happen.
Can you help me do it, thank you so much.
Here is the function that I have :
public void MoveForPlayer(JLabel PlayerCard)
{
int delay = q;
ActionListener taskPerformer = new ActionListener() {
int count=0;
#Override
public void actionPerformed(ActionEvent evt) {
if(count==20) {
((Timer)evt.getSource()).stop();
}
PlayerCard.setLocation((PlayerCard.getLocation().x-5), PlayerCard.getLocation().y+5);
count++;
}
};
new Timer(delay, taskPerformer).start();
}
You could take a look at something like this example which wraps animation layer around a GridBagLayout or someting like this which is based on the Universal Tween Engine
This is a modified version of the first example, which adds the ability to be notified when the animation for a particular layout sequence has finished.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import javax.swing.event.EventListenerList;
public class TestAnimatedLayout {
public static void main(String[] args) {
new TestAnimatedLayout();
}
public TestAnimatedLayout() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestAnimatedLayoutPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestAnimatedLayoutPane extends JPanel {
private Card card1, card2;
public TestAnimatedLayoutPane() {
AnimatedLayout animatedLayout = new AnimatedLayout(new GridBagLayout());
animatedLayout.addLayoutAnimationListener(new LayoutAnimationListener() {
#Override
public void layoutAnimationStopped(LayoutAnimationEvent evt) {
System.out.println("Stoppped");
if (card2 == null) {
card2 = new Card("2");
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridx = 1;
gbc.gridy = 0;
add(card2, gbc);
revalidate();
}
}
});
setLayout(animatedLayout);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.gridx = 0;
gbc.gridy = 0;
card1 = new Card("1");
add(card1, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class Card extends JLabel {
public Card(String text) {
super(text);
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
setBorder(new LineBorder(Color.BLACK));
setOpaque(true);
setBackground(Color.WHITE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 100);
}
}
public class LayoutAnimationEvent extends EventObject {
private Container parent;
public LayoutAnimationEvent(AnimatedLayout source, Container parent) {
super(source);
this.parent = parent;
}
public Container getParent() {
return parent;
}
public AnimatedLayout getAnimationLayout() {
return (AnimatedLayout) getSource();
}
}
public interface LayoutAnimationListener extends EventListener {
public void layoutAnimationStopped(LayoutAnimationEvent evt);
}
public class AnimatedLayout implements LayoutManager2 {
private EventListenerList listenerList = new EventListenerList();
private LayoutManager2 proxy;
private Map<Component, Rectangle> mapStart;
private Map<Component, Rectangle> mapTarget;
private Map<Container, Timer> mapTrips;
private Map<Container, Animator> mapAnimators;
public AnimatedLayout(LayoutManager2 proxy) {
this.proxy = proxy;
mapTrips = new WeakHashMap<>(5);
mapAnimators = new WeakHashMap<>(5);
}
public void addLayoutAnimationListener(LayoutAnimationListener listener) {
listenerList.add(LayoutAnimationListener.class, listener);
}
public void removeLayoutAnimationListener(LayoutAnimationListener listener) {
listenerList.add(LayoutAnimationListener.class, listener);
}
protected void fireAnimationStopped(Container parent) {
LayoutAnimationListener[] listeners = listenerList.getListeners(LayoutAnimationListener.class);
if (listeners != null && listeners.length > 0) {
LayoutAnimationEvent evt = new LayoutAnimationEvent(this, parent);
for (LayoutAnimationListener listener : listeners) {
listener.layoutAnimationStopped(evt);
}
}
}
#Override
public void addLayoutComponent(String name, Component comp) {
proxy.addLayoutComponent(name, comp);
}
#Override
public void removeLayoutComponent(Component comp) {
proxy.removeLayoutComponent(comp);
}
#Override
public Dimension preferredLayoutSize(Container parent) {
return proxy.preferredLayoutSize(parent);
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return proxy.minimumLayoutSize(parent);
}
#Override
public void layoutContainer(Container parent) {
Timer timer = mapTrips.get(parent);
if (timer == null) {
System.out.println("...create new trip");
timer = new Timer(125, new TripAction(parent));
timer.setRepeats(false);
timer.setCoalesce(false);
mapTrips.put(parent, timer);
}
System.out.println("trip...");
timer.restart();
}
protected void doLayout(Container parent) {
System.out.println("doLayout...");
mapStart = new HashMap<>(parent.getComponentCount());
for (Component comp : parent.getComponents()) {
mapStart.put(comp, (Rectangle) comp.getBounds().clone());
}
proxy.layoutContainer(parent);
LayoutConstraints constraints = new LayoutConstraints();
for (Component comp : parent.getComponents()) {
Rectangle bounds = comp.getBounds();
Rectangle startBounds = mapStart.get(comp);
if (!mapStart.get(comp).equals(bounds)) {
if (startBounds.x == 0 && startBounds.y == 0) {
startBounds.x = (parent.getWidth() - startBounds.width) / 2;
startBounds.y = (parent.getHeight() - startBounds.height) / 2;
}
comp.setBounds(startBounds);
constraints.add(comp, startBounds, bounds);
}
}
System.out.println("Items to layout " + constraints.size());
if (constraints.size() > 0) {
Animator animator = mapAnimators.get(parent);
if (animator == null) {
animator = new Animator(this, parent, constraints);
mapAnimators.put(parent, animator);
} else {
animator.setConstraints(constraints);
}
animator.restart();
} else {
if (mapAnimators.containsKey(parent)) {
Animator animator = mapAnimators.get(parent);
animator.stop();
mapAnimators.remove(parent);
}
}
}
#Override
public void addLayoutComponent(Component comp, Object constraints) {
proxy.addLayoutComponent(comp, constraints);
}
#Override
public Dimension maximumLayoutSize(Container target) {
return proxy.maximumLayoutSize(target);
}
#Override
public float getLayoutAlignmentX(Container target) {
return proxy.getLayoutAlignmentX(target);
}
#Override
public float getLayoutAlignmentY(Container target) {
return proxy.getLayoutAlignmentY(target);
}
#Override
public void invalidateLayout(Container target) {
proxy.invalidateLayout(target);
}
protected void animationDidStop(Container parent) {
fireAnimationStopped(parent);
}
protected class TripAction implements ActionListener {
private Container container;
public TripAction(Container container) {
this.container = container;
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("...trip");
mapTrips.remove(container);
doLayout(container);
}
}
}
public class LayoutConstraints {
private List<AnimationBounds> animationBounds;
public LayoutConstraints() {
animationBounds = new ArrayList<AnimationBounds>(25);
}
public void add(Component comp, Rectangle startBounds, Rectangle targetBounds) {
add(new AnimationBounds(comp, startBounds, targetBounds));
}
public void add(AnimationBounds bounds) {
animationBounds.add(bounds);
}
public int size() {
return animationBounds.size();
}
public AnimationBounds[] getAnimationBounds() {
return animationBounds.toArray(new AnimationBounds[animationBounds.size()]);
}
}
public class AnimationBounds {
private Component component;
private Rectangle startBounds;
private Rectangle targetBounds;
public AnimationBounds(Component component, Rectangle startBounds, Rectangle targetBounds) {
this.component = component;
this.startBounds = startBounds;
this.targetBounds = targetBounds;
}
public Rectangle getStartBounds() {
return startBounds;
}
public Rectangle getTargetBounds() {
return targetBounds;
}
public Component getComponent() {
return component;
}
public Rectangle getBounds(float progress) {
return calculateProgress(getStartBounds(), getTargetBounds(), progress);
}
}
public static Rectangle calculateProgress(Rectangle startBounds, Rectangle targetBounds, float progress) {
Rectangle bounds = new Rectangle();
if (startBounds != null && targetBounds != null) {
bounds.setLocation(calculateProgress(startBounds.getLocation(), targetBounds.getLocation(), progress));
bounds.setSize(calculateProgress(startBounds.getSize(), targetBounds.getSize(), progress));
}
return bounds;
}
public static Point calculateProgress(Point startPoint, Point targetPoint, float progress) {
Point point = new Point();
if (startPoint != null && targetPoint != null) {
point.x = calculateProgress(startPoint.x, targetPoint.x, progress);
point.y = calculateProgress(startPoint.y, targetPoint.y, progress);
}
return point;
}
public static Dimension calculateProgress(Dimension startSize, Dimension targetSize, float progress) {
Dimension size = new Dimension();
if (startSize != null && targetSize != null) {
size.width = calculateProgress(startSize.width, targetSize.width, progress);
size.height = calculateProgress(startSize.height, targetSize.height, progress);
}
return size;
}
public static int calculateProgress(int startValue, int endValue, float fraction) {
int value = 0;
int distance = endValue - startValue;
value = (int) ((float) distance * fraction);
value += startValue;
return value;
}
public class Animator implements ActionListener {
private Timer timer;
private LayoutConstraints constraints;
private int tick;
private Container parent;
private AnimatedLayout layout;
public Animator(AnimatedLayout layout, Container parent, LayoutConstraints constraints) {
setConstraints(constraints);
timer = new Timer(16, this);
timer.setRepeats(true);
timer.setCoalesce(true);
this.parent = parent;
this.layout = layout;
}
private void setConstraints(LayoutConstraints constraints) {
this.constraints = constraints;
}
public void restart() {
tick = 0;
timer.restart();
}
protected void stop() {
timer.stop();
tick = 0;
}
#Override
public void actionPerformed(ActionEvent e) {
tick += 16;
float progress = (float) tick / (float) 1000;
if (progress >= 1f) {
progress = 1f;
timer.stop();
layout.animationDidStop(parent);
}
for (AnimationBounds ab : constraints.getAnimationBounds()) {
Rectangle bounds = ab.getBounds(progress);
Component comp = ab.getComponent();
comp.setBounds(bounds);
comp.invalidate();
comp.repaint();
}
parent.repaint();
}
}
}
Before you tell me "it's too complicated", understand that animation alone is a complex subject, but when trying to animated components who should be under the control of appropriate layout managers, it becomes even more complicated...
If I understand your question correctly, you want that one JLabel should be moved every second time the timer is run, and a different one should be moved every other time the timer is run. If this is the case, you can use the count variable you already have to decide which one to move.
For example:
public void MoveForPlayer(JLabel PlayerCard1, JLabel PlayerCard2)
{
and then...
if (count%2==0) { // It's even
PlayerCard1.setLocation((PlayerCard1.getLocation().x-5), PlayerCard1.getLocation().y+5);
} else {
PlayerCard2.setLocation((PlayerCard2.getLocation().x-5), PlayerCard2.getLocation().y+5);
}
Am trying to modify a java code that implements Analog Clock using BufferedImage. I want the face of the clock to be changed dynamically when a user clicks on a button that scrolls through an array preloaded with the locations of images. So far my attempts have failed. I have two classes; The first Class creates and displays the Analog, and the second class attempts to change the face of the Analog clock using a mutator (setAnalogClockFace()) and an accessor (getAnalogClockFace()). The following is the code for the first Class as found on here, with minor modifications:
package panels;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.util.*;
public class Clock extends JPanel{
/**
*
*/
private static final long serialVersionUID = 1L;
private int hours = 0;
private int minutes = 0;
private int seconds = 0;
private int millis = 0;
private static final int spacing = 10;
private static final float threePi = (float)(3.0 * Math.PI);
private static final float radPerSecMin = (float)(Math.PI/30.0);
private int size; //height and width of clockface
private int centerX;//X coord of middle of clock
private int centerY;//Y coord of middle of clock
private BufferedImage clockImage;
private BufferedImage imageToUse;
private javax.swing.Timer t;
public Clock(){
this.setPreferredSize(new Dimension(203,203));
this.setBackground(getBackground());
this.setForeground(Color.darkGray);
t = new javax.swing.Timer(1000, new ActionListener(){
public void actionPerformed(ActionEvent ae){
update();
}
});
}
public void update(){
this.repaint();
}
public void start(){
t.start();
}
public void stop(){
t.stop();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth();
int h = getHeight();
size = ((w < h) ? w : h) - 2 * spacing;
centerX = size/2 + spacing;
centerY = size/2 + spacing;
setClockFace(new AnalogClockTimer().getAnalogClockFace());
clockImage = getClockFace();
if (clockImage == null | clockImage.getWidth() != w | clockImage.getHeight() != h){
clockImage = (BufferedImage)(this.createImage(w,h));
Graphics2D gc = clockImage.createGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawClockFace(gc);
}
Calendar now = Calendar.getInstance();
hours = now.get(Calendar.HOUR);
minutes = now.get(Calendar.MINUTE);
seconds = now.get(Calendar.SECOND);
millis = now.get(Calendar.MILLISECOND);
g2d.drawImage(clockImage, null, 0, 0);
drawClockHands(g);
}
private void drawClockHands(Graphics g){
int secondRadius = size/2;
int minuteRadius = secondRadius * 3/4;
int hourRadius = secondRadius/2;
float fseconds = seconds + (float)millis/1000;
float secondAngle = threePi - (radPerSecMin * fseconds);
drawRadius(g, centerX, centerY, secondAngle, 0, secondRadius);
float fminutes = (float)(minutes + fseconds/60.0);
float minuteAngle = threePi - (radPerSecMin * fminutes);
drawRadius(g, centerX, centerY, minuteAngle, 0 , minuteRadius);
float fhours = (float)(hours + fminutes/60.0);
float hourAngle = threePi - (5 * radPerSecMin * fhours);
drawRadius(g, centerX, centerY, hourAngle, 0 ,hourRadius);
}
private void drawClockFace(Graphics g){
//g.setColor(Color.lightGray);
g.fillOval(spacing, spacing, size, size);
//g.setColor(new Color(168,168,168));
g.drawOval(spacing, spacing, size, size);
for(int sec = 0; sec < 60; sec++){
int tickStart;
if(sec%5 == 0){
tickStart = size/2-10;
}else{
tickStart = size/2-5;
}
drawRadius(g, centerX, centerY, radPerSecMin * sec, tickStart, size/2);
}
}
private void drawRadius(Graphics g, int x, int y, double angle,
int minRadius, int maxRadius){
float sine = (float)Math.sin(angle);
float cosine = (float)Math.cos(angle);
int dxmin = (int)(minRadius * sine);
int dymin = (int)(minRadius * cosine);
int dxmax = (int)(maxRadius * sine);
int dymax = (int)(maxRadius * cosine);
g.drawLine(x+dxmin, y+dymin, x+dxmax,y+dymax);
}
public void setClockFace(BufferedImage img){
if(img != null){
imageToUse = img;
}else{
try{
imageToUse =
ImageIO.read(getClass().getClassLoader().getResource("http://imgur.com/T8x0I29"));
}catch(IOException io){
io.printStackTrace();
}
}
}
public BufferedImage getClockFace(){
return imageToUse;
}
}
The second Class that attempts to set the Clock face is:
package panels;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import panels.Clock;
public class AnalogClockTimer extends javax.swing.JPanel implements ActionListener,
MouseListener {
private Clock clock;
/**
*
*/
private static final long serialVersionUID = 1L;
private JButton prevBtn;
private JTextField clockFaceCount;
private JButton nextBtn;
private JPanel buttonedPanel,leftPanel,rightPanel;
private BufferedImage image;
String[] clockFace;
int arrayPointer = 1;
/**
* Auto-generated main method to display this
* JPanel inside a new JFrame.
*/
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new AnalogClockTimer());
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public AnalogClockTimer() {
super();
initGUI();
}
private void initGUI() {
try {
BorderLayout thisLayout = new BorderLayout();
this.setLayout(thisLayout);
this.setPreferredSize(new java.awt.Dimension(283, 233));
this.setBackground(getBackground());
{
clock = new Clock();
add(clock,BorderLayout.CENTER);
clock.start();
clock.setSize(203, 203);
}
{
buttonedPanel = new JPanel();
FlowLayout buttonedPanelLayout = new FlowLayout();
buttonedPanel.setLayout(buttonedPanelLayout);
this.add(buttonedPanel, BorderLayout.SOUTH);
buttonedPanel.setPreferredSize(new java.awt.Dimension(283, 30));
{
prevBtn = new JButton(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_rest.png")));
prevBtn.setPreferredSize(new java.awt.Dimension(20, 19));
prevBtn.setBackground(getBackground());
prevBtn.setBorderPainted(false);
prevBtn.setEnabled(false);
prevBtn.addMouseListener(this);
prevBtn.addActionListener(this);
buttonedPanel.add(prevBtn);
}
{
nextBtn = new JButton(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_rest.png")));
nextBtn.setPreferredSize(new java.awt.Dimension(20, 19));
nextBtn.setBackground(getBackground());
nextBtn.setBorderPainted(false);
nextBtn.addMouseListener(this);
nextBtn.addActionListener(this);
}
{
clockFaceCount = new JTextField();
clockFaceCount.setPreferredSize(new java.awt.Dimension(50, 19));
clockFaceCount.setBackground(getBackground());
clockFaceCount.setBorder(null);
clockFaceCount.setHorizontalAlignment(SwingConstants.CENTER);
clockFaceCount.setEditable(false);
buttonedPanel.add(clockFaceCount);
buttonedPanel.add(nextBtn);
}
{
clockFace = new String[]{"http://imgur.com/079QSdJ","http://imgur.com/vQ7tZjI",
"http://imgur.com/T8x0I29"};
for(int i = 1; i <= clockFace.length; i++){
if(i == 1){
nextBtn.setEnabled(true);
prevBtn.setEnabled(false);
}
clockFaceCount.setText(arrayPointer+" of "+clockFace.length);
}
}
{
leftPanel = new JPanel();
leftPanel.setPreferredSize(new Dimension(40, getHeight()));
add(leftPanel,BorderLayout.WEST);
}
{
rightPanel = new JPanel();
rightPanel.setPreferredSize(new Dimension(40,getHeight()));
add(rightPanel,BorderLayout.EAST);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void mouseClicked(MouseEvent me) {
if(me.getSource() == prevBtn)
prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_pressed.png")));
else
nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_pressed.png")));
}
#Override
public void mouseEntered(MouseEvent me) {
if(me.getSource()==prevBtn)
prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_hover.png")));
else
nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_hover.png")));
}
#Override
public void mouseExited(MouseEvent me) {
if(me.getSource() == prevBtn)
prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_rest.png")));
else
nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_rest.png")));
}
#Override
public void mousePressed(MouseEvent me) {
if(me.getSource() == prevBtn){
prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_pressed.png")));
}else{
nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_pressed.png")));
}
}
#Override
public void mouseReleased(MouseEvent me) {
if(me.getSource() == prevBtn)
prevBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_left_hover.png")));
else
nextBtn.setIcon(new ImageIcon(getClass().getClassLoader().getResource("icons/settings_right_hover.png")));
}
#Override
public void actionPerformed(ActionEvent ae) {
if(ae.getSource() == nextBtn){
{
if(arrayPointer < clockFace.length){
++arrayPointer;
prevBtn.setEnabled(true);
if(arrayPointer == clockFace.length)
nextBtn.setEnabled(false);
}
clockFaceCount.setText(arrayPointer + " of " + clockFace.length);
setAnalogClockFace(arrayPointer);
}
}else if(ae.getSource() == prevBtn){
{
if(arrayPointer > 1){
--arrayPointer;
nextBtn.setEnabled(true);
if(arrayPointer == 1){
clockFaceCount.setText(arrayPointer+" of "+clockFace.length);
prevBtn.setEnabled(false);
}
}
clockFaceCount.setText(arrayPointer + " of "+clockFace.length);
setAnalogClockFace(arrayPointer);
}
}
}
private void setAnalogClockFace(int pointerPosition) {
try{
image = ImageIO.read(getClass().getClassLoader().getResource(clockFace[pointerPosition]));
}catch(IOException io){
io.printStackTrace();
}
}
public BufferedImage getAnalogClockFace(){
return image;
}
}
I've been working on this for days, can someone please help me understand what am not doing right? Any help would be greatly appreciated.
Here is an MCVE using a JLabel to achieve the same result:
package panels;
import java.awt.Dimension;
import javax.swing.ImageIcon;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JTextField;
import java.awt.event.*;
import java.awt.BorderLayout;
#SuppressWarnings({"serial"})
public class ClockPanel extends javax.swing.JPanel implements ActionListener {
private JLabel clockImageLabel;
private JPanel buttonsPanel;
private JButton next,prev;
private JTextField displayCount;
String[] imageFaces;
int pointer = 1;
/**
* Auto-generated main method to display this
* JPanel inside a new JFrame.
*/
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new ClockPanel());
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public ClockPanel() {
super();
initGUI();
}
private void initGUI() {
try {
setPreferredSize(new Dimension(203, 237));
BorderLayout panelLayout = new BorderLayout();
setLayout(panelLayout);
{
clockImageLabel = new JLabel();
clockImageLabel.setPreferredSize(new Dimension(203,203));
clockImageLabel.setHorizontalAlignment(SwingConstants.CENTER);
clockImageLabel.setVerticalAlignment(SwingConstants.CENTER);
add(clockImageLabel,BorderLayout.NORTH);
}
{
buttonsPanel = new JPanel();
{
next = new JButton(">");
next.addActionListener(this);
}
{
prev = new JButton("<");
prev.addActionListener(this);
displayCount = new JTextField();
displayCount.setBorder(null);
displayCount.setEditable(false);
displayCount.setBackground(getBackground());
displayCount.setHorizontalAlignment(SwingConstants.CENTER);
buttonsPanel.add(prev);
buttonsPanel.add(displayCount);
displayCount.setPreferredSize(new java.awt.Dimension(45, 23));
buttonsPanel.add(next);
add(buttonsPanel,BorderLayout.SOUTH);
}
{
imageFaces = new String[]{"http://imgur.com/T8x0I29",
"http://imgur.com/079QSdJ","http://imgur.com/vQ7tZjI"
};
for(int i = 1; i < imageFaces.length; i++){
if(i == 1){
next.setEnabled(true);
prev.setEnabled(false);
}
displayCount.setText(pointer+" of "+imageFaces.length);
setLabelImage(pointer);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void setLabelImage(int pointerPosition){
clockImageLabel.setIcon(new ImageIcon(getClass().getClassLoader().getResource(imageFaces[pointerPosition])));
this.repaint();
}
#Override
public void actionPerformed(ActionEvent ae) {
if(ae.getSource() == next){
if(pointer < imageFaces.length){
++pointer;
prev.setEnabled(true);
if(pointer == imageFaces.length)next.setEnabled(false);
displayCount.setText(pointer+" of "+imageFaces.length);
setLabelImage(pointer);
}
}else if(ae.getSource() == prev){
if(pointer > 1){
--pointer;
next.setEnabled(true);
if(pointer == 1)prev.setEnabled(false);
displayCount.setText(pointer+" of "+imageFaces.length);
setLabelImage(pointer);
}
}
}
}
Here are 1/2 Images for the code:
HandlessAnalogClock 1
HandlessAnalogClock 2
HandlessAnalogClock 3
This MCVE will successfully flip between two of the three images, but still has problems with ArrayIndexOutOfBoundsException.
That last part is 'Batteries Not Included' (left as an exercise for the user to correct).
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.imageio.ImageIO;
import java.net.URL;
#SuppressWarnings({"serial"})
public class ClockPanel extends JPanel implements ActionListener {
private JLabel clockImageLabel;
private JPanel buttonsPanel;
private JButton next, prev;
private JTextField displayCount;
BufferedImage[] images;
int pointer = 1;
/**
* Auto-generated main method to display this JPanel inside a new JFrame.
*/
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new ClockPanel());
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public ClockPanel() {
super();
initGUI();
}
private void initGUI() {
try {
setPreferredSize(new Dimension(203, 237));
BorderLayout panelLayout = new BorderLayout();
setLayout(panelLayout);
clockImageLabel = new JLabel();
clockImageLabel.setPreferredSize(new Dimension(203, 203));
clockImageLabel.setHorizontalAlignment(SwingConstants.CENTER);
clockImageLabel.setVerticalAlignment(SwingConstants.CENTER);
add(clockImageLabel, BorderLayout.NORTH);
buttonsPanel = new JPanel();
next = new JButton(">");
next.addActionListener(this);
prev = new JButton("<");
prev.addActionListener(this);
displayCount = new JTextField();
displayCount.setBorder(null);
displayCount.setEditable(false);
displayCount.setBackground(getBackground());
displayCount.setHorizontalAlignment(SwingConstants.CENTER);
buttonsPanel.add(prev);
buttonsPanel.add(displayCount);
displayCount.setPreferredSize(new java.awt.Dimension(45, 23));
buttonsPanel.add(next);
add(buttonsPanel, BorderLayout.SOUTH);
String[] imageFaces = new String[]{
"http://i.imgur.com/T8x0I29.png",
"http://i.imgur.com/079QSdJ.png",
"http://i.imgur.com/vQ7tZjI.png"
};
images = new BufferedImage[imageFaces.length];
for (int ii=0; ii<imageFaces.length; ii++) {
System.out.println("loading image: " + ii);
images[ii] = ImageIO.read(new URL(imageFaces[ii]));
System.out.println("loaded image: " + images[ii]);
}
setLabelImage(0);
} catch (Exception e) {
e.printStackTrace();
}
}
private void setLabelImage(int pointerPosition) {
System.out.println("setLabelImage: " + pointerPosition);
clockImageLabel.setIcon(new ImageIcon(images[pointerPosition]));
this.repaint();
}
#Override
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == next) {
if (pointer < images.length) {
++pointer;
prev.setEnabled(true);
if (pointer == images.length) {
next.setEnabled(false);
}
displayCount.setText(pointer + " of " + images.length);
setLabelImage(pointer);
}
} else if (ae.getSource() == prev) {
if (pointer > 1) {
--pointer;
next.setEnabled(true);
if (pointer == 1) {
prev.setEnabled(false);
}
displayCount.setText(pointer + " of " + images.length);
setLabelImage(pointer);
}
}
}
}
Expanding on #Andrew's example, consider arranging for the index of the current image to wrap-around, as shown in the example cited here.
private int pointer = 0;
...
private void initGUI() {
...
setLabelImage(pointer);
...
}
private void setLabelImage(int pointerPosition) {
System.out.println("setLabelImage: " + pointerPosition);
clockImageLabel.setIcon(new ImageIcon(images[pointerPosition]));
displayCount.setText((pointerPosition + 1) + " of " + images.length);
this.repaint();
}
#Override
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == next) {
pointer++;
if (pointer >= images.length) {
pointer = 0;
}
} else if (ae.getSource() == prev) {
pointer--;
if (pointer < 0) {
pointer = images.length - 1;
}
}
setLabelImage(pointer);
}
As an exercise, try to factor out the repeated instantiation of ImageIcon in setLabelImage() by creating a List<ImageIcon> in initGUI().
Invoke javax.swing.Timer#start() same time,
7u25 is not problem.
but 7u40 is big problem.
Too laggy invoke ActionListener#actionPerformed. (basically same time invoke u25)
Totally different move between u25 and u40. (I use Windows 8)
I bug report but still not add bug tracking system. Oracle crushing swing apps?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerProblem extends JComponent {
int red = 0;
TimerProblem(final long startMs) {
setPreferredSize(new Dimension(10, 10));
Timer t = new Timer(16, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
red = (int)(System.currentTimeMillis() - startMs) % 255;
repaint();
}
});
t.setInitialDelay(1000);
t.start();
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(new Color(red, 255 - red, 0));
g.fillRect(0, 0, getWidth(), getHeight());
}
public static void main(String[] args) {
JFrame f = new JFrame();
Container c = f.getContentPane();
c.setLayout(new GridLayout(10, 10));
long startMs = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
c.add(new TimerProblem(startMs));
}
f.pack();
f.setVisible(true);
}
}
Several issues arise in your example:
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
All Swing Timer instances share a common thread, which is being saturated.
Depending on the goal, some alternatives are possible:
Use a single Timer instance, and select some fraction for update at a proportionally higher rate. The example below randomly selects N of the components and updates them every 100 ms.
Use TexturePaint, as shown here.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.*;
/** #see https://stackoverflow.com/a/18936444/230513 */
public class BlinkenLights {
private static final int S = 24;
private static final int N = 10;
private static final Random r = new Random();
private static final List<MyComponent> list = new ArrayList<MyComponent>();
private static final class MyComponent extends JComponent {
public MyComponent() {
this.setOpaque(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(S, S);
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.getHSBColor(r.nextFloat() / 6, 1, 1));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
private static JPanel createPanel() {
final JPanel p = new JPanel();
p.setLayout(new GridLayout(N, N));
for (int i = 0; i < N * N; i++) {
MyComponent c = new MyComponent();
p.add(c);
list.add(c);
}
Timer t = new Timer(1000 / N, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Collections.shuffle(list, r);
for (int i = 0; i < N; i++) {
list.get(i).repaint();
}
}
});
t.start();
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(createPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
Finally I write DIY repaint management class .. :(
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
/**
* EffectTimer
*/
public class EffectTimer {
/**
* All of effect timers in instance of this class.
*/
static class GlobalTimer implements ActionListener {
List<EffectTimer> registeredEffects = new ArrayList<>();
Timer timer = new Timer(16, this);
public void start(final EffectTimer t) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
internalStart(t);
}
});
}
void internalStart(EffectTimer t) {
int initialDelay = Math.max(0, (int) (t.getEffectStartTime() - System.currentTimeMillis()));
if(timer.getInitialDelay() >= initialDelay) {
timer.setInitialDelay(initialDelay);
}
if(!registeredEffects.contains(t)) {
registeredEffects.add(t);
if(registeredEffects.size() == 1) {
timer.start();
}
}
}
void stop(final EffectTimer t) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
registeredEffects.remove(t);
checkStop();
}
});
}
#Override
public void actionPerformed(ActionEvent e) {
long now = e.getWhen();
Iterator<EffectTimer> iter = registeredEffects.iterator();
while(iter.hasNext()) {
EffectTimer t = iter.next();
long elapsedMs = now - t.getEffectStartTime();
if(elapsedMs > 0) {
float p = elapsedMs / (float)t.getEffectLengthMs();
if(p >= 1.0f) {
iter.remove();
t.stop();
} else {
if(t.isReversed()) {
p = 1.0f - p;
}
t.progressChanged(p);
}
}
}
checkStop();
}
void checkStop() {
if(registeredEffects.isEmpty()) {
timer.stop();
}
}
public int getRunningTimerCount() {
return registeredEffects.size();
}
public void stopAll() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
registeredEffects.clear();
checkStop();
}
});
}
}
static final GlobalTimer GTIMER = new GlobalTimer();
int effectLengthMs = -1;
long effectStartMs = -1;
float progress = 0.0f;
boolean reversed = true;
public long getEffectStartTime() {
return effectStartMs;
}
public int getEffectLengthMs() {
return effectLengthMs;
}
public void start(int lengthMs) {
start(lengthMs, System.currentTimeMillis());
}
public void start(int lengthMs, long startMs) {
effectLengthMs = lengthMs;
effectStartMs = startMs;
reversed = false;
progress = 0.0f;
GTIMER.start(this);
}
public boolean isReversed() {
return reversed;
}
public void reverse(final int lengthMs) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
internalReverse(lengthMs);
}
});
}
void internalReverse(int lengthMs) {
reversed = !reversed;
effectLengthMs = lengthMs;
int adjust = reversed ? (int)(lengthMs * (1.0f - progress)) : (int)(lengthMs * progress);
effectStartMs = System.currentTimeMillis() - adjust;
GTIMER.start(this);
}
final public void progressChanged(float p) {
progress = p;
run(p);
}
/**
* 0.0f to 1.0f effect progress.
* <code>Float.compare(progress, 1.0f) >= 0</code> to end progress.
*/
protected void run(float p) {}
public void stop() {
progress = reversed ? 0.0f : 1.0f;
GTIMER.stop(this);
}
public boolean isRunning() {
return 0.0f < progress && progress < 1.0f;
}
public float getProgress() {
return progress;
}
public static int getRunningTimerCount() {
return GTIMER.getRunningTimerCount();
}
public static void stopAll() {
GTIMER.stopAll();
}
}
let me if have you anyone answer, this ans. basically required like as google search engine , when we press any key then it would be display suggestion related pressed key.
regard
Satish Dhiman
From my comment/previous code see this update:
Using JTextField with AutoSuggestor:
Using JTextArea (or any other JTextComponent besides JTextField will result in Pop up window being shown under caret) with AutoSuggestor:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JWindow;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
/**
* #author David
*/
public class Test {
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//JTextField f = new JTextField(10);
JTextArea f = new JTextArea(10, 10);
//JEditorPane f = new JEditorPane();
//create words for dictionary could also use null as parameter for AutoSuggestor(..,..,null,..,..,..,..) and than call AutoSuggestor#setDictionary after AutoSuggestr insatnce has been created
ArrayList<String> words = new ArrayList<>();
words.add("hello");
words.add("heritage");
words.add("happiness");
words.add("goodbye");
words.add("cruel");
words.add("car");
words.add("war");
words.add("will");
words.add("world");
words.add("wall");
AutoSuggestor autoSuggestor = new AutoSuggestor(f, frame, words, Color.WHITE.brighter(), Color.BLUE, Color.RED, 0.75f) {
#Override
boolean wordTyped(String typedWord) {
System.out.println(typedWord);
return super.wordTyped(typedWord);//checks for a match in dictionary and returns true or false if found or not
}
};
JPanel p = new JPanel();
p.add(f);
frame.add(p);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
}
class AutoSuggestor {
private final JTextComponent textComp;
private final Window container;
private JPanel suggestionsPanel;
private JWindow autoSuggestionPopUpWindow;
private String typedWord;
private final ArrayList<String> dictionary = new ArrayList<>();
private int currentIndexOfSpace, tW, tH;
private DocumentListener documentListener = new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent de) {
checkForAndShowSuggestions();
}
#Override
public void removeUpdate(DocumentEvent de) {
checkForAndShowSuggestions();
}
#Override
public void changedUpdate(DocumentEvent de) {
checkForAndShowSuggestions();
}
};
private final Color suggestionsTextColor;
private final Color suggestionFocusedColor;
public AutoSuggestor(JTextComponent textComp, Window mainWindow, ArrayList<String> words, Color popUpBackground, Color textColor, Color suggestionFocusedColor, float opacity) {
this.textComp = textComp;
this.suggestionsTextColor = textColor;
this.container = mainWindow;
this.suggestionFocusedColor = suggestionFocusedColor;
this.textComp.getDocument().addDocumentListener(documentListener);
setDictionary(words);
typedWord = "";
currentIndexOfSpace = 0;
tW = 0;
tH = 0;
autoSuggestionPopUpWindow = new JWindow(mainWindow);
autoSuggestionPopUpWindow.setOpacity(opacity);
suggestionsPanel = new JPanel();
suggestionsPanel.setLayout(new GridLayout(0, 1));
suggestionsPanel.setBackground(popUpBackground);
addKeyBindingToRequestFocusInPopUpWindow();
}
private void addKeyBindingToRequestFocusInPopUpWindow() {
textComp.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released");
textComp.getActionMap().put("Down released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {//focuses the first label on popwindow
for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) {
if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) {
((SuggestionLabel) suggestionsPanel.getComponent(i)).setFocused(true);
autoSuggestionPopUpWindow.toFront();
autoSuggestionPopUpWindow.requestFocusInWindow();
suggestionsPanel.requestFocusInWindow();
suggestionsPanel.getComponent(i).requestFocusInWindow();
break;
}
}
}
});
suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released");
suggestionsPanel.getActionMap().put("Down released", new AbstractAction() {
int lastFocusableIndex = 0;
#Override
public void actionPerformed(ActionEvent ae) {//allows scrolling of labels in pop window (I know very hacky for now :))
ArrayList<SuggestionLabel> sls = getAddedSuggestionLabels();
int max = sls.size();
if (max > 1) {//more than 1 suggestion
for (int i = 0; i < max; i++) {
SuggestionLabel sl = sls.get(i);
if (sl.isFocused()) {
if (lastFocusableIndex == max - 1) {
lastFocusableIndex = 0;
sl.setFocused(false);
autoSuggestionPopUpWindow.setVisible(false);
setFocusToTextField();
checkForAndShowSuggestions();//fire method as if document listener change occured and fired it
} else {
sl.setFocused(false);
lastFocusableIndex = i;
}
} else if (lastFocusableIndex <= i) {
if (i < max) {
sl.setFocused(true);
autoSuggestionPopUpWindow.toFront();
autoSuggestionPopUpWindow.requestFocusInWindow();
suggestionsPanel.requestFocusInWindow();
suggestionsPanel.getComponent(i).requestFocusInWindow();
lastFocusableIndex = i;
break;
}
}
}
} else {//only a single suggestion was given
autoSuggestionPopUpWindow.setVisible(false);
setFocusToTextField();
checkForAndShowSuggestions();//fire method as if document listener change occured and fired it
}
}
});
}
private void setFocusToTextField() {
container.toFront();
container.requestFocusInWindow();
textComp.requestFocusInWindow();
}
public ArrayList<SuggestionLabel> getAddedSuggestionLabels() {
ArrayList<SuggestionLabel> sls = new ArrayList<>();
for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) {
if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) {
SuggestionLabel sl = (SuggestionLabel) suggestionsPanel.getComponent(i);
sls.add(sl);
}
}
return sls;
}
private void checkForAndShowSuggestions() {
typedWord = getCurrentlyTypedWord();
suggestionsPanel.removeAll();//remove previos words/jlabels that were added
//used to calcualte size of JWindow as new Jlabels are added
tW = 0;
tH = 0;
boolean added = wordTyped(typedWord);
if (!added) {
if (autoSuggestionPopUpWindow.isVisible()) {
autoSuggestionPopUpWindow.setVisible(false);
}
} else {
showPopUpWindow();
setFocusToTextField();
}
}
protected void addWordToSuggestions(String word) {
SuggestionLabel suggestionLabel = new SuggestionLabel(word, suggestionFocusedColor, suggestionsTextColor, this);
calculatePopUpWindowSize(suggestionLabel);
suggestionsPanel.add(suggestionLabel);
}
public String getCurrentlyTypedWord() {//get newest word after last white spaceif any or the first word if no white spaces
String text = textComp.getText();
String wordBeingTyped = "";
text = text.replaceAll("(\\r|\\n)", " ");
if (text.contains(" ")) {
int tmp = text.lastIndexOf(" ");
if (tmp >= currentIndexOfSpace) {
currentIndexOfSpace = tmp;
wordBeingTyped = text.substring(text.lastIndexOf(" "));
}
} else {
wordBeingTyped = text;
}
return wordBeingTyped.trim();
}
private void calculatePopUpWindowSize(JLabel label) {
//so we can size the JWindow correctly
if (tW < label.getPreferredSize().width) {
tW = label.getPreferredSize().width;
}
tH += label.getPreferredSize().height;
}
private void showPopUpWindow() {
autoSuggestionPopUpWindow.getContentPane().add(suggestionsPanel);
autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30));
autoSuggestionPopUpWindow.setSize(tW, tH);
autoSuggestionPopUpWindow.setVisible(true);
int windowX = 0;
int windowY = 0;
if (textComp instanceof JTextField) {//calculate x and y for JWindow at bottom of JTextField
windowX = container.getX() + textComp.getX() + 5;
if (suggestionsPanel.getHeight() > autoSuggestionPopUpWindow.getMinimumSize().height) {
windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getMinimumSize().height;
} else {
windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getHeight();
}
} else {//calculate x and y for JWindow on any JTextComponent using the carets position
Rectangle rect = null;
try {
rect = textComp.getUI().modelToView(textComp, textComp.getCaret().getDot());//get carets position
} catch (BadLocationException ex) {
ex.printStackTrace();
}
windowX = (int) (rect.getX() + 15);
windowY = (int) (rect.getY() + (rect.getHeight() * 3));
}
//show the pop up
autoSuggestionPopUpWindow.setLocation(windowX, windowY);
autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30));
autoSuggestionPopUpWindow.revalidate();
autoSuggestionPopUpWindow.repaint();
}
public void setDictionary(ArrayList<String> words) {
dictionary.clear();
if (words == null) {
return;//so we can call constructor with null value for dictionary without exception thrown
}
for (String word : words) {
dictionary.add(word);
}
}
public JWindow getAutoSuggestionPopUpWindow() {
return autoSuggestionPopUpWindow;
}
public Window getContainer() {
return container;
}
public JTextComponent getTextField() {
return textComp;
}
public void addToDictionary(String word) {
dictionary.add(word);
}
boolean wordTyped(String typedWord) {
if (typedWord.isEmpty()) {
return false;
}
//System.out.println("Typed word: " + typedWord);
boolean suggestionAdded = false;
for (String word : dictionary) {//get words in the dictionary which we added
boolean fullymatches = true;
for (int i = 0; i < typedWord.length(); i++) {//each string in the word
if (!typedWord.toLowerCase().startsWith(String.valueOf(word.toLowerCase().charAt(i)), i)) {//check for match
fullymatches = false;
break;
}
}
if (fullymatches) {
addWordToSuggestions(word);
suggestionAdded = true;
}
}
return suggestionAdded;
}
}
class SuggestionLabel extends JLabel {
private boolean focused = false;
private final JWindow autoSuggestionsPopUpWindow;
private final JTextComponent textComponent;
private final AutoSuggestor autoSuggestor;
private Color suggestionsTextColor, suggestionBorderColor;
public SuggestionLabel(String string, final Color borderColor, Color suggestionsTextColor, AutoSuggestor autoSuggestor) {
super(string);
this.suggestionsTextColor = suggestionsTextColor;
this.autoSuggestor = autoSuggestor;
this.textComponent = autoSuggestor.getTextField();
this.suggestionBorderColor = borderColor;
this.autoSuggestionsPopUpWindow = autoSuggestor.getAutoSuggestionPopUpWindow();
initComponent();
}
private void initComponent() {
setFocusable(true);
setForeground(suggestionsTextColor);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
replaceWithSuggestedText();
autoSuggestionsPopUpWindow.setVisible(false);
}
});
getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), "Enter released");
getActionMap().put("Enter released", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
replaceWithSuggestedText();
autoSuggestionsPopUpWindow.setVisible(false);
}
});
}
public void setFocused(boolean focused) {
if (focused) {
setBorder(new LineBorder(suggestionBorderColor));
} else {
setBorder(null);
}
repaint();
this.focused = focused;
}
public boolean isFocused() {
return focused;
}
private void replaceWithSuggestedText() {
String suggestedWord = getText();
String text = textComponent.getText();
String typedWord = autoSuggestor.getCurrentlyTypedWord();
String t = text.substring(0, text.lastIndexOf(typedWord));
String tmp = t + text.substring(text.lastIndexOf(typedWord)).replace(typedWord, suggestedWord);
textComponent.setText(tmp + " ");
}
}
As you can see I changed the code by making its constructor accept a JTextComponent rather than a JTextField or JTextArea etc.
The problem we are left with is we have to show the pop up JWindow at a different position depending on the JTextComponent passed i.e a JTextField will have autosuggest window pop up at the bottom while JTextArea/JEditorPane etc would have the JWindow pop up under the caret/word.
Have a look at this specific method showPopUpWindow() in AutoSuggestor class:
private void showPopUpWindow() {
autoSuggestionPopUpWindow.getContentPane().add(suggestionsPanel);
autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30));
autoSuggestionPopUpWindow.setSize(tW, tH);
autoSuggestionPopUpWindow.setVisible(true);
int windowX = 0;
int windowY = 0;
if (textComp instanceof JTextField) {//calculate x and y for JWindow at bottom of JTextField
windowX = container.getX() + textComp.getX() + 5;
if (suggestionsPanel.getHeight() > autoSuggestionPopUpWindow.getMinimumSize().height) {
windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getMinimumSize().height;
} else {
windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getHeight();
}
} else {//calculate x and y for JWindow on any JTextComponent using the carets position
Rectangle rect = null;
try {
rect = textComp.getUI().modelToView(textComp, textComp.getCaret().getDot());//get carets position
} catch (BadLocationException ex) {
ex.printStackTrace();
}
windowX = (int) (rect.getX() + 15);
windowY = (int) (rect.getY() + (rect.getHeight() * 3));
}
//show the pop up
autoSuggestionPopUpWindow.setLocation(windowX, windowY);
autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30));
autoSuggestionPopUpWindow.revalidate();
autoSuggestionPopUpWindow.repaint();
}
As you can see we check to see what instance the JTextComponent is and if its not a JTextField simply get the caret position (via the Rectangle of the caret) of the JTextComponent and position JWindow pop up from there (underneath the caret in my case).
I can propose my own implementation. It is based on JList shown in JWindow.
As I wanted to use this code for a combo box, I've disabled UP and DOWN keys with keyEvent.consume() call.
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class GAutoCompletionDecorator {
private final JTextComponent textComponent;
private final JWindow suggestionsPopup;
private final JList completionList;
private final DefaultListModel completionListModel;
public static void decorate(JComboBox comboBox, JFrame parent) {
comboBox.setEditable(true);
final JTextComponent textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent();
decorate(textComponent, parent);
}
private static void decorate(JTextComponent textComponent, JFrame parent) {
final GAutoCompletionDecorator autoCompletionDecorator = new GAutoCompletionDecorator(textComponent, parent);
autoCompletionDecorator.decorate();
}
private GAutoCompletionDecorator(final JTextComponent textComponent, JFrame parent) {
this.textComponent = textComponent;
this.suggestionsPopup = new JWindow(parent);
this.completionListModel = new DefaultListModel();
this.completionList = new JList();
this.completionList.setModel(completionListModel);
this.suggestionsPopup.getContentPane().add(this.completionList);
}
private void decorate() {
textComponent.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent documentEvent) {
updateSuggestions();
}
public void removeUpdate(DocumentEvent documentEvent) {
updateSuggestions();
}
public void changedUpdate(DocumentEvent documentEvent) {
updateSuggestions();
}
});
this.textComponent.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN || keyEvent.getKeyCode() == KeyEvent.VK_UP) {
updateSuggestions();
completionList.requestFocus();
completionList.dispatchEvent(keyEvent);
keyEvent.consume();
}
}
public void keyReleased(KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN || keyEvent.getKeyCode() == KeyEvent.VK_UP) {
keyEvent.consume();
}
}
});
this.completionList.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE) {
textComponent.requestFocus();
hideSuggestionsPopup();
} else if (keyEvent.getKeyCode() == KeyEvent.VK_UP) {
if (completionList.getSelectedIndex() == 0) {
completionList.setSelectedIndex(completionListModel.size() - 1);
keyEvent.consume();
}
} else if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
if (completionList.getSelectedIndex() == completionListModel.size() - 1) {
completionList.setSelectedIndex(0);
keyEvent.consume();
}
} else if (keyEvent.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
textComponent.requestFocus();
hideSuggestionsPopup();
textComponent.dispatchEvent(keyEvent);
} else if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER) {
textComponent.requestFocus();
hideSuggestionsPopup();
final String selectedSuggestion = (String) completionList.getSelectedValue();
if (selectedSuggestion != null) {
try {
final int caretPosition = textComponent.getCaretPosition();
textComponent.getDocument().insertString(caretPosition, getCompletionString(selectedSuggestion, textComponent.getText(), caretPosition), null);
} catch (BadLocationException e) {
//ignore
}
}
}
}
});
}
private String getCompletionString(String selectedSuggestion, String text, int caretPosition) {
//we may insert selectedSuggestion fully of some part of it
return selectedSuggestion;
}
private void updateSuggestions() {
final String text = textComponent.getText();
final int caretPosition = textComponent.getCaretPosition();
final List<String> suggestions = getSuggestions(text, caretPosition);
if (suggestions == null || suggestions.size() == 0) {
//hide suggestions window
hideSuggestionsPopup();
} else {
//show suggestions window
showSuggestionsPopup(suggestions);
}
}
private void hideSuggestionsPopup() {
suggestionsPopup.setVisible(false);
}
private void showSuggestionsPopup(List<String> suggestions) {
completionListModel.clear();
for (String suggestion : suggestions) {
completionListModel.addElement(suggestion);
}
final Point textComponentLocation = new Point(textComponent.getLocation());
SwingUtilities.convertPointToScreen(textComponentLocation, textComponent);
Point caretLocation = textComponent.getCaret().getMagicCaretPosition();
if (caretLocation != null) {
caretLocation = new Point(caretLocation);
SwingUtilities.convertPointToScreen(caretLocation, textComponent);
}
suggestionsPopup.pack();
suggestionsPopup.setLocation(caretLocation == null ? textComponentLocation.x : caretLocation.x,
textComponentLocation.y + textComponent.getHeight());
suggestionsPopup.setVisible(true);
}
private List<String> getSuggestions(String text, int caretPosition) {
final List<String> words = new ArrayList<String>();
words.add("suggestion 1");
words.add("suggestion 2");
words.add("suggestion 3");
words.add("suggestion 4");
words.add("suggestion 5");
//make suggestions funny
return text.length() < words.size() ? words.subList(0, words.size() - text.length()) : Collections.<String>emptyList();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JComboBox comboBox = new JComboBox(new String[] {"Choice1", "Choice2"});
comboBox.setEditable(true);
GAutoCompletionDecorator.decorate(comboBox, frame);
frame.add(comboBox);
frame.pack();
frame.setVisible(true);
}
});
}
}