Drawing Circles to JFrame - java

I'm having issues drawing some circles to my JFrame. I originally had it using the default layout and realized this was only adding the most recent circle, so I changed the layout to null, and now nothing gets drawn. I've also tried frame.setLayout(new FlowLayout()) which also doesn't draw anything. Any help would be appreciated!
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* #author Christopher Nielson
*
*/
public class Main {
private static JFrame frame;
private static Random rand;
private static Jiggler jiggler;
private static ArrayList<JComponent> circles;
private static int fps;
public static void main(String[] args) {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setBounds(100, 100, 450, 450);
rand = new Random();
circles = new ArrayList<JComponent>();
int x = frame.getWidth();
int y = frame.getHeight();
for (int i = 0; i < Integer.parseInt(args[0]); i++) {
circles.add(new Circle(rand.nextInt(frame.getWidth()), rand.nextInt(frame.getHeight()),
rand.nextInt(frame.getWidth() / 10) + 100, rand.nextInt(frame.getHeight() / 10) + 100, null));
}
circles.forEach(current -> {
frame.add(current);
});
frame.setVisible(true);
jiggler = new Jiggler(circles, new JLabel("FPS: ")); // TODO add fps
jiggler.run();
}
}

And this is one reason you'll see us recommending time and time again to avoid using null layouts like the plague.
Having said that, your main problem is a design problem, not a layout problem, and that problem being that your Circle class shouldn't extend JComponent or any component for that matter, since if you want to draw multiple circles, you should have only one component, probably a JPanel doing the drawing, and the Circles should be logical classes, classes that have a public void draw(Graphics g) method, not component classes. You would pass the List of Circles to your drawing JPanel, and it would draw the Circles in its paintComponent method by calling the draw(g) methods of each Circle in the list.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawChit extends JPanel {
private static final int PREF_W = 900;
private static final int PREF_H = 700;
private static final int MAX_SHAPES = 30;
private List<MyShape> shapes = new ArrayList<>();
public DrawChit() {
setBackground(Color.WHITE);
for (int i = 0; i < MAX_SHAPES; i++) {
double x = (PREF_W - 100) * Math.random();
double y = (PREF_H - 100) * Math.random();
double w = 100 + (Math.random() * PREF_W) / 10;
double h = 100 + (Math.random() * PREF_H) / 10;
Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
float hue = (float) Math.random();
double delta = 0.3;
float saturation = (float) (Math.random() * delta + (1 - delta));
float brightness = (float) (Math.random() * delta + (1 - delta));
Color color = Color.getHSBColor(hue, saturation, brightness);
shapes.add(new MyShape(ellipse, color));
}
// we'll throw a black square in the middle!
int rectW = 200;
int rectX = (PREF_W - rectW) / 2;
int rectY = (PREF_H - rectW) / 2;
shapes.add(new MyShape(new Rectangle(rectX, rectY, rectW, rectW), Color.BLACK));
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use anti-aliasing to make graphics smooth
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the shapes list, filling all
for (MyShape shape : shapes) {
shape.fill(g2);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
private Point p0 = null;
private MyShape shape = null;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// iterate *backwards* so get top-most Shape
for (int i = shapes.size() - 1; i >= 0; i--) {
if (shapes.get(i).contains(e.getPoint())) {
p0 = e.getPoint();
shape = shapes.get(i);
// move selected shape to the top!
shapes.remove(shape);
shapes.add(shape);
repaint();
return;
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p0 != null) {
moveShape(e.getPoint());
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p0 != null) {
moveShape(e.getPoint());
p0 = null;
shape = null;
}
}
// translates the shape
private void moveShape(Point p1) {
int deltaX = p1.x - p0.x;
int deltaY = p1.y - p0.y;
shape.translate(deltaX, deltaY);
p0 = p1;
repaint();
}
}
private static void createAndShowGui() {
DrawChit mainPanel = new DrawChit();
JFrame frame = new JFrame("Draw Chit");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MyShape {
private Path2D path = new Path2D.Double();
private Color color;
public MyShape(Shape shape, Color color) {
path.append(shape, true);
this.color = color;
}
public boolean contains(Point p) {
return path.contains(p);
}
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.draw(path);
}
public void fill(Graphics2D g2) {
g2.setColor(color);
g2.fill(path);
}
public void translate(int deltaX, int deltaY) {
path.transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
}
}

Related

Drawing on a JPanel from multiple places

I'm currently working on a 2D game in Java for school. We have to use an Abstract Factory design pattern. For the 2D implementation I use a factory as follows:
public class Java2DFact extends AbstractFactory {
public Display display;
private Graphics g;
public Java2DFact() {
display = new Display(2000, 1200);
}
#Override
public PlayerShip getPlayership()
{
return new Java2DPlayership(display.panel);
}
In my display class I create a JFrame and Jpanel
public class Display {
public JFrame frame;
public JPanel panel;
public int width, height;
public Display(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame();
frame.setTitle("SpaceInvaders");
frame.setSize(1200,800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel = new JPanel(){
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
}
};
panel.setFocusable(true);
frame.add(panel);
}
}
Now from my main gameloop I call the visualize method inside the Java2DPLayership class to visualize my Playership
public class Java2DPlayership extends PlayerShip {
private JPanel panel;
private Graphics2D g2d;
private Image image;
private BufferStrategy bs;
public Java2DPlayership(JPanel panel) {
super();
this.panel = panel;
}
public void visualize() {
try {
image = ImageIO.read(new File("src/Bee.gif"));
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
//g.setColor(new Color(0, 0, 0));
//g.fillRect(10, 10, 12, 8);
g.drawImage(image, (int) super.getMovementComponent().x, (int) super.getMovementComponent().y, null);
Toolkit.getDefaultToolkit().sync();
g.dispose();
panel.repaint();
} catch(Exception e){
System.out.println(e.toString());
}
}
}
My goal is to pass around the JPanel to every entity and let it draw its contents onto the panel before showing it. However I can't seem to figure out how to do this. When using this approach by changing the Graphics of the panel I get a lot of flickering.
Here is a fully functional, albeit simple example, I wrote some time ago. It just has a bunch of balls bouncing off the sides of the panel. Notice that the render method of the Ball class accepts the graphics context from paintComponent. If I had more classes that needed to be rendered, I could have created a Renderable interface and have each class implement it. Then I could have a list of Renderable objects and just go thru them and call the method. But as I also said, that would need to happen quickly to avoid tying up the EDT.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Bounce extends JPanel {
private static final int COLOR_BOUND = 256;
private final static double INC = 1;
private final static int DIAMETER = 40;
private final static int NBALLS = 20;
private final static int DELAY = 5;
private final static int PANEL_WIDTH = 800;
private final static int PANEL_HEIGHT = 600;
private final static int LEFT_EDGE = 0;
private final static int TOP_EDGE = 0;
private JFrame frame;
private double rightEdge;
private double bottomEdge;
private List<Ball> balls = new ArrayList<>();
private Random rand = new Random();
private List<Long> times = new ArrayList<>();
private int width;
private int height;
public Bounce(int width, int height) {
this.width = width;
this.height = height;
frame = new JFrame("Bounce");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
addComponentListener(new MyComponentListener());
frame.pack();
frame.setLocationRelativeTo(null);
rightEdge = width - DIAMETER;
bottomEdge = height - DIAMETER;
for (int j = 0; j < NBALLS; j++) {
int r = rand.nextInt(COLOR_BOUND);
int g = rand.nextInt(COLOR_BOUND);
int b = rand.nextInt(COLOR_BOUND);
Ball bb = new Ball(new Color(r, g, b), DIAMETER);
balls.add(bb);
}
frame.setVisible(true);
}
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
public static void main(String[] args) {
new Bounce(PANEL_WIDTH, PANEL_HEIGHT).start();
}
public void start() {
/**
* Note: Using sleep gives a better response time than
* either the Swing timer or the utility timer. For a DELAY
* of 5 msecs between updates, the sleep "wakes up" every 5
* to 6 msecs while the other two options are about every
* 15 to 16 msecs. Not certain why this is happening though
* since the other timers are run on threads.
*
*/
Timer timer = new Timer(0,(ae)-> {repaint();
for (Ball b : balls) {
b.updateDirection();
}} );
timer.setDelay(5); // 5 ms.
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : balls) {
ball.render(g2d);
}
}
class MyComponentListener extends ComponentAdapter {
public void componentResized(ComponentEvent ce) {
Component comp = ce.getComponent();
rightEdge = comp.getWidth() - DIAMETER;
bottomEdge = comp.getHeight() - DIAMETER;
for (Ball b : balls) {
b.init();
}
}
}
class Ball {
private Color color;
public double x;
private double y;
private double yy;
private int ydir = 1;
private int xdir = 1;
private double slope;
private int diameter;
public Ball(Color color, int diameter) {
this.color = color;
this.diameter = diameter;
init();
}
public void init() {
// Local constants not uses outside of method
// Provides default slope and direction for ball
slope = Math.random() * .25 + .50;
x = (int) (rightEdge * Math.random());
yy = (int) (bottomEdge * Math.random()) + diameter;
xdir = Math.random() > .5 ? -1
: 1;
ydir = Math.random() > .5 ? -1
: 1;
y = yy;
}
public void render(Graphics2D g2d) {
g2d.setColor(color);
g2d.fillOval((int) x, (int) y, diameter, diameter);
}
public void updateDirection() {
x += (xdir * INC);
yy += (ydir * INC);
y = yy * slope;
if (x < LEFT_EDGE || x > rightEdge) {
xdir = -xdir;
}
if (y < TOP_EDGE || y > bottomEdge) {
ydir = -ydir;
}
}
}
}

How to rotate something with Graphics2D

So I want to rotate this Rectangle I made
public void paint (Graphics g)
{
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillRect(10, 10, 30, 30);
g2.rotate(Math.toRadians(45)); //I tried this but doesn't seem to work...
}
How do I do that? Rotate as in rotate in 45* angle or 200* angle.
It really isn't that hard to rotate objects. Most of the code below is simply boiler plate to create the frames and panels. Here is a demo that shows two methods that were mentioned in the comments.
the left panel simply rotates the graphics context. This is, imo, the easiest method but it does not alter the object.
the right panel uses the AffineTransform to rotate the object. This actually changes the contents of the shape.
If the desire is to rotate an object in place it is necessary to ensure one is rotating about the middle of the image that is under rotation. In both cases below that would be (125,125) or the center of both the panels and the rectangle.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.border.LineBorder;
public class RotateRectangle extends JPanel {
JFrame frame = new JFrame();
double angle = 0;
MyPanel mypanel = new MyPanel();
public static void main(String[] args) {
SwingUtilities
.invokeLater(() -> new RotateRectangle().start());
}
public void start() {
Timer t = new Timer(0, (ae) -> {mypanel.rotateit(); frame.repaint();});
t.setDelay(30);
t.start();
}
public RotateRectangle() {
frame.setLayout(new FlowLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.add(mypanel);
setBorder(new LineBorder(Color.red,2));
mypanel.setBorder(new LineBorder(Color.red, 2));
frame.pack();
// center on screen
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// visually smooth the lines
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.BLACK);
g2d.rotate(angle, 125,125);
g2d.drawRect(75, 75, 100, 100);
// adjust the amount of rotation per timer interval
angle += Math.PI / 200;
g2d.dispose();
}
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
class MyPanel extends JPanel {
Polygon polygon = new Polygon();
// amount to rotate
double angle = Math.PI / 200;
Shape shape = polygon;
AffineTransform af = new AffineTransform();
public MyPanel() {
af.rotate(angle, 125,125);
polygon.addPoint(75,175);
polygon.addPoint(175,175);
polygon.addPoint(175,75);
polygon.addPoint(75,75);
}
public void rotateit() {
shape = af.createTransformedShape(shape);
}
public void paintComponent(Graphics g) {
if (shape == null) {
return;
}
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// visually smooth the lines
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.BLACK);
g2d.draw(shape);
}
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
Following up on my comment, I created the following GUI.
I used math to calculate the four endpoints of a rotated rectangle and used the Graphics2D fillPolygon method to draw the rectangle.
The buttons at the bottom of the GUI allow you to rotate the rectangle on the center point or the upper left endpoint.
I created a drawing JPanel to draw the rectangle. All the paintComponent method of the drawing JPanel does is draw the Polygon returned by the application model.
The application model is a key part of this application. I create a plain Java getter / setter class. I start with a java.awt.Rectangle and use polar coordinates to rotate the rectangle. I convert the polar coordinates back to cartesian coordinates to get the four endpoints of the Polygon.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
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 RotatingRectangle implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotatingRectangle());
}
private DrawingPanel drawingPanel;
private JButton centerButton;
private JButton endPointButton;
private RotatedRectangle rotatedRectangle;
public RotatingRectangle() {
this.rotatedRectangle = new RotatedRectangle(Color.BLUE,
new Rectangle(200, 200, 100, 50));
}
#Override
public void run() {
JFrame frame = new JFrame("Rotating Rectangle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.drawingPanel = new DrawingPanel(rotatedRectangle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
ButtonListener listener = new ButtonListener(this, rotatedRectangle);
centerButton = new JButton("Rotate on center point");
centerButton.addActionListener(listener);
panel.add(centerButton);
endPointButton = new JButton("Rotate on end point");
endPointButton.addActionListener(listener);
endPointButton.setPreferredSize(centerButton.getPreferredSize());
panel.add(endPointButton);
return panel;
}
public void repaint() {
drawingPanel.repaint();
}
public JButton getCenterButton() {
return centerButton;
}
public JButton getEndPointButton() {
return endPointButton;
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private RotatedRectangle rotatedRectangle;
public DrawingPanel(RotatedRectangle rotatedRectangle) {
this.rotatedRectangle = rotatedRectangle;
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(400, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Polygon polygon = rotatedRectangle.getRectangle();
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(rotatedRectangle.getColor());
g2d.fillPolygon(polygon);
}
}
public class ButtonListener implements ActionListener {
private final RotatingRectangle frame;
private final RotatedRectangle model;
private Timer timer;
public ButtonListener(RotatingRectangle frame, RotatedRectangle model) {
this.frame = frame;
this.model = model;
}
#Override
public void actionPerformed(ActionEvent event) {
if (timer != null) {
timer.stop();
}
JButton button = (JButton) event.getSource();
if (button.equals(frame.getEndPointButton())) {
model.setCenterPoint(false);
model.setAngle(180);
} else {
model.setCenterPoint(true);
model.setAngle(0);
}
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
model.incrementAngle(1);
frame.repaint();
}
});
timer.start();
}
}
public class RotatedRectangle {
private boolean centerPoint;
private int angle;
private final Color color;
private final Rectangle rectangle;
public RotatedRectangle(Color color, Rectangle rectangle) {
this.color = color;
this.rectangle = rectangle;
this.angle = 0;
this.centerPoint = true;
}
public int getAngle() {
return angle;
}
public void setAngle(int angle) {
this.angle = angle;
}
public void incrementAngle(int increment) {
this.angle += increment;
this.angle %= 360;
}
public Polygon getRectangle() {
Point rotatePoint = new Point(rectangle.x, rectangle.y);
if (isCenterPoint()) {
int x = rectangle.x + rectangle.width / 2;
int y = rectangle.y + rectangle.height / 2;
rotatePoint = new Point(x, y);
}
Point[] point = new Point[4];
int width = rectangle.x + rectangle.width;
int height = rectangle.y + rectangle.height;
point[0] = new Point(rectangle.x, rectangle.y);
point[1] = new Point(width, rectangle.y);
point[2] = new Point(width, height);
point[3] = new Point(rectangle.x, height);
Polygon polygon = new Polygon();
for (int i = 0; i < point.length; i++) {
point[i] = calculatePoint(rotatePoint, point[i], angle);
polygon.addPoint(point[i].x, point[i].y);
}
return polygon;
}
private Point calculatePoint(Point rotatePoint, Point point, int angle) {
double theta = Math.toRadians(angle);
int xDistance = rotatePoint.x - point.x;
int yDistance = rotatePoint.y - point.y;
double distance = Math.sqrt(xDistance * xDistance + yDistance * yDistance);
double alpha = Math.atan2(yDistance, xDistance);
theta += alpha;
int x = (int) Math.round(Math.cos(theta) * distance) + rotatePoint.x;
int y = (int) Math.round(Math.sin(theta) * distance) + rotatePoint.y;
return new Point(x, y);
}
public Color getColor() {
return color;
}
public boolean isCenterPoint() {
return centerPoint;
}
public void setCenterPoint(boolean centerPoint) {
this.centerPoint = centerPoint;
}
}
}

