Continuous animation using java applets - java

I want to create a continuous animation of a moving truck in java applets. In this the truck should reappear from the other side as soon as it disappears.
I tried using one truck but then it appears on the other side only after completely disappearing from one side. Hence, I used two.
Following is my code:
public class prc1 extends Applet implements Runnable{
Thread t;
int x=0;
int y=0;
int i;
public void start(){
t = new Thread(this);
t.start();
}
public void paint(Graphics g){
g.setColor(Color.gray);
g.fillRect(x,10,70,45);
g.setColor(Color.yellow);
g.fillRoundRect(50+x,15,35,35,10,10);
g.setColor(Color.black);
g.fillArc(5+x,50,10,10,0,360);
g.fillArc(55+x,50,10,10,0,360);
g.setColor(Color.gray);
g.fillRect(y,10,70,45);
g.setColor(Color.yellow);
g.fillRoundRect(50+y,15,35,35,10,10);
g.setColor(Color.black);
g.fillArc(5+y,50,10,10,0,360);
g.fillArc(55+y,50,10,10,0,360);
}
public void run(){
try{
y=-90;
x=0;
for(; ;){
Thread.sleep(100);
repaint();
run();
x=x+5;
if(x==400){
for(; ;){
y=y+5;
Thread.sleep(100);
repaint();
run();
x=x-5;
if(x==0){
y=-90;
break;
}
}
}
}
}catch(InterruptedException e){ }
}
}
Now the problem is that the truck does not move at all.

So the basic idea is, you need to determine when the truck begins to leave the viewable area and then calculate the amount of "overflow", which would then allow you to paint the truck a second time at a new position subtracted from the overflow.
I've broken you code down so the Truck is it's own class, this makes it easier to deal with and allows a little more control over the painting process.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Truck truck;
public TestPane() {
truck = new Truck();
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
truck.update(getSize());
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
truck.paint(g2d);
g2d.dispose();
}
}
public class Truck {
private int x, y;
// I had to calculate these
private int width = 85;
private int height = 65;
private int xDelta = 4;
private int xOverFlow = 0;
public void update(Dimension bounds) {
x += xDelta;
if (x > bounds.width) {
x = 0;
}
if (x + width > bounds.width) {
xOverFlow = (x + width) - bounds.width;
} else {
xOverFlow = -1;
}
}
public void paint(Graphics2D g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(x, y);
paintTruck(g2d);
g2d.dispose();
if (xOverFlow > 0) {
g2d = (Graphics2D) g.create();
g2d.translate(xOverFlow - width, y);
paintTruck(g2d);
g2d.dispose();
}
}
protected void paintTruck(Graphics2D g2d) {
g2d.setColor(Color.gray);
g2d.fillRect(0, 10, 70, 45);
g2d.setColor(Color.yellow);
g2d.fillRoundRect(50, 15, 35, 35, 10, 10);
g2d.setColor(Color.black);
g2d.fillArc(5, 50, 10, 10, 0, 360);
g2d.fillArc(55, 50, 10, 10, 0, 360);
g2d.setColor(Color.gray);
g2d.fillRect(0, 10, 70, 45);
g2d.setColor(Color.yellow);
g2d.fillRoundRect(50, 15, 35, 35, 10, 10);
g2d.setColor(Color.black);
g2d.fillArc(5, 50, 10, 10, 0, 360);
g2d.fillArc(55, 50, 10, 10, 0, 360);
}
}
}
Another option might be to have more than one instance of truck, so as the second one begins to leave the viewable you could create another one
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Truck> trucks;
public TestPane() {
trucks = new ArrayList<>(2);
trucks.add(new Truck());
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Truck[] truckArray = trucks.toArray(new Truck[trucks.size()]);
for (Truck truck : truckArray) {
truck.update(getSize());
if (truck.getX() > getWidth()) {
trucks.remove(truck);
} else if (truck.getX() + truck.getWidth() > getWidth() && trucks.size() == 1) {
trucks.add(new Truck());
}
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Truck truck : trucks) {
truck.paint(g2d);
}
g2d.dispose();
}
}
public class Truck {
// I had to calculate these
private int width = 85;
private int height = 65;
private int x = -width;
private int y;
private int xDelta = 4;
public void update(Dimension bounds) {
x += xDelta;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void paint(Graphics2D g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(x, y);
paintTruck(g2d);
g2d.dispose();
}
protected void paintTruck(Graphics2D g2d) {
g2d.setColor(Color.gray);
g2d.fillRect(0, 10, 70, 45);
g2d.setColor(Color.yellow);
g2d.fillRoundRect(50, 15, 35, 35, 10, 10);
g2d.setColor(Color.black);
g2d.fillArc(5, 50, 10, 10, 0, 360);
g2d.fillArc(55, 50, 10, 10, 0, 360);
g2d.setColor(Color.gray);
g2d.fillRect(0, 10, 70, 45);
g2d.setColor(Color.yellow);
g2d.fillRoundRect(50, 15, 35, 35, 10, 10);
g2d.setColor(Color.black);
g2d.fillArc(5, 50, 10, 10, 0, 360);
g2d.fillArc(55, 50, 10, 10, 0, 360);
}
}
}

