I would like to set two or more icons to one JButton (Java, Swing). Is it possible?
I add picture made in drawing. ;-)
The best way to achieve this is by creating a custom class that implements the Icon interface, which simply paints two given icons side by side.
public class TwoIcon implements Icon {
private final int iconGap = 2;
private final Icon icon1;
private final Icon icon2;
public TwoIcon(final Icon icon1, final Icon icon2) {
this.icon1 = icon1;
this.icon2 = icon2;
}
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
int mid = getIconHeight() / 2;
int y1 = y + mid - icon1.getIconHeight() / 2;
int y2 = y + mid - icon2.getIconHeight() / 2;
icon1.paintIcon(c, g, x, y1);
icon2.paintIcon(c, g, x + icon1.getIconWidth() + iconGap, y2);
}
#Override
public int getIconWidth() {
return icon1.getIconWidth() + icon2.getIconWidth() + iconGap;
}
#Override
public int getIconHeight() {
return Math.max(icon1.getIconHeight(), icon2.getIconHeight());
}
}
The icons will be painted side by side with a padding of 2 and centered vertically.
Play around with the spacing if you want them to be aligned differently.
Icon leftIcon = ...
Icon rightIcon = ...
button.setIcon(new TwoIcon(leftIcon, rightIcon));
Result:
I am simply using Icons that paint a solid colour here. One is 16x16 and the other 20x20 in size to demonstrate the vertical alignment.
In fact this is not restricted to JButton and will for any JComponent that can use an icon e.g. JLabel etc.
Are those two icons (happy / sad) literally the icons required? If so, I'd suggest instead using a JCheckBox and setting the icons as the default and selected icon (so the check box changes between them on selection).
Here's how (using a red icon for happy, and blue icon for sad).
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.net.*;
import javax.imageio.ImageIO;
public class CheckBoxWithIcons {
private JComponent ui = null;
Image happyImage;
Image sadImage;
CheckBoxWithIcons() {
try {
initUI();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public final void initUI() throws MalformedURLException, IOException {
if (ui!=null) return;
happyImage = ImageIO.read(new URL("https://i.stack.imgur.com/wCF8S.png"));
sadImage = ImageIO.read(new URL("https://i.stack.imgur.com/gJmeJ.png"));
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,100,4,100));
JCheckBox checkBox = new JCheckBox("Mood", true);
checkBox.setIcon(new ImageIcon(sadImage));
checkBox.setSelectedIcon(new ImageIcon(happyImage));
ui.add(checkBox);
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = () -> {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
CheckBoxWithIcons o = new CheckBoxWithIcons();
JFrame f = new JFrame(o.getClass().getSimpleName());
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);
}
}
Related
I have an assignment to create a GUI that switches images when a menu item is selected (ex. file, new picture) and also contains buttons for zooming in and out on the images. When I try switching images with my code, the image only partly loads. When I minimize the window and then reopen it, the image is fully loaded. I'm wondering why this is happening.
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
public class ImageZoom extends JPanel {
private Image image;
int x, y;
private JButton zoomIn;
private JButton zoomOut;
private JMenuBar bar;
private JMenu file;
private JMenuItem choosePic = new JMenuItem("New Picture");
private String pics[] = {"waterfall.jpg", "mountains.jpg"};
private int picIndex = 1;
int imageHeight = getHeight();
int imageWidth = getWidth();
int zoom = 1;
Image images[] = new Image[2];
public ImageZoom() {
try {
images[0] = ImageIO.read(new File("waterfall.jpg"));
images[1] = ImageIO.read(new File("mountains.jpg"));
} catch (IOException e) {}
zoomIn = new JButton("+");
zoomOut = new JButton("-");
JPanel panel = new JPanel();
bar = new JMenuBar();
file = new JMenu("File");
file.add(choosePic);
bar.add(file);
choosePic.addActionListener (new ActionListener() {
public void actionPerformed (ActionEvent e) {
if (e.getSource() == choosePic) {
repaint();
}
}
});
zoomIn.addActionListener (new ActionListener() {
public void actionPerformed (ActionEvent e) {
if (e.getSource() == zoomIn) {
if (zoom < 6) {
zoom += 1;
repaint();
}
}
}
});
zoomOut.addActionListener( new ActionListener() {
public void actionPerformed (ActionEvent e) {
if (e.getSource() == zoomOut) {
if (zoom > 1) {
zoom -= 1;
repaint();
}
}
}
});
}
public JPanel getButtonPanel () {
JPanel panel = new JPanel();
panel.add(zoomIn);
panel.add(zoomOut);
return panel;
}
public Image getImage() {
try {
image = ImageIO.read(new File(pics[picIndex % 2]));
picIndex++;
}
catch (IOException e){}
return image;
}
protected void paintComponent (Graphics g) {
imageHeight = getHeight() * zoom;
imageWidth = getWidth() * zoom;
super.paintComponent(g);
g.drawImage(getImage(), 0, 0, imageWidth, imageHeight, null);
}
public void createJFrame () {
JFrame frame = new JFrame();
ImageZoom imgZoom = new ImageZoom();
frame.setJMenuBar(bar);
frame.add(imgZoom);
frame.add(getButtonPanel(), BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(650, 650);
frame.setResizable(false);
frame.setVisible(true);
}
}
class Test {
public static void main (String[] args) {
ImageZoom zoom = new ImageZoom();
zoom.createJFrame();
}
}
For simplicity, you should just be using a JLabel and calling its setIcon method to switch the images. You could dynamically scale the images as required (just maintain a reference to the original)
Problem #1
You should be passing this to drawImage, this will allow the component to act as the ImageObserver and schedule additional reprints as required based on events from the image's state, which leads to
Problem #2
You should not be calling getImage from within the paintComponent method, paintComponent could be called for any number of reasons, many of which you don't control or even know about and paintComponent should simply paint the current state of the component and never, ever try and change the state
Side Note: Instead of repeatedly trying to load the images, it would be better to load them once and continue to reuse the loaded reference
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've just started my course Programming 2, and its quite a jump from the introducting course we had before. I am to make a program that has three buttons "red", "blue", "green" and when I click "red" a red square is to appear. When I click another button, there are more squares, and the color of the square depends on the button I press.
My problem is, that when I press "red", I have to press another button before the red square appears. So in reality it will seem like the buttons I press are connected to a wrong action. It's really hard to describe, but it's as is there is a "delay of one action".. Can you provide a novice like me any assistance? I have tree classes in total: SquareIcon, CompositeIcon, and a tester with the frame.
It should only be necessary to look at the last class I pasted here.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Rectangle2D;
/**
* Made by Rasmus
* Version 13-11-2014.
*/
public class SquareIcon implements Icon{
private int size;
private Color color;
public SquareIcon(int size, Color color) {
this.size = size;
this.color = color;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2 = (Graphics2D) g;
Rectangle2D rec = new Rectangle2D.Double(x, y, size, size);
g2.fill(rec);
g2.setColor(color);
}
public int getIconWidth() {
return size;
}
public int getIconHeight() {
return size;
}
}
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
/**
* Made by Rasmus
* Version 13-11-2014.
*/
public class CompositeIcon implements Icon {
private ArrayList<Icon> icons;
public CompositeIcon() {
icons = new ArrayList<Icon>();
}
public void paintIcon(Component c, Graphics g, int x, int y) {
for (Icon i : icons) {
i.paintIcon(c, g, x, y);
x += i.getIconWidth();
}
}
public int getIconWidth() {
int width = 0;
for (Icon i : icons) {
width += i.getIconWidth();
}
return width;
}
public int getIconHeight() {
int height = 0;
for (Icon i : icons) {
if (i.getIconHeight() > height) {
height = i.getIconHeight();
}
}
return height;
}
public void addIcon(Icon i) {
icons.add(i);
}
}
And the last:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Made by Rasmus
* Version 13-11-2014.
*/
public class FrameTest implements ActionListener {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setLayout(new FlowLayout());
JButton redButton = new JButton("Rød");
JButton greenButton = new JButton("Grøn");
JButton blueButton = new JButton("Blå");
frame.add(redButton);
frame.add(greenButton);
frame.add(blueButton);
final CompositeIcon ci = new CompositeIcon();
final SquareIcon red = new SquareIcon(50, Color.RED);
final SquareIcon green = new SquareIcon(50, Color.GREEN);
final SquareIcon blue = new SquareIcon(50, Color.BLUE);
JLabel squareLabel = new JLabel(ci);
frame.add(squareLabel);
redButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ci.addIcon(red);
frame.pack();
frame.repaint();
}
});
greenButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ci.addIcon(green);
frame.pack();
frame.repaint();
}
});
blueButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ci.addIcon(blue);
frame.pack();
frame.repaint();
}
});
frame.setSize(250, 75);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
}
}
Two things I would suggest:
First, I would advise that you delete the last action performed block. That block may be causing the error.
Second, if that isn't causing the error, try inserting breakpoints or print statements to see when it enter the action performed loop, then you can work from there.
I have a simple Swing Java application that performs searches, and the results are shown in a new tab. While the search is running, I want to show a progress icon or animation in the title of the tab. I tried adding a gif icon, but it doesn't animate. Is there a reason why this isn't working?
The Swing tutorial about progress bars (and showing progress in general) is a very good place to start. It shows you how to perform long-lasting operations on a worker thread by using a SwingWorker, and updating your UI at certain intervals to show progress of the long-lasting operation to the user. There is another tutorial available for more information on the SwingWorker and concurrency in Swing
And as always, this site is filled with examples. For example a previous answer of mine uses the SwingWorker class to show progress to a user
Edit
As I missed the title of tab part of your question. You could create a 'progress icon' and set that on the tab. The SwingWorker can then be used to update the icon.
An example of such an icon is , which is basically an image you rotate each time some progress is made. The tabbed pane tutorial shows you how to add icons to your tabs (or even use custom components)
Edit2
As it seems my Mac in combination with JDK1.7 makes it much easier to show an animated gif then on other systems, I created a small SSCCE as well, quite similar to that of Andrew but with a rotating icon which does not look like it has been created by, and I quote, 'demented Chimpanzee'. The rotating icon code comes from this site (I used a stripped down version and added the timer). Only thing I am not too happy about is the fact I need to pass my tabbed pane to the rotating icon to trigger. Possible solution is to pull the timer outside the RotatingIcon class, but hey, it's only an SSCCE . Images are not included but were found with Google.
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;
import javax.swing.Timer;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
public class ProgressTabbedPane {
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame( "RotatingIcon" );
JTabbedPane tabbedPane = new JTabbedPane( );
tabbedPane.addTab( "Searching", new RotatingIcon( new ImageIcon( "resources/images/progress-indeterminate.png" ), tabbedPane ),
new JLabel( new ImageIcon( "resources/images/rotatingIcon.gif" ) ) );
frame.getContentPane().add( tabbedPane );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.pack();
frame.setVisible( true );
}
} );
}
private static class RotatingIcon implements Icon{
private final Icon delegateIcon;
private double angleInDegrees = 90;
private final Timer rotatingTimer;
private RotatingIcon( Icon icon, final JComponent component ) {
delegateIcon = icon;
rotatingTimer = new Timer( 100, new ActionListener() {
#Override
public void actionPerformed( ActionEvent e ) {
angleInDegrees = angleInDegrees + 10;
if ( angleInDegrees == 360 ){
angleInDegrees = 0;
}
component.repaint();
}
} );
rotatingTimer.setRepeats( false );
rotatingTimer.start();
}
#Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
rotatingTimer.stop();
Graphics2D g2 = (Graphics2D )g.create();
int cWidth = delegateIcon.getIconWidth() / 2;
int cHeight = delegateIcon.getIconHeight() / 2;
Rectangle r = new Rectangle(x, y, delegateIcon.getIconWidth(), delegateIcon.getIconHeight());
g2.setClip(r);
AffineTransform original = g2.getTransform();
AffineTransform at = new AffineTransform();
at.concatenate(original);
at.rotate(Math.toRadians( angleInDegrees ), x + cWidth, y + cHeight);
g2.setTransform(at);
delegateIcon.paintIcon(c, g2, x, y);
g2.setTransform(original);
rotatingTimer.start();
}
#Override
public int getIconWidth() {
return delegateIcon.getIconWidth();
}
#Override
public int getIconHeight() {
return delegateIcon.getIconHeight();
}
}
}
A screenshot for reference. A shame the icons do not rotate in the screenshot.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
public class ImageOnTab {
ImageOnTab() {
final BufferedImage image = new BufferedImage(
32,32,BufferedImage.TYPE_INT_RGB);
final JTabbedPane pane = new JTabbedPane();
ImageIcon icon = new ImageIcon(image);
pane.addTab( "Progress", icon, new JTree() );
ActionListener listener = new ActionListener() {
int x = 0;
int step = 1;
public void actionPerformed(ActionEvent ae) {
Graphics g = image.createGraphics();
x+=step;
if (step>0) {
if (x>32) {
step=-step;
}
} else if (x<0) {
step=-step;
}
g.setColor(Color.ORANGE);
g.fillRect(0,0,32,32);
g.setColor(Color.RED);
g.fillRect(0,0,x,32);
g.dispose();
pane.repaint();
}
};
Timer timer = new Timer(100,listener);
timer.start();
JOptionPane.showMessageDialog(null, pane);
}
public static void main(String[] args) throws Exception {
//Create the GUI on the event dispatching thread
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new ImageOnTab();
}
});
}
}
#Andrew Thompson
It would be great if the J2SE supported animated GIFs 'out of the box'
in more situations. I tried that animated GIF (nice image, BTW) as a
tab icon, and no, it remains static.
I don't want to read whole ...., but put together code by yours and #trashgod's majesty
1) use Htlm (I'm not good in plain Html)
2) use GlassPane with JLabel#(setOpaque(true))
3) use JLayer (JXLayer is better, becasue Sn'Oracle remove important methods == my view)
4) you have to force ..... for Swing JComponents by #aterai
5) Rob's Animated Icon a few times metioned support by Rob for JTabbedPane
code
import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
//https://stackoverflow.com/questions/3483485/java-jprogressbar-or-equivalent-in-a-jtabbedpane-tab-title/3484251#3484251
public class JTabbedTest {
private JFrame f = new JFrame();
private JTabbedPane jtp = new JTabbedPane();
private URL url = null;
public JTabbedTest() {
try {
url = new URL("http://pscode.org/media/starzoom-thumb.gif");
} catch (MalformedURLException ex) {
Logger.getLogger(JTabbedTest.class.getName()).log(Level.SEVERE, null, ex);
}
ImageIcon ii = new ImageIcon(url);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jtp.setPreferredSize(new Dimension(400, 200));
createTab("Reds", Color.RED);
createTab("Greens", Color.GREEN);
createTab("Blues", Color.BLUE);
f.add(jtp, BorderLayout.CENTER);
jtp.setTitleAt(2, "<html><img src=" + ii + " width=20 height=20></img></html>");
// change foreground Color for disabled tab
/*jtp.setTitleAt(2, "<html><font color=" + (jtp.isEnabledAt(2) ? "black" : "red") + ">"
+ jtp.getTitleAt(2) + "</font></html>");*/
Rectangle tabBounds = jtp.getBoundsAt(0);
Container glassPane = (Container) f.getRootPane().getGlassPane();
glassPane.setVisible(true);
glassPane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.NONE;
gbc.insets = new Insets(tabBounds.y + 23, 0, 0, 5);
gbc.anchor = GridBagConstraints.NORTHEAST;
JButton button = new JButton("My Button Position", ii);
button.setPreferredSize(new Dimension(button.getPreferredSize().width, (int) tabBounds.getHeight() - 2));
glassPane.add(button, gbc);
f.pack();
f.setVisible(true);
}
private void createTab(String name, Color color) {
ProgressIcon icon = new ProgressIcon(color);
jtp.addTab(name, icon, new ColorPanel(jtp, icon));
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JTabbedTest jTabbedTest = new JTabbedTest();
}
});
}
private static class ColorPanel extends JPanel implements ActionListener {
private static final Random rnd = new Random();
private static final long serialVersionUID = 1L;
private final Timer timer = new Timer(1000, this);
private final JLabel label = new JLabel("Stackoverflow!");
private final JTabbedPane parent;
private final ProgressIcon icon;
private final int mask;
private int count;
public ColorPanel(JTabbedPane parent, ProgressIcon icon) {
super(true);
this.parent = parent;
this.icon = icon;
this.mask = icon.color.getRGB();
this.setBackground(icon.color);
label.setForeground(icon.color);
this.add(label);
timer.start();
}
#Override
public void actionPerformed(ActionEvent e) {
this.setBackground(new Color(rnd.nextInt() & mask));
this.icon.update(count += rnd.nextInt(8));
this.parent.repaint();
}
}
private static class ProgressIcon implements Icon {
private static final int H = 16;
private static final int W = 3 * H;
private Color color;
private int w;
public ProgressIcon(Color color) {
this.color = color;
}
public void update(int i) {
w = i % W;
}
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(color);
g.fillRect(x, y, w, H);
}
#Override
public int getIconWidth() {
return W;
}
#Override
public int getIconHeight() {
return H;
}
}
}
In Java, what is the best way to perform drag and drop when the item being dragged is the source control itself? I know a control is nothing but data too, but the difference does have UI impacts.
I'm creating a solitaire-style game where I have card objects of class Card derived from JLabel. I want to drag that card to another location by dropping it onto a yet-to-be named Destination control. During the drag, I want the card to visually move with the mouse and when dropped I want it to either move to this destination object or return to its previous location.
I've done various D-n-D tests and haven't found anything that works under the proper rules of Java's D-D.
For example, if I drag the Card object using true D-n-D I can only create a ghosted image of the card and not a solid image. Also, the cursor changes and I'd rather it did not (I think I can fix that), and the source control remains visible (though it should be easy to make it transparent during the drag)
On the other hand, I can drag the Card beautifully by listening for MouseMotionListener.mouseDragged() events and manually moving the Card to the new location. This works great, but it is not following proper D-n-D because this will not inform other controls of the drag. I figured I could either create my own system to notify the other controls, but that would not be using Java's real D-n-D. Also, if I mix the real Java d-n-d stuff with this method of literally moving the Card during mouseDragged then I assume the real D-n-D stuff will never work because the mouse will never technically be directly over any other control than the card being dragged. This direction just seems like a crude hack.
I hope this makes sense. I've been having problems following samples because they all seem very different, and one that I spent a great deal of time studying looks to be dated a couple years before D-n-D had its major overhaul in version 1.4.
One way to drag a component around a single application and not between applications is to use a JLayeredPane. For example please see my code here: dragging a jlabel around the screen
An example with playing cards could look like this (as long as the playing card image remains valid!):
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.*;
public class PlayingCardTest {
public static void main(String[] args) {
String pathToDeck = "http://www.jfitz.com/cards/classic-playing-cards.png";
try {
final List<ImageIcon> cardImgList = CreateCards.createCardIconList(pathToDeck);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Moving Cards");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CardGameTable(cardImgList, frame));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
} catch (MalformedURLException e) {
e.printStackTrace();
System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
#SuppressWarnings("serial")
class CardGameTable extends JLayeredPane {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color BASE_COLOR = new Color(0, 80, 0);
private static final int CARD_COUNT = 20;
private static final int WIDTH_SHOWING = 20;
private JPanel basePane = new JPanel(null);
public CardGameTable(List<ImageIcon> cardImgList, final JFrame frame) {
basePane.setSize(getPreferredSize());
basePane.setBackground(BASE_COLOR);
add(basePane, JLayeredPane.DEFAULT_LAYER);
final MyMouseAdapter myMouseAdapter = new MyMouseAdapter(this, basePane);
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
for (int i = 0; i < CARD_COUNT; i++) {
JLabel card = new JLabel(cardImgList.remove(0));
card.setSize(card.getPreferredSize());
int x = (PREF_W / 2) + WIDTH_SHOWING * (CARD_COUNT - 2 * i) / 2 -
card.getPreferredSize().width / 2;
int y = PREF_H - card.getPreferredSize().height - WIDTH_SHOWING * 2;
card.setLocation(x, y);
basePane.add(card);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
class MyMouseAdapter extends MouseAdapter {
private JLabel selectedCard = null;
private JLayeredPane cardGameTable = null;
private JPanel basePane = null;
private int deltaX = 0;
private int deltaY = 0;
public MyMouseAdapter(JLayeredPane gameTable, JPanel basePane) {
this.cardGameTable = gameTable;
this.basePane = basePane;
}
#Override
public void mousePressed(MouseEvent mEvt) {
Component comp = basePane.getComponentAt(mEvt.getPoint());
if (comp != null && comp instanceof JLabel) {
selectedCard = (JLabel) comp;
basePane.remove(selectedCard);
basePane.revalidate();
basePane.repaint();
cardGameTable.add(selectedCard, JLayeredPane.DRAG_LAYER);
cardGameTable.revalidate();
cardGameTable.repaint();
deltaX = mEvt.getX() - selectedCard.getX();
deltaY = mEvt.getY() - selectedCard.getY();
}
}
#Override
public void mouseReleased(MouseEvent mEvt) {
if (selectedCard != null) {
cardGameTable.remove(selectedCard);
cardGameTable.revalidate();
cardGameTable.repaint();
basePane.add(selectedCard, 0);
basePane.revalidate();
basePane.repaint();
selectedCard = null;
}
}
#Override
public void mouseDragged(MouseEvent mEvt) {
if (selectedCard != null) {
int x = mEvt.getX() - deltaX;
int y = mEvt.getY() - deltaY;
selectedCard.setLocation(x, y);
cardGameTable.revalidate();
cardGameTable.repaint();
}
}
}
class CreateCards {
private static final int SUIT_COUNT = 4;
private static final int RANK_COUNT = 13;
public static List<ImageIcon> createCardIconList(String pathToDeck)
throws MalformedURLException, IOException {
BufferedImage fullDeckImg = ImageIO.read(new URL(pathToDeck));
int width = fullDeckImg.getWidth();
int height = fullDeckImg.getHeight();
List<ImageIcon> iconList = new ArrayList<ImageIcon>();
for (int suit = 0; suit < SUIT_COUNT; suit++) {
for (int rank = 0; rank < RANK_COUNT; rank++) {
int x = (rank * width) / RANK_COUNT;
int y = (suit * height) / SUIT_COUNT;
int w = width / RANK_COUNT;
int h = height / SUIT_COUNT;
BufferedImage cardImg = fullDeckImg.getSubimage(x, y, w, h);
iconList.add(new ImageIcon(cardImg));
}
}
Collections.shuffle(iconList);
return iconList;
}
}