netbeans: animate circle drawn using drawline funtion

I am trying to draw a circle like this: http://35.197.37.158/Circle/
using drawline function and animate it same as the attached link.
here is what I tried but it draw a line of circle and delete the previous one
This is the class that have my code for drawing a circle and animating it using swin Timer. anyone have better idea to animate the circle ?
import java.awt.Color;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.GeneralPath;
import javax.swing.*;
public class CustomPanel extends JPanel implements ActionListener{
Point [] coordinates;
GeneralPath circle;
final int C = 10;
int j =0;
int i =j;
Point p;
Point p2 ;
Timer clock = new Timer(100, this);
public CustomPanel()
{
LinesCoordinates();
clock.setInitialDelay(50);
clock.start();
}
private void LinesCoordinates()
{
int numberOfLines = 360/C;
coordinates = new Point[numberOfLines];
double cx = 200.0;
double cy = 200.0;
double r = 75.0;
int count = 0;
for(int theta = 0; theta < 360; theta+=C)
{
int x = (int)(cx + r * Math.cos(Math.toRadians(theta)));
int y = (int)(cy + r * Math.sin(Math.toRadians(theta)));
coordinates[count++] = new Point(x, y);
}
}
#Override
public void actionPerformed(ActionEvent e) {
Redraw();
repaint();
}
public void Redraw(){
j=j+1;
p = p2;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(Color.red);
// while (j<=coordinates.length){
while(i<=j){
j--;
p2 = coordinates[j % coordinates.length];
g2.drawLine(p.x, p.y, p2.x , p2.y);
}}}
And this is my Main
public static void main(String[] args)
{
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
JFrame frame = new JFrame("Circle ");
CustomPanel co = new CustomPanel();
frame.add(co);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
}
Since I was not able to get your solution draw the circle I had to rewrite your code a bit. Here is my solution preserving your JPanel listening to Timer approach. Hopefully this will work for you. I can send the complete NetBeans project if you want.
package circleanimation;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CustomPanel extends JPanel implements ActionListener {
final int numberOfPoints = 20;
Point[] circleCoordinates = new Point[numberOfPoints];
int nextPointToAnimate = 1;
Timer clock = new Timer(100, this);
public CustomPanel() {
calculateCirclePoints();
clock.setInitialDelay(50);
clock.start();
}
private void calculateCirclePoints() {
int angle = 360 / numberOfPoints;
double cx = 150.0;
double cy = 150.0;
double r = 75.0;
int count = 0;
for (int totalAngle = 0; totalAngle < 360; totalAngle = totalAngle + angle) {
int x = (int) (cx + r * Math.cos(Math.toRadians(totalAngle)));
int y = (int) (cy + r * Math.sin(Math.toRadians(totalAngle)));
circleCoordinates[count++] = new Point(x, y);
}
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.red);
for (int i = 0; i < nextPointToAnimate; i++) {
Point firstPoint = circleCoordinates[i];
Point secondPoint;
if (i == numberOfPoints - 1) {
secondPoint = circleCoordinates[0];
} else {
secondPoint = circleCoordinates[i + 1];
}
g.drawLine(firstPoint.x, firstPoint.y, secondPoint.x, secondPoint.y);
}
}
#Override
public void actionPerformed(ActionEvent e) {
nextPointToAnimate++;
if (nextPointToAnimate == numberOfPoints) {
clock.stop();
}
repaint();
}
}