Related

How to move Polygon Object with KeyListener in Java

I am working on a 2D game as a learning project and I have hit a bump. I cannot figure out how to move a Polygon object using the KeyListener within a JPanel (which is added to a JFrame). I've tried the frog.translate(int x, int y) method, which does not update the location. I've also tried changing the array coordinates manually. A sample of my code is below:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Board extends JPanel implements KeyListener {
private Frog frog;
public Board() {
setBackground(Color.GREEN);
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
frog = new Frog();
// Frog graphics
g2.setColor(Color.BLACK);
g2.drawPolygon(frog);
g2.setColor(new Color(0,150,15));
g2.fillPolygon(frog);
}
#Override
public void keyTyped(KeyEvent ke) {
}
#Override
public void keyPressed(KeyEvent ke) {
int c = ke.getKeyCode();
if(c == KeyEvent.VK_LEFT){
frog.moveFrogLeft(25);
//frog.translate(-25,0);
}
if(c == KeyEvent.VK_RIGHT){
frog.moveFrogRight(25);
//frog.translate(25,0);
}
if(c == KeyEvent.VK_UP){
frog.moveFrogUp(25);
//frog.translate(0,-25);
}
if(c == KeyEvent.VK_DOWN){
frog.moveFrogDown(25);
//frog.translate(0,25);
}
repaint();
}
#Override
public void keyReleased(KeyEvent ke) {
}
}
///////////////////////
import java.awt.Polygon;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Frog extends Polygon {
private Integer[] xcoord;
private Integer[] ycoord;
public Frog(){
xcoord = new Integer[] {5,10,10,15,15,20,
20,30,30,35,35,40,40,
45,45,40,40,30,30,40,
40,45,45,40,40,35,35,
30,30,20,20,15,15,10,
10,5,5,10,10,20,20,
10,10,5,5};
ycoord = new Integer[] {10,10,5,5,20,20,
10,10,20,20,5,5,10,10,
15,15,25,25,30,30,35,35,
40,40,45,45,35,35,40,40,
35,35,45,45,40,40,35,35,
30,30,25,25,15,15,10};
for(int i = 0; i < xcoord.length; i++){
this.addPoint(xcoord[i],ycoord[i]);
}
}
public void moveFrogLeft(int x) {
if(xcoord[0] - x < 0){
//do nothing
} else {
for(int i = 0; i < xcoord.length; i++){
xcoord[i] = xcoord[i] - x;
}
}
}
public void moveFrogRight(int x){
if(xcoord[0] + x > 600){
//do nothing
} else {
for(int i = 0; i < xcoord.length; i++){
xcoord[i] = xcoord[i] + x;
}
}
}
public void moveFrogUp(int y){
if(ycoord[0] - y < 0){
//do nothing
} else {
for(int i = 0; i < ycoord.length; i++){
ycoord[i] = ycoord[i] - y;
}
}
}
public void moveFrogDown(int y){
if(ycoord[0] + y > 600){
//do nothing
} else {
for(int i = 0; i < ycoord.length; i++){
ycoord[i] = ycoord[i] + y;
}
}
}
}
This code has a simple issue:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
frog = new Frog();// <-- !!!!!
// Frog graphics
g2.setColor(Color.BLACK);
g2.drawPolygon(frog);
g2.setColor(new Color(0,150,15));
g2.fillPolygon(frog);
}
The marked line overwrites the frog with a new instance, every time the frog is painted, thus reseting it to the original point. Apart from the obvious issue that this is the reason for the unexpected behaviour, never do any unnecessary calculations in the paintComponent(...)-method. Any precomputation, Object-generation, etc. should be done outside of paintComponent!!!
First of all, I would strongly discourage you from using KeyListener, it's troublesome at the best, a better choice would be to make use of the Key Bindings API which was desgiend to fix the short commings of the KeyListener API.
Second, you shouldn't be modifying the points of the polygon, the 2D Graphics API is actually capable of some really neat tricks which makes it much easier and faster to change the location (and rotation and scale) of what you are drawing.
Take a look closer look at 2D Graphics for more details.
Instead of changing the points of the polygon, which isn't taking into consideration the viewable bounds, you could simply use an AffineTransform...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Point location = frog.getLocation();
AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
g2d.transform(at);
g2d.setColor(new Color(0, 150, 15));
g2d.fill(frog);
g2d.setColor(Color.BLACK);
g2d.draw(frog);
g2d.dispose();
}
This just simply changes the origin point of the Graphics context to the location where you want to paint the polygon (yes, there's another reason why I'm using AffineTransform
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Board());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected enum VerticalDirection {
NONE, UP, DOWN;
}
protected enum HorizontalDirection {
NONE, LEFT, RIGHT;
}
public static class Board extends JPanel {
protected static final int Y_DELTA = 4;
protected static final int X_DELTA = 4;
private Frog frog;
private VerticalDirection verticalDirection = VerticalDirection.NONE;
private HorizontalDirection horizontalDirection = HorizontalDirection.NONE;
public Board() {
setBackground(Color.GREEN);
frog = new Frog();
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point location = frog.getLocation();
switch (verticalDirection) {
case UP:
location.y -= Y_DELTA;
break;
case DOWN:
location.y += Y_DELTA;
break;
}
switch (horizontalDirection) {
case LEFT:
location.x -= X_DELTA;
break;
case RIGHT:
location.x += X_DELTA;
break;
}
Rectangle bounds = frog.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
if (location.y < 0) {
location.y = 0;
} else if (location.y + height > getHeight()) {
location.y = getHeight() - height;
}
if (location.x < 0) {
location.x = 0;
} else if (location.x + width > getWidth()) {
location.x = getWidth() - width;
}
frog.setLocation(location);
repaint();
}
});
timer.start();
addPressedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.UP));
addPressedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.DOWN));
addPressedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.LEFT));
addPressedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.RIGHT));
addReleasedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.NONE));
addReleasedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.NONE));
addReleasedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.NONE));
addReleasedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.NONE));
}
protected void addPressedKeyBinding(String name, int virtuaKey, Action action) {
addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(virtuaKey, 0, false), action);
}
protected void addReleasedKeyBinding(String name, int virtuaKey, Action action) {
addKeyBinding(name + ".released", KeyStroke.getKeyStroke(virtuaKey, 0, true), action);
}
protected void addKeyBinding(String name, KeyStroke ks, Action action) {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(ks, name);
am.put(name, action);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Point location = frog.getLocation();
AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
g2d.transform(at);
g2d.setColor(new Color(0, 150, 15));
g2d.fill(frog);
g2d.setColor(Color.BLACK);
g2d.draw(frog);
g2d.dispose();
}
protected class VerticalMovementAction extends AbstractAction {
private VerticalDirection direction;
public VerticalMovementAction(VerticalDirection direction) {
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
verticalDirection = direction;
}
}
protected class HorizontalMovementAction extends AbstractAction {
private HorizontalDirection direction;
public HorizontalMovementAction(HorizontalDirection direction) {
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
horizontalDirection = direction;
}
}
}
public static class Frog extends Polygon {
private Integer[] xcoord;
private Integer[] ycoord;
private Point location;
public Frog() {
location = new Point(0, 0);
xcoord = new Integer[]{5, 10, 10, 15, 15, 20,
20, 30, 30, 35, 35, 40, 40,
45, 45, 40, 40, 30, 30, 40,
40, 45, 45, 40, 40, 35, 35,
30, 30, 20, 20, 15, 15, 10,
10, 5, 5, 10, 10, 20, 20,
10, 10, 5, 5};
ycoord = new Integer[]{10, 10, 5, 5, 20, 20,
10, 10, 20, 20, 5, 5, 10, 10,
15, 15, 25, 25, 30, 30, 35, 35,
40, 40, 45, 45, 35, 35, 40, 40,
35, 35, 45, 45, 40, 40, 35, 35,
30, 30, 25, 25, 15, 15, 10};
for (int i = 0; i < xcoord.length; i++) {
this.addPoint(xcoord[i], ycoord[i]);
}
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
}
}
Now, you're probably wondering why I would use AffineTransform instead of Graphcis2D#translate, the main reason is, it's easy to apply other transformations, like rotation...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Point location = frog.getLocation();
Rectangle bounds = frog.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
at.rotate(Math.toRadians(angle), width / 2, height / 2);
g2d.transform(at);
g2d.setColor(new Color(0, 150, 15));
g2d.fill(frog);
g2d.setColor(Color.BLACK);
g2d.draw(frog);
g2d.dispose();
}
All this does is apply a compound transformation, moving the Graphics context's origin and rotation matrix
And for a complete example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Board());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected enum VerticalDirection {
NONE, UP, DOWN;
}
protected enum HorizontalDirection {
NONE, LEFT, RIGHT;
}
public static class Board extends JPanel {
protected static final int Y_DELTA = 4;
protected static final int X_DELTA = 4;
private Frog frog;
private VerticalDirection verticalDirection = VerticalDirection.NONE;
private HorizontalDirection horizontalDirection = HorizontalDirection.NONE;
private double angle = 0; // Up...
public Board() {
setBackground(Color.GREEN);
frog = new Frog();
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Point location = frog.getLocation();
switch (verticalDirection) {
case UP:
angle = 0;
location.y -= Y_DELTA;
break;
case DOWN:
angle = 180;
location.y += Y_DELTA;
break;
}
switch (horizontalDirection) {
case LEFT:
location.x -= X_DELTA;
angle = 270;
break;
case RIGHT:
location.x += X_DELTA;
angle = 90;
break;
}
Rectangle bounds = frog.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
if (location.y < 0) {
location.y = 0;
} else if (location.y + height > getHeight()) {
location.y = getHeight() - height;
}
if (location.x < 0) {
location.x = 0;
} else if (location.x + width > getWidth()) {
location.x = getWidth() - width;
}
frog.setLocation(location);
repaint();
}
});
timer.start();
addPressedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.UP));
addPressedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.DOWN));
addPressedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.LEFT));
addPressedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.RIGHT));
addReleasedKeyBinding("up", KeyEvent.VK_UP, new VerticalMovementAction(VerticalDirection.NONE));
addReleasedKeyBinding("down", KeyEvent.VK_DOWN, new VerticalMovementAction(VerticalDirection.NONE));
addReleasedKeyBinding("left", KeyEvent.VK_LEFT, new HorizontalMovementAction(HorizontalDirection.NONE));
addReleasedKeyBinding("right", KeyEvent.VK_RIGHT, new HorizontalMovementAction(HorizontalDirection.NONE));
}
protected void addPressedKeyBinding(String name, int virtuaKey, Action action) {
addKeyBinding(name + ".pressed", KeyStroke.getKeyStroke(virtuaKey, 0, false), action);
}
protected void addReleasedKeyBinding(String name, int virtuaKey, Action action) {
addKeyBinding(name + ".released", KeyStroke.getKeyStroke(virtuaKey, 0, true), action);
}
protected void addKeyBinding(String name, KeyStroke ks, Action action) {
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(ks, name);
am.put(name, action);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Point location = frog.getLocation();
Rectangle bounds = frog.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
AffineTransform at = AffineTransform.getTranslateInstance(location.x, location.y);
at.rotate(Math.toRadians(angle), width / 2, height / 2);
g2d.transform(at);
g2d.setColor(new Color(0, 150, 15));
g2d.fill(frog);
g2d.setColor(Color.BLACK);
g2d.draw(frog);
g2d.dispose();
}
protected class VerticalMovementAction extends AbstractAction {
private VerticalDirection direction;
public VerticalMovementAction(VerticalDirection direction) {
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
verticalDirection = direction;
}
}
protected class HorizontalMovementAction extends AbstractAction {
private HorizontalDirection direction;
public HorizontalMovementAction(HorizontalDirection direction) {
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
horizontalDirection = direction;
}
}
}
public static class Frog extends Polygon {
private Integer[] xcoord;
private Integer[] ycoord;
private Point location;
public Frog() {
location = new Point(0, 0);
xcoord = new Integer[]{5, 10, 10, 15, 15, 20,
20, 30, 30, 35, 35, 40, 40,
45, 45, 40, 40, 30, 30, 40,
40, 45, 45, 40, 40, 35, 35,
30, 30, 20, 20, 15, 15, 10,
10, 5, 5, 10, 10, 20, 20,
10, 10, 5, 5};
ycoord = new Integer[]{10, 10, 5, 5, 20, 20,
10, 10, 20, 20, 5, 5, 10, 10,
15, 15, 25, 25, 30, 30, 35, 35,
40, 40, 45, 45, 35, 35, 40, 40,
35, 35, 45, 45, 40, 40, 35, 35,
30, 30, 25, 25, 15, 15, 10};
// I rest the coordinates back to 0x0 because it's easier to
// deal with when applying a rotation...
for (int index = 0; index < xcoord.length; index++) {
xcoord[index] -= 5;
}
for (int index = 0; index < ycoord.length; index++) {
ycoord[index] -= 5;
}
for (int i = 0; i < xcoord.length; i++) {
this.addPoint(xcoord[i], ycoord[i]);
}
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
}
}
Look ma, no maths!
Don't create a Frog in your paintComponent() method! That is throwing away the existing frog and creating a new one with default position.
You should create all of your Frog instances when you initialize your panel, or possibly in response to a b button click to "create a new frog".