Generating specific shapes with random dimensions using a JComboBox to select shapes

I am trying to create a program that uses a JComboBox containing specific shapes (Circle, Square, Oval, Rectangle). After the user clicks on a specified shape, the Panel will display 20 of that shape in random dimensions and locations.
I am having trouble on how to make the shapes have random dimensions and locations. Here is my code so far. Any advice or sources to look at would be appreciated.
Thank you.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import java.awt.event.*;
public class HW1b extends JFrame
{
public HW1b()
{
super("Shapes");
final ComboPanel comboPanel = new ComboPanel();
String[] shapeItems = {"Circle", "Square", "Oval", "Rectangle"};
JComboBox shapeBox = new JComboBox<String>(shapeItems);
shapeBox.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent ie)
{
if (ie.getStateChange() == ItemEvent.SELECTED)
{
String item = (String)ie.getItem();
if(shapeBox.getSelectedItem().equals("Circle"))
comboPanel.makeCircles();
if(shapeBox.getSelectedItem().equals("Square"))
comboPanel.makeSquares();
if(shapeBox.getSelectedItem().equals("Oval"))
comboPanel.makeOvals();
if(shapeBox.getSelectedItem().equals("Rectangle"))
comboPanel.makeRectangles();
}
}
});
JPanel southPanel = new JPanel();
southPanel.add(shapeBox);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().add(comboPanel, "Center");
getContentPane().add(southPanel, "South");
setSize( 400, 400 );
setLocation( 200, 200 );
setVisible( true );
}
private class ComboPanel extends JPanel
{
int w, h;
Random rand;
static final int OVAL = 0;
static final int RECTANGLE = 1;
int shapeType = -1;
public ComboPanel()
{
rand = new Random();
setBackground(Color.white);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int width = getWidth();
int height = getHeight();
int x, y;
Shape s = null;
for (int i = 0; i < 20; i++)
{
x = rand.nextInt(width - w);
y = rand.nextInt(width - h);
switch(shapeType)
{
case OVAL: s = new Ellipse2D.Double(x,y,w,h);
break;
case RECTANGLE: s = new Rectangle2D.Double(x,y,w,h);
break;
}
if (shapeType > -1)
g2d.draw(s);
}
}
public void makeCircles()
{
shapeType = OVAL;
w = 75;
h = 75;
repaint();
}
public void makeSquares()
{
shapeType = RECTANGLE;
w = 50;
h = 50;
repaint();
}
public void makeOvals()
{
shapeType = OVAL;
w = 80;
h = 60;
repaint();
}
public void makeRectangles()
{
shapeType = RECTANGLE;
w = 80;
h = 40;
repaint();
}
}
public static void main(String[] args)
{
new HW1b();
}
}
You're hard-coding w and h in your code, and so there's no way for this to vary among your shapes. Instead of doing this, use your Random variable, rand, to select random w and h values that are within some desired range. Myself, I wouldn't create my shapes within the paintComponent method since painting is not fully under my control and can occur when I don't want it to. For instance, in your code, your shapes will vary tremendously if the GUI is resized. Instead I'd create a collection such as an ArrayList<Shape> and fill it with created Shape objects (i.e., Ellipse2D for my circles) when desired, and then iterate through that collection within your paintComponent method, drawing your shapes.
for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
public class SomeShapes extends JPanel {
private ShapePanel shapePanel = new ShapePanel();
private JComboBox<MyShape> myShapeCombo = new JComboBox<>(MyShape.values());
public SomeShapes() {
myShapeCombo.setSelectedIndex(-1);
myShapeCombo.addItemListener(new ComboListener());
JPanel bottomPanel = new JPanel();
bottomPanel.add(myShapeCombo);
setLayout(new BorderLayout());
add(shapePanel, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.PAGE_END);
}
private class ComboListener implements ItemListener {
#Override
public void itemStateChanged(ItemEvent e) {
MyShape myShape = (MyShape) e.getItem();
shapePanel.drawShapes(myShape);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("SomeShapes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SomeShapes());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum MyShape {
OVAL("Oval"), RECTANGLE("Rectangle"), SQUARE("Square"), CIRCLE("Circle");
private String name;
private MyShape(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return getName();
}
}
class ShapePanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color SHAPE_COLOR = Color.BLUE;
private static final int SHAPE_COUNT = 20;
private static int MIN = 5;
private static int MAX = 200;
private List<Shape> shapeList = new ArrayList<>();
private Random random = new Random();
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public void drawShapes(MyShape myShape) {
shapeList.clear(); // empty the shapeList
switch (myShape) {
case OVAL:
drawOval();
break;
case RECTANGLE:
drawRectangle();
break;
// etc...
default:
break;
}
repaint();
}
private void drawOval() {
// for loop to do this times SHAPE_COUNT(20) times.
for (int i = 0; i < SHAPE_COUNT; i++) {
// first create random width and height
int w = random.nextInt(MAX - MIN) + MIN;
int h = random.nextInt(MAX - MIN) + MIN;
// then random location, but taking care so that it
// fully fits into our JPanel
int x = random.nextInt(getWidth() - w);
int y = random.nextInt(getHeight() - h);
// then create new Shape and place in our shapeList.
shapeList.add(new Ellipse2D.Double(x, y, w, h));
}
}
private void drawRectangle() {
// .... etc
}
//.. .. etc
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
// set rendering hints for smooth ovals
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(SHAPE_COLOR);
// iterate through the shapeList ArrayList
for (Shape shape : shapeList) {
g2d.draw(shape); // and draw each Shape it holds
}
}
}

Java on screen virtual joystick control

I need a on-screen virtual joystick solution to control external device - a cart. My program is written on Java/Swing and it works on windows rugged tablet with resistive touch screen. There's no x-y slider control in Swing, failed to find it thru Google, so probably I need to write it from scratch. Now the question: what is best approach to write such control? I was thinking to place two JSlider controls, one horizontal and second vertical, and make custom thumb, but i'm afraid this can bring me troubles because it's actually a hack. Maybe just write it from scratch? Maybe there's an existing solution?
I will use it with thumb dragging and touching final thumb position.
for example
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SimpleJoystickDemo extends JFrame {
//I think that orginally made by #HFOE
private int displayWidth = 340;
private int displayHeight = 550;
private final Point position;
public SimpleJoystickDemo() {
super("SimpleJoystickDemo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(displayWidth, displayHeight);
setLocationRelativeTo(null);
position = new Point();
SimpleJoystick myJoystick = new SimpleJoystick(150, position, 100);
add(myJoystick, BorderLayout.PAGE_END);
Drawing drawing = new Drawing(position);
add(drawing);
}
public static void main(final String[] args) {
Runnable gui = new Runnable() {
#Override
public void run() {
new SimpleJoystickDemo().setVisible(true);
}
};
SwingUtilities.invokeLater(gui);
}
private class Drawing extends JPanel {
private static final long serialVersionUID = 1L;
private final Point position;
public Drawing(Point position) {
this.position = position;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.RED);
g2.fillOval(160 + position.x, 160 - position.y, 15, 15);
}
}
}
class SimpleJoystick extends JPanel {
private static final long serialVersionUID = 1L;
//Maximum value for full horiz or vert position where centered is 0:
private int joyOutputRange;
private float joySize; //joystick icon size
private float joyWidth, joyHeight;
private float joyCenterX, joyCenterY; //Joystick displayed Center
//Display positions for text feedback values:
private int textHorizPos, textVertPos;
private int fontSpace = 12;
private float curJoyAngle; //Current joystick angle
private float curJoySize; //Current joystick size
private boolean isMouseTracking;
private boolean leftMouseButton;
private int mouseX, mouseY;
private Stroke lineStroke = new BasicStroke(10, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
private final Point position;
public SimpleJoystick(final int joyOutputRange,
final Point position, final int joySize) {
this.joyOutputRange = joyOutputRange;
this.position = position;
this.joySize = joySize;
joyWidth = joySize;
joyHeight = joyWidth;
setPreferredSize(new Dimension((int) joyWidth + 250,
(int) joyHeight + 80));
joyCenterX = getPreferredSize().width / 2;
joyCenterY = getPreferredSize().height / 2;
this.joySize = joyWidth / 2;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseMoved(final MouseEvent e) {
mouseCheck(e);
}
#Override
public void mousePressed(final MouseEvent e) {
leftMouseButton = SwingUtilities.isLeftMouseButton(e);
mouseCheck(e);
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private void mouseCheck(final MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
float dx = mouseX - joyCenterX;
float dy = mouseY - joyCenterY;
if (leftMouseButton) {
isMouseTracking = true;
} else {
isMouseTracking = false;
}
if (isMouseTracking) {
curJoyAngle = (float) Math.atan2(dy, dx);
curJoySize = (float) Point.distance(mouseX, mouseY,
joyCenterX, joyCenterY);
} else {
curJoySize = 0;
}
if (curJoySize > joySize) {
curJoySize = joySize;
}
position.x = (int) (joyOutputRange * (Math.cos(curJoyAngle)
* curJoySize) / joySize);
position.y = (int) (joyOutputRange * (-(Math.sin(curJoyAngle)
* curJoySize) / joySize));
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.LIGHT_GRAY);
g2.fillOval((int) (joyCenterX - joyWidth / 2),
(int) (joyCenterY - joyHeight / 2),
(int) joyWidth, (int) joyHeight);
//rotate and draw joystick line segment:
Graphics2D g3 = (Graphics2D) g2.create();
g3.translate(joyCenterX, joyCenterY);
g3.rotate(curJoyAngle);
g3.setColor(Color.GRAY);
g3.setStroke(lineStroke);
g3.drawLine(0, 0, (int) curJoySize, 0);
g3.dispose();
//
g2.setColor(Color.GRAY);
g2.fillOval((int) joyCenterX - 10, (int) joyCenterY - 10, 20, 20);
textHorizPos = 50;
textVertPos = (int) (joyCenterY - 50);
g2.drawString("Horizont:", textHorizPos, textVertPos);
textHorizPos += (4 * fontSpace);
g2.drawString(String.valueOf(position.x), textHorizPos, textVertPos);
textHorizPos = 50;
textVertPos += 12;
g2.drawString("Vertical :", textHorizPos, textVertPos);
textHorizPos += (4 * fontSpace);
g2.drawString(String.valueOf(position.y), textHorizPos, textVertPos);
}
}
Reworked code from #tutejszy. Warnings removed and the missing PointChangeEvent added.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class VJoystick extends JPanel implements ChangeListener
{
private JLabel lblPosition;
private static final long serialVersionUID = 1L;
public VJoystick()
{
setLayout(new BorderLayout(0, 0));
SimpleJoystick myJoystick = new SimpleJoystick(150);
myJoystick.setPreferredSize(new Dimension(100,100));
myJoystick.addChangeListener(this);
add(myJoystick, BorderLayout.CENTER);
lblPosition = new JLabel("position");
add(lblPosition, BorderLayout.SOUTH);
}
#Override
public void stateChanged(ChangeEvent ev) {
Point p = null;
try {
p = ((PointChangeEvent)ev).p;
} catch (Exception e) {
return;
}
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
class SimpleJoystick extends JPanel
{
private static final long serialVersionUID = 1L;
/** Maximum value for full horiz or vert position where centered is 0 */
private int joyOutputRange;
/** max x and y value, in pixels */
private int joyRadius;
/** Joystick displayed Center, in pixels */
private int joyCenterX, joyCenterY;
/** joystick output position scaled to given joyOutputRange */
private Point position = new Point();
/** joystick x axis value in pixels */
private int dx = 0;
/** joystick y axis value in pixels */
private int dy = 0;
/**
* #param joyOutputRange
*/
public SimpleJoystick(final int joyOutputRange) {
this.joyOutputRange = joyOutputRange;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private boolean cursorChanged(int mouseX, int mouseY) {
if (joyRadius == 0) return false;
dx = mouseX - joyCenterX;
dy = mouseY - joyCenterY;
if (dx > joyRadius) dx = joyRadius;
if (dy > joyRadius) dy = joyRadius;
if (dx < -joyRadius) dx = -joyRadius;
if (dy < -joyRadius) dy = -joyRadius;
position.x = joyOutputRange * dx / joyRadius;
position.y = -joyOutputRange * dy / joyRadius;
return true;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
int joyWidth = getSize().width;
int joyHeight = getSize().height;
joyRadius = Math.min(joyWidth, joyHeight) / 2;
if (joyRadius == 0) return;
joyCenterX = joyWidth / 2;
joyCenterY = joyHeight / 2;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter;
//background
g2.setColor(Color.LIGHT_GRAY);
diameter = joyRadius*2;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
g2.setColor(Color.RED);
diameter = 40;
g2.fillOval(joyCenterX + dx - diameter/2 , joyCenterY + dy - diameter/2, diameter, diameter);
//thumb
g2.setColor(Color.GRAY);
diameter = 20;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
}
void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
protected void fireStateChanged() {
ChangeEvent e = new PointChangeEvent(this, position);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(e);
}
}
}
}
class PointChangeEvent extends ChangeEvent
{
private static final long serialVersionUID = 1L;
public Point p;
public PointChangeEvent(Object source, Point p)
{
super(source);
this.p=p;
}
}
This is reworked code from #mKorbel, it's event based and more swing-oriented, without floats:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class JoyDemo extends JPanel implements ChangeListener {
private JLabel lblPosition;
public JoyDemo() {
setLayout(new BorderLayout(0, 0));
SimpleJoystick myJoystick = new SimpleJoystick(150);
myJoystick.setPreferredSize(new Dimension(100,100));
myJoystick.addChangeListener(this);
add(myJoystick, BorderLayout.CENTER);
lblPosition = new JLabel("position");
add(lblPosition, BorderLayout.SOUTH);
}
#Override
public void stateChanged(ChangeEvent ev) {
Point p = null;
try {
p = ((PointChangeEvent)ev).p;
} catch (Exception e) {
return;
}
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
class SimpleJoystick extends JPanel {
/** Maximum value for full horiz or vert position where centered is 0 */
private int joyOutputRange;
/** max x and y value, in pixels */
private int joyRadius;
/** Joystick displayed Center, in pixels */
private int joyCenterX, joyCenterY;
/** joystick output position scaled to given joyOutputRange */
private Point position = new Point();
/** joystick x axis value in pixels */
private int dx = 0;
/** joystick y axis value in pixels */
private int dy = 0;
/**
* #param joyOutputRange
*/
public SimpleJoystick(final int joyOutputRange) {
this.joyOutputRange = joyOutputRange;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private boolean cursorChanged(int mouseX, int mouseY) {
if (joyRadius == 0) return false;
dx = mouseX - joyCenterX;
dy = mouseY - joyCenterY;
if (dx > joyRadius) dx = joyRadius;
if (dy > joyRadius) dy = joyRadius;
if (dx < -joyRadius) dx = -joyRadius;
if (dy < -joyRadius) dy = -joyRadius;
position.x = joyOutputRange * dx / joyRadius;
position.y = -joyOutputRange * dy / joyRadius;
return true;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
int joyWidth = getSize().width;
int joyHeight = getSize().height;
joyRadius = Math.min(joyWidth, joyHeight) / 2;
if (joyRadius == 0) return;
joyCenterX = joyWidth / 2;
joyCenterY = joyHeight / 2;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter;
//background
g2.setColor(Color.LIGHT_GRAY);
diameter = joyRadius*2;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
g2.setColor(Color.RED);
diameter = 40;
g2.fillOval(joyCenterX + dx - diameter/2 , joyCenterY + dy - diameter/2, diameter, diameter);
//thumb
g2.setColor(Color.GRAY);
diameter = 20;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
}
void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
protected void fireStateChanged() {
ChangeEvent e = new PointChangeEvent(this, position);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(e);
}
}
}
}
Here's my solution. Note, I reworked #Dark.Rider's answer, who reworked #tutejszy's answer, who reworked #mKorbel's answer...
I made 2 files. One called Joystick.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.SwingUtilities;
public class Joystick extends javax.swing.JPanel {
private final int outputMax;
private final int thumbDiameter;
private final int thumbRadius;
private final int panelWidth;
private final int arrowRadius;
private final int BORDER_THICKNESS = 2;
private final Point thumbPos = new Point();
protected SwingPropertyChangeSupport propertySupporter = new SwingPropertyChangeSupport(this);
/**
* #param output_max The maximum value to scale output to. If this value was
* 5 and the joystick thumb was dragged to the top-left corner, the output
* would be (-5,5)
* #param panel_width how big the JPanel will be. The sizes of the joystick's
* visual components are proportional to this value
*/
public Joystick(int output_max, int panel_width) {
assert output_max > 0;
assert panel_width > 0;
outputMax = output_max;
panelWidth = panel_width;
thumbDiameter = panel_width/4;
thumbRadius = thumbDiameter/2;
arrowRadius = panel_width/24;
MouseAdapter mouseAdapter = new MouseAdapter() {
private void repaintAndTriggerListeners(){
SwingUtilities.getRoot(Joystick.this).repaint();
propertySupporter.firePropertyChange(null, null, getOutputPos());
}
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateThumbPos(e.getX(), e.getY());
repaintAndTriggerListeners();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateThumbPos(e.getX(), e.getY());
repaintAndTriggerListeners();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
centerThumbPad();
repaintAndTriggerListeners();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
setPreferredSize(new java.awt.Dimension(panel_width, panel_width));
setOpaque(false);
centerThumbPad();
}
private void centerThumbPad(){
thumbPos.x = panelWidth/2;
thumbPos.y = panelWidth/2;
}
/**
* update both thumbPos
* #param mouseX the x position of cursor that has clicked in the joystick panel
* #param mouseY the y position of cursor that has clicked in the joystick panel
* #return
*/
private void updateThumbPos(int mouseX, int mouseY) {
// if the cursor is clicked out of bounds, we'll modify the position
// to be the closest point where we can draw the thumb pad completely
if (mouseX < thumbRadius)
mouseX = thumbRadius;
else if(mouseX > panelWidth - thumbRadius)
mouseX = panelWidth - thumbRadius;
if (mouseY < thumbRadius)
mouseY = thumbRadius;
else if(mouseY > panelWidth - thumbRadius)
mouseY = panelWidth - thumbRadius;
thumbPos.x = mouseX;
thumbPos.y = mouseY;
}
/**
* #return the scaled position of the joystick thumb pad
*/
Point getOutputPos(){
Point result = new Point();
result.x = outputMax * (thumbPos.x - panelWidth/2) / (panelWidth/2-thumbDiameter/2);
result.y = -outputMax * (thumbPos.y - panelWidth/2) / (panelWidth/2-thumbDiameter/2);
return result;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
//joystick background border
g.setColor(Color.BLACK);
g.fillOval(thumbRadius, thumbRadius, panelWidth-thumbDiameter, panelWidth-thumbDiameter);
//joystick background color
g.setColor(Color.GRAY);
g.fillOval(thumbRadius+BORDER_THICKNESS, thumbRadius+BORDER_THICKNESS, panelWidth-thumbDiameter-BORDER_THICKNESS*2, panelWidth-thumbDiameter-BORDER_THICKNESS*2);
//joystick background arrows
g.setColor(Color.BLACK);
int[] left_x = {thumbDiameter-arrowRadius,thumbDiameter+arrowRadius,thumbDiameter+arrowRadius};
int[] left_y = {panelWidth/2,panelWidth/2+arrowRadius,panelWidth/2-arrowRadius};
g.fillPolygon(left_x, left_y,3);
int[] right_x = {panelWidth-thumbDiameter+arrowRadius,panelWidth-thumbDiameter-arrowRadius,panelWidth-thumbDiameter-arrowRadius};
int[] right_y = {panelWidth/2,panelWidth/2+arrowRadius,panelWidth/2-arrowRadius};
g.fillPolygon(right_x, right_y,3);
int[] up_x = left_y;
int[] up_y = left_x;
g.fillPolygon(up_x, up_y,3);
int[] down_x = right_y;
int[] down_y = right_x;
g.fillPolygon(down_x, down_y,3);
//thumb pad border
g.setColor(Color.BLACK);
g.fillOval(thumbPos.x - thumbRadius - BORDER_THICKNESS, thumbPos.y - thumbRadius - BORDER_THICKNESS, thumbRadius*2+BORDER_THICKNESS*2, thumbRadius*2+BORDER_THICKNESS*2);
//thumb pad color
g.setColor(Color.GRAY);
g.fillOval(thumbPos.x - thumbRadius, thumbPos.y - thumbRadius, thumbRadius*2, thumbRadius*2);
}
#Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupporter.addPropertyChangeListener(listener);
}
#Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertySupporter.removePropertyChangeListener(listener);
}
}
and one called JoystickOutputContainer.java (used to make example pictures)
import java.awt.BorderLayout;
import java.awt.Point;
import java.beans.PropertyChangeListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class JoystickOutputContainer extends JPanel {
private Joystick myJoystick = new Joystick(100, 140);
private JLabel lblPosition = new JLabel("x=0 y=0");
public JoystickOutputContainer() {
setLayout(new BorderLayout(0, 0));
add(myJoystick, BorderLayout.CENTER);
add(lblPosition, BorderLayout.SOUTH);
myJoystick.addPropertyChangeListener(updateConsoleListener);
}
private final PropertyChangeListener updateConsoleListener = (evt) -> {
updateConsoleCallback( (Point) evt.getNewValue() );
};
private void updateConsoleCallback(Point p){
lblPosition.setText("x="+p.x+" y="+p.y);
}
}

Categories