Graphics Object not displaying to screen

So the answer to this might be completely obvious, but I just can't see it. Why is my Graphics object not displaying what I tell it to? I'm fairly certain I'm honoring Swing concurrency and such but perhaps not. Here's my JPanel code:
package com.kraken.towerdefense.graphics;
import com.kraken.towerdefense.TowerDefense;
import com.kraken.towerdefense.listener.KeyHandler;
import com.kraken.towerdefense.listener.MouseMotionHandler;
import com.kraken.towerdefense.scene.Scene;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
private int FPS = 0;
public Scene scene;
TowerDefense tD;
private boolean running = false;
public RoundRectangle2D.Float playGame, quitGame;
public boolean playGameHighlighted, quitGameHighlighted;
#Override
public void run() {
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
while (running) {
repaint();
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
FPS = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
}
System.exit(0);
}
public Screen(TowerDefense tD) {
thread.start();
addKeyListener(new KeyHandler(this));
addMouseMotionListener(new MouseMotionHandler(this));
this.tD = tD;
scene = Scene.MENU;
}
#Override
public void paintComponent(Graphics g2) {
super.paintComponent(g2);
playGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) - 100, 400, 100, 10, 10);
quitGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) + 20, 400, 100, 10, 10);
Graphics2D g = (Graphics2D) g2.create();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.clearRect(0, 0, getWidth(), getHeight());
g.drawString("FPS: " + FPS, 10, 10);
if (scene == Scene.MENU) {
if (playGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.fill(playGame);
if (quitGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.fill(quitGame);
g.setColor(Color.WHITE);
g.setFont(new Font("Gisha", Font.PLAIN, 20));
g.drawString("Play", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Play") / 2), (getHeight() / 2) - 45);
g.drawString("Quit", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Quit") / 2), (getHeight() / 2) + 75);
}
}
public class KeyTyped {
public void keyESC() {
running = false;
}
}
}
And here's my Scene Enum:
package com.kraken.towerdefense.scene;
public enum Scene {
MENU,
GAME
}
I'm pretty sure I don't need to supply the JFrame code, but if necessary I will. Any other solutions to problems in my code you could give would be greatly appreciated. Thanks!
EDIT 1
Here's my MouseMotionListener class:
package com.kraken.towerdefense.listener;
import com.kraken.towerdefense.graphics.Screen;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class MouseMotionHandler implements MouseMotionListener {
Screen screen;
public MouseMotionHandler(Screen screen) {
this.screen = screen;
}
#Override
public void mouseDragged(MouseEvent e) {
}
#Override
public void mouseMoved(MouseEvent e) {
if (screen.playGame.contains(e.getPoint())) {
screen.playGameHighlighted = true;
} else {
screen.playGameHighlighted = false;
}
if (screen.quitGame.contains(e.getPoint())) {
screen.quitGameHighlighted = true;
} else {
screen.playGameHighlighted = false;
}
}
}
Here's my JFrame code:
package com.kraken.towerdefense;
import com.kraken.towerdefense.graphics.Screen;
import javax.swing.*;
public class TowerDefense extends JFrame {
public static void main(String[] args) {
new TowerDefense();
}
public TowerDefense() {
setExtendedState(MAXIMIZED_BOTH);
setUndecorated(true);
setTitle("Tower Defense");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
Screen screen = new Screen(this);
this.add(screen);
setVisible(true);
}
}
And here's my KeyListener code:
package com.kraken.towerdefense.listener;
import com.kraken.towerdefense.graphics.Screen;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class KeyHandler implements KeyListener {
private Screen screen;
private Screen.KeyTyped keyTyped;
public KeyHandler(Screen screen) {
this.screen = screen;
keyTyped = screen.new KeyTyped();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 27) {
keyTyped.keyESC();
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
So those are all my classes, I hope that helps
So, I gutted your code to make it run and was capable of displaying...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GraphicsTest {
public static void main(String[] args) {
new GraphicsTest();
}
public GraphicsTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Screen());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Scene {
MENU,
GAME
}
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
private int FPS = 0;
public Scene scene;
private boolean running = false;
#Override
public void run() {
long lastFrame = System.currentTimeMillis();
int frames = 0;
running = true;
scene = Scene.MENU;
while (running) {
repaint();
frames++;
if (System.currentTimeMillis() - 1000 >= lastFrame) {
FPS = frames;
frames = 0;
lastFrame = System.currentTimeMillis();
}
}
System.exit(0);
}
public Screen() {
thread.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.clearRect(0, 0, getWidth(), getHeight());
g.drawString("FPS: " + FPS, 10, 10);
if (scene == Scene.MENU) {
g.setColor(Color.BLACK);
g.fillRoundRect((getWidth() / 2) - 100, (getHeight() / 2) - 50, 200, 100, 25, 25);
}
}
}
}
So that suggests that the problem you're describing is some where else.
To me this;
g.clearRect(0, 0, tD.getWidth(), tD.getHeight());
looks suspicious, as you're relying on the TowerDefense properties when you should relying on the components own width/height properties (apart from clearRect actually not been required in this context).
This further makes me suspicious that you're not actually adding the Screen component to anything that is displayable
The other possible problem is you're making use of an appropriate layout manager, but since your Screen class doesn't supply any preferredSize hints, this would be an additional issue which you're not demonstrating
Updated based on changes to the original question
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import static java.awt.Frame.MAXIMIZED_BOTH;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import javafx.scene.Scene;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
public class TowerDefense extends JFrame {
public static void main(String[] args) {
new TowerDefense();
}
public TowerDefense() {
// setExtendedState(MAXIMIZED_BOTH);
// setUndecorated(true);
setTitle("Tower Defense");
setDefaultCloseOperation(EXIT_ON_CLOSE);
// setResizable(false);
Screen screen = new Screen(this);
this.add(screen);
pack();
setVisible(true);
}
public enum Scene {
MENU,
GAME
}
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
private int FPS = 0;
public Scene scene;
TowerDefense tD;
private boolean running = false;
public RoundRectangle2D.Float playGame, quitGame;
public boolean playGameHighlighted, quitGameHighlighted;
#Override
public void run() {
// long lastFrame = System.currentTimeMillis();
// int frames = 0;
//
// running = true;
//
// while (running) {
// repaint();
//
// frames++;
//
// if (System.currentTimeMillis() - 1000 >= lastFrame) {
// FPS = frames;
// frames = 0;
//
// lastFrame = System.currentTimeMillis();
// }
// }
//
// System.exit(0);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public Screen(TowerDefense tD) {
thread.start();
addKeyListener(new KeyHandler(this));
addMouseMotionListener(
new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
playGameHighlighted = playGame.contains(e.getPoint());
quitGameHighlighted = quitGame.contains(e.getPoint());
repaint();
}
});
this.tD = tD;
scene = Scene.MENU;
}
#Override
public void paintComponent(Graphics g2) {
super.paintComponent(g2);
playGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) - 100, 400, 100, 10, 10);
quitGame = new RoundRectangle2D.Float((getWidth() / 2) - 200, (getHeight() / 2) + 20, 400, 100, 10, 10);
Graphics2D g = (Graphics2D) g2.create();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.clearRect(0, 0, getWidth(), getHeight());
g.drawString("FPS: " + FPS, 10, 10);
if (scene == Scene.MENU) {
if (playGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.draw(playGame);
if (quitGameHighlighted) {
g.setColor(new Color(255, 152, 56));
} else {
g.setColor(new Color(4, 47, 61));
}
g.draw(quitGame);
g.setColor(Color.WHITE);
g.setFont(new Font("Gisha", Font.PLAIN, 20));
g.drawString("Play", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Play") / 2), (getHeight() / 2) - 45);
g.drawString("Quit", (getWidth() / 2) - (g.getFontMetrics().stringWidth("Quit") / 2), (getHeight() / 2) + 75);
}
}
public class KeyTyped {
public void keyESC() {
running = false;
}
}
}
public class KeyHandler implements KeyListener {
private Screen screen;
private Screen.KeyTyped keyTyped;
public KeyHandler(Screen screen) {
this.screen = screen;
keyTyped = screen.new KeyTyped();
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 27) {
keyTyped.keyESC();
}
}
#Override
public void keyReleased(KeyEvent e) {
}
}
}

change size rounded button in java

Hi I am programming a game and i thought a rounded button would look nice but i am curious how do i change the size of said button to something bigger? here is my code
public JavaApplication3(String label) {
super(label);
Dimension size = getPreferredSize();
size.width = size.height = Math.max(size.width,size.height);
setPreferredSize(size);
setContentAreaFilled(false);
}
protected void paintComponent(Graphics g) {
if (getModel().isArmed()) {
g.setColor(Color.RED);
} else {
g.setColor(Color.yellow);
}
g.fillOval(0, 0, getSize().width-1,getSize().height-1);
super.paintComponent(g);
}
protected void paintBorder(Graphics g) {
g.setColor(getForeground());
g.drawOval(0, 0, getSize().width-1, getSize().height-1);
}
Shape shape;
public boolean contains(int x, int y) {
if (shape == null ||
!shape.getBounds().equals(getBounds())) {
shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight());
}
return shape.contains(x, y);
}
Hope someone can help :)
this changes the size of your button with every time you click on it.
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("klick");
setSize(new Dimension(newSizeWidth, newSizeHeight));
}
});
}
just call the
setSize(new Dimension(newSizeWidth, newSizeHeight));
method.
here is some working code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
public class test2 extends JButton {
int width = 100;
int height = 100;
public test2(String label) {
super(label);
setSize(width, height);
setContentAreaFilled(false);
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("klick");
setSize(new Dimension(width--, height--));
}
});
}
protected void paintComponent(Graphics g) {
if (getModel().isArmed()) {
System.out.println(width + " " + height);
g.setColor(Color.RED);
} else {
g.setColor(Color.yellow);
}
g.fillOval(0, 0, getSize().width - 1, getSize().height - 1);
super.paintComponent(g);
}
protected void paintBorder(Graphics g) {
g.setColor(getForeground());
g.drawOval(0, 0, getSize().width - 1, getSize().height - 1);
}
Shape shape;
public boolean contains(int x, int y) {
if (shape == null || !shape.getBounds().equals(getBounds())) {
shape = new Ellipse2D.Float(0, 0, getWidth(), getHeight());
}
return shape.contains(x, y);
}
static JFrame f;
public static void main(String[] args) {
f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setSize(200, 200);
f.add(new test2("label"));
f.setLayout(new BorderLayout());
f.setVisible(true);
}
}

Create a Trailing line of blood behind a player

I am currently working on a simple top down shooter. The object is a ball that slides around the screen, and I am trying to make a sort of wet-dragging affect.
I am using Java Swing and just the default Graphics2d lib inside.
This is what I have:
and this is my goal:
I need to know how I can make a curved line that has the ability to change alpha at the trailing end. I have searched online but I can only find non-dynamic solutions. (The tail needs to update as the player moves across the screen.)
A simple solution might be to simple add each point to a List of Points which before the player is moved.
You would simply then need to iterate this list and either simple use something like Graphics#drawLine or even GeneralPath to render the "drag" line, for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Drag {
public static void main(String[] args) {
new Drag();
}
public Drag() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Point> points;
private Point pos;
private int diametere = 10;
public TestPane() {
points = new ArrayList<>(25);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down");
ActionMap am = getActionMap();
am.put("left", new MoveAction(-5, 0));
am.put("right", new MoveAction(5, 0));
am.put("up", new MoveAction(0, -5));
am.put("down", new MoveAction(0, 5));
pos = new Point(100 - (diametere / 2), 100 - (diametere / 2));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (points.size() > 1) {
g2d.setColor(Color.RED);
GeneralPath path = new GeneralPath();
boolean started = false;
System.out.println("----");
for (Point p : points) {
if (started) {
System.out.println(p);
path.lineTo(p.x, p.y);
} else {
path.moveTo(p.x, p.y);
started = true;
}
}
g2d.draw(path);
}
int radius = (int) (diametere / 2d);
g2d.setColor(Color.GREEN);
g2d.draw(new Ellipse2D.Double(pos.x - radius, pos.y - radius, diametere, diametere));
g2d.dispose();
}
protected void moveBy(int xDelta, int yDelta) {
if (pos.x + xDelta < 0) {
xDelta = 0;
pos.x = 0;
} else if (pos.x + xDelta + diametere > getWidth()) {
xDelta = 0;
pos.x = getWidth() - diametere;
}
if (pos.y + yDelta < 0) {
yDelta = 0;
pos.y = 0;
} else if (pos.y + yDelta + diametere > getHeight()) {
yDelta = 0;
pos.y = getWidth() - diametere;
}
points.add(new Point(pos));
pos.x += xDelta;
pos.y += yDelta;
repaint();
}
public class MoveAction extends AbstractAction {
private int xDelta;
private int yDelta;
public MoveAction(int xDelta, int yDelta) {
this.xDelta = xDelta;
this.yDelta = yDelta;
}
#Override
public void actionPerformed(ActionEvent e) {
moveBy(xDelta, yDelta);
}
}
}
}
Hmm.. maybe you need something like this:
public class BallArea extends JComponent {
static final int MAX_SIZE = 63;
static final BasicStroke stroke = new BasicStroke(5);
final Queue<Point> points = new LinkedList();
public BallArea() {
setSize(400, 400);
setBackground(Color.BLACK);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
if (points.size() >= MAX_SIZE) {
points.poll();
}
points.add(e.getPoint());
repaint();
}
});
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(stroke);
int i = 1;
Point prev = null;
for (Point p : points) {
if (prev == null) {
prev = p;
continue;
}
g2.setColor(new Color(255, 0, 0, i*4));
g.drawLine(prev.x, prev.y, p.x, p.y);
i++;
prev = p;
}
}
}

How to Draw a Transparent Background?

I am trying to make a piece of a JPanel transparent, but I cannot quite get it to work. Is it possible to do this?
import java.awt.*;
import javax.swing.*;
public class ClearPanel extends JPanel{
public static void main(String[] args) {
ClearPanel c = new ClearPanel();
c.setPreferredSize(new Dimension(200, 200));
c.setOpaque(false);
JPanel backPanel = new JPanel();
backPanel.setBackground(Color.CYAN);
backPanel.add(c);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(backPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(0, 0, 200, 200);
g.clearRect(45, 45, 50, 50);
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.0f));
g2.fillRect(75, 75, 50, 50);
}
}
The oval should be opaque, but the rectangles I would like to be transparent. By transparent, I mean that I should be able to see the panel behind the ClearPanel.
Going off of MadProgrammer's answer, is there any way to make that gray box draw where it is outside of the area, but remain transparent where it is in the area?
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle fill = new Rectangle(getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
Rectangle hole = new Rectangle(0, 0, 100, 100);
Area area = new Area(fill);
area.subtract(new Area(hole));
g2d.setColor(getBackground());
g2d.fill(area);
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.fill(hole);
g2d.setComposite(AlphaComposite.SrcOver.derive(1.0f));
g2d.setColor(Color.DARK_GRAY);
if(area.contains(0,0,100,200))
g2d.fillRect(0, 0, 100, 200);
g2d.dispose();
}
The problem you have is, by default, JPanel is opaque, meaning that the repaint will NOT paint anything under it.
You need to set the the panel to transparent and then take over the painting of the background.
Now, the real trick begins. If you simply fill the component and then try and paint transparent section over the top of it, you will simply be painting a transparent section over a opaque background...not very helpful.
What you need to do is not fill the area you want to remain transparent.
You can accomplish this by using a Area shape, which has a neat trick of been able to append/add and remove shapes from it.
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransparentPane {
public static void main(String[] args) {
new TransparentPane();
}
public TransparentPane() {
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) {
}
BackgroundPane backgroundPane = new BackgroundPane();
backgroundPane.setBackground(Color.RED);
backgroundPane.setLayout(new BorderLayout());
backgroundPane.add(new TranslucentPane());
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(backgroundPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage bg;
public BackgroundPane() {
try {
bg = ImageIO.read(new File("/path/to/your/image.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return bg == null ? super.getPreferredSize() : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - bg.getWidth()) / 2;
int y = (height - bg.getHeight()) / 2;
g.drawImage(bg, x, y, this);
}
}
}
public class TranslucentPane extends JPanel {
public TranslucentPane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle fill = new Rectangle(getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
int radius = Math.min(width, height) / 2;
int x = (width - radius) / 2;
int y = (height - radius) / 2;
Ellipse2D hole = new Ellipse2D.Float(x, y, radius, radius);
Area area = new Area(fill);
area.subtract(new Area(hole));
g2d.setColor(getBackground());
g2d.fill(area);
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.25f));
g2d.fill(hole);
g2d.dispose();
}
}
}
Update
Well, that took a little longer the I expected...
Basically, we need to create a mask of the shape that subtracts the hole from the rectangle we want to display, then subtract that result from the rectangle we want to diplay
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransparentPane {
public static void main(String[] args) {
new TransparentPane();
}
public TransparentPane() {
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) {
}
BackgroundPane backgroundPane = new BackgroundPane();
backgroundPane.setBackground(Color.RED);
backgroundPane.setLayout(new BorderLayout());
backgroundPane.add(new TranslucentPane());
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(backgroundPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage bg;
public BackgroundPane() {
try {
bg = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/Evil_Small.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return bg == null ? super.getPreferredSize() : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - bg.getWidth()) / 2;
int y = (height - bg.getHeight()) / 2;
g.drawImage(bg, x, y, this);
}
}
}
public class TranslucentPane extends JPanel {
public TranslucentPane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle fill = new Rectangle(getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
int radius = Math.min(width, height) / 2;
int x = (width - radius) / 2;
int y = (height - radius) / 2;
Ellipse2D hole = new Ellipse2D.Float(x, y, radius, radius);
Area area = new Area(fill);
area.subtract(new Area(hole));
g2d.setColor(getBackground());
g2d.fill(area);
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.fill(hole);
g2d.dispose();
g2d = (Graphics2D) g.create();
// Basically, we create an area that is subtraction of the window/rectangle
// from the whole. This leaves us with a rectangle (with a hole in it)
// that doesn't include the area where the whole is...
Rectangle win = new Rectangle(
x + (radius / 2),
y + (radius / 2), radius, (radius / 4));
area = new Area(win);
area.subtract(new Area(hole));
// Then we create a area that is a subtraction of the original rectangle
// from the one with a "hole" in it...
Area actual = new Area(win);
actual.subtract(area);
g2d.setColor(Color.BLUE);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(actual);
g2d.dispose();
}
}
}

Categories

Resources