I'm having issues getting a rectangle drawn when the mouse is clicked on screen in my JFrame. I've tried a few different methods and the closest I've gotten is just getting the coordinates appearing. Any drawing seems to be ignored for some reason.
package pathfinder;
import java.awt.BorderLayout;
import java.awt.Canvas;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
public class forLoopDesign extends JPanel{
//offsets for hard-coded path
int hOffset = 40;
int vOffset = 40;
//check if adjacent block is wall
boolean wall = false;
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
//draw start and end points
g.setColor(Color.magenta);
g.fillRect(0, 0, 40, 40);
g.setColor(Color.white);
g.drawString("Start", 7, 24);
g.setColor(Color.red);
g.fillRect(720, 720, 40, 40);
g.setColor(Color.white);
g.drawString("Finish", 724, 744);
//draw grid
g.setColor(Color.black);
for(int i=0; i<760; i+=40){
for(int j=0; j<800; j+= 40){
g.drawRect(i, j, 40, 40);
}
}
//draw hard-coded path
g.setColor(Color.cyan);
for(int i=0; i< 17; i++){
g.fillRect(hOffset + 1, vOffset + 1, 39, 39);
hOffset += 40;
vOffset += 40;
}
//check for mouse click, print coordinates
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.println("(x, y) of mouse click= (" + x + ", " + y + ")");
g.setColor(Color.orange);
g.fillRect(200,200,40,40);
}
});
}
//draw wall at mouse click location
public void highlightSquare(int x, int y, Graphics g){
wall = true;
System.out.println("wall value=" + wall);
g.setColor(Color.black);
g.fillRect(x, y, 40, 40);
}
//initialize jframe properties
public static void main(String[] args) {
JFrame f = new JFrame();
f.getContentPane().add(new forLoopDesign(), BorderLayout.CENTER);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(767, 790);
f.setVisible(true);
f.setResizable(false);
f.setLocationRelativeTo(null);
}}
My end goal is to be able to draw rectangles on the screen to create a maze like screen and have the program navigate its way through it (cyan squares). This is an ambitious project and I realize I've probably made many mistakes so far so feel free to add any other criticisms you might have. Thanks in advance
All custom painting needs to be done in the paintComponent(..) method.
So in your mouseClick logic you need to save the point where you clicked in an ArrayList and invoke repaint(). Then the paintComponent(...) method needs to iterate through the ArrayList to paint the rectangle at the given point.
See the DrawOnComponent example from Custom Painting Approaches for a working example of this approach.
Related
I have used several java.awt.Rectangle, java.awt.Polygon, and java.awt.geom.Ellipse2D shapes together and I want to rotate them with eachother and I also want to have them keep their location on the JFrame. When I use g2D.rotate(Math.toRadians(rotation)), the shapes move on the JFrame and they are no longer close together. How can I make it so that all of the shapes keep their position relative to eachother and their position on the JFrame? Thank you in advance.
If you want to be able to rotate the shapes together and keep their position on the JFrame I would recommend that you use the form of g2D.rotate that uses x and y coordinates to rotate with. You should make a standard x and y coordinate to draw each shapes position from so that when you use these standard x and y coordinates to rotate from all of the shapes will rotate together. It would probably look something like this:
// imports
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Container;
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
import java.awt.Polygon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JComponent;
public class GraphicsRotate extends JComponent implements ActionListener {
// JFrame and Container
JFrame frame = new JFrame("Graphics Rotate");
Container container = frame.getContentPane();
public int standardX = 100;
public int standardY = 100;
public int rotation = 0;
public static void main(String[] args) {
GraphicsRotate graphicsRotate = new GraphicsRotate();
graphicsRotate.setup();
}
public void setup() {
container.setBackground(Color.BLACK);
container.add(this);
KeyListener kl = new KeyListener() {
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_LEFT) {
rotation -= 10;
repaint();
}
if (code == KeyEvent.VK_RIGHT) {
rotation += 10;
repaint();
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
};
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(kl);
frame.setFocusable(true);
frame.requestFocus();
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.GREEN);
g2.rotate(Math.toRadians(rotation), standardX, standardY);
Rectangle rect = new Rectangle(standardX + 10, standardY + 10, 5,
5);
Ellipse2D ellipse = new Ellipse2D.Double(standardX + 13, standardY
+ 5, 10, 10);
g2.fill(ellipse);
g2.fill(rect);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
This could be implemented on a much larger scale, and if you wanted the shapes to rotate around the center of the larger shape that you have made with them, then you could do the necessary calculations to figure out the center and do something like g2.rotate(Math.toRadians(rotation), standardX + middleX, standardY + middleY).
I hope that this answered your question.
It's really important to remember, transformations are compounding. So you can't simply apply a new rotation to the Graphics context for each object, instead, you need to reset the state between transformations
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Shape> shapes = new ArrayList<>(4);
private double angle = 0;
public TestPane() {
shapes.add(new Rectangle2D.Double(50, 50, 100, 100));
shapes.add(new Rectangle2D.Double(250, 50, 100, 100));
shapes.add(new Rectangle2D.Double(50, 250, 100, 100));
shapes.add(new Rectangle2D.Double(250, 250, 100, 100));
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
Rectangle bounds = shape.getBounds();
int midx = bounds.x + (bounds.width / 2);
int midy = bounds.y + (bounds.height / 2);
g2d.setTransform(AffineTransform.getRotateInstance(Math.toRadians(angle), midx, midy));
g2d.draw(shape);
g2d.dispose();
}
}
}
}
You could also transform the shape directly, for example...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Shape shape : shapes) {
Rectangle bounds = shape.getBounds();
int midx = bounds.x + (bounds.width / 2);
int midy = bounds.y + (bounds.height / 2);
Path2D.Double rotatedShape = new Path2D.Double(shape, AffineTransform.getRotateInstance(Math.toRadians(angle), midx, midy));
g2d.draw(rotatedShape);
}
g2d.dispose();
}
So, I need to make the stick-man movable by a user-input. When the user clicks on a part (Head, hands, feet and posterior) and he should move, and have no idea how to go about this..
If possible, there also needs to be a confine around the character, likely rectangular, so that there is a limit to how far each part can be pulled.
See below for my code;
// Created by Charlie Carr - (28/11/17 - /11/17)
import java.awt.*;
import java.applet.Applet;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
//Imports complete
//Suppress warning about undeclared static final serialVersionUID field in VS Code
#SuppressWarnings("serial")
public class Animator extends JPanel {
public static class AnimatorWindow extends JPanel {
public void paint(Graphics page) {
setBackground(Color.gray);
setForeground(Color.white);
super.paintComponent(page);
page.drawString("Stickmen Animation Station", 150, 15);
//draw the head
//x1, y1, x2, y2
page.drawOval(90, 60, 20, 20);
// draw the body
page.drawLine(100, 80, 100, 110);
// draw the hands
page.drawLine(100, 90, 80, 105);
page.drawLine(100, 90, 120, 105);
//draw the legs, he hasn't a leg to stand on..
page.drawLine(100, 110, 85, 135);
page.drawLine(100, 110, 115, 135);
}
}
public static void main(String[] args) {
AnimatorWindow displayPanel = new AnimatorWindow();
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(displayPanel, BorderLayout.CENTER);
//declare window size
int x = 480;
int y = 240;
JFrame window = new JFrame("GUI");
window.setContentPane(content);
window.setSize(x, y);
window.setLocation(101, 101);
window.setVisible(true);
}
}
Use MouseListenerto deal with mouse events.
Also, you should override the paintComponent() method instead of paint(), because paint() also paints the border and other stuff.
public static class AnimatorWindow extends JPanel implements MouseListener{
public AnimatorWindow(){
setBackground(Color.gray);
setForeground(Color.white);
//add the listener
addMouseListener(this);
}
public void paintComponent(Graphics page) {
super.paintComponent(page);
//You should not alter the Graphics object passed in
Graphics2D g = (Graphics2D) page.create();
//draw your stuff with g
g.drawString("Stickmen Animation Station", 150, 15);
.......
//finish
g.dispose();
}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseClicked(MouseEvent e){
//implement your clicking here
//Use e.getX() and e.getY() to get the click position
}
}
For more on swing events, check this site
EDIT: Your problem also includes animation, and you can use a javax.swing.Timer to do that.
This example draws a simple PolyLine.
Is it possible to draw an outline around this PolyLine in red.
Not a single large red square but one that outlines the original PolyLine by 3-5 points away from all areas.
Some calculations were attempted and work for a fixed value, but when the PolyLine values are random, the algorithm doesn't always work as the next section of the line could turn right instead of left or up instead of down.
You almost have to look 2-3 points ahead to know if you are going to have add or subtract.
Is there an easier way to do it?
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PolyLine extends JPanel
{
public void paint(Graphics g) {
int[] xs = {25, 125, 85, 75, 25, 65, };
int[] ys = {50, 50, 100, 110, 150, 100};
BasicStroke traceStroke = new BasicStroke (1);
Graphics2D gc = (Graphics2D) g.create();
gc.setStroke(traceStroke);
gc.setColor(Color.BLUE);
gc.drawPolyline(xs, ys, 6);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new PolyLine());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(20,20, 1500,1500);
frame.setVisible(true);
}
}
First, a remark: It's usually preferable to have the geometric primitives as a Shape. The drawPolyline function (which uses these odd integer array coordinates) is somewhat out-dated. Creating the polyline as a Path2D is far more flexible.
For the task that you described, it will also be necessary to convert the polyline coordinates into a Path2D (if you switched to Path2D anyhow, you could omit this conversion step).
When you have the polyline as a Shape, the task is rather simple: You can create a stroked version of this shape, using a BasicStroke with the desired thickness and cap/join characteristics, by calling BasicStroke#createStrokedShape. This shape will basically be the shape of a "thick line". In order to avoid artifacts at the joins, you can create an Area from this Shape, and then draw this area.
So in the end, painting the actual outline is done with 2 lines of code, and the result is as follows:
But the MCVE here, for completeness:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeOutlineTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new ShapeOutlineTestPanel());
f.setSize(500, 500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ShapeOutlineTestPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
int[] xs = { 25, 125, 85, 75, 25, 65, };
int[] ys = { 50, 50, 100, 110, 150, 100 };
BasicStroke traceStroke = new BasicStroke(1);
g.setStroke(traceStroke);
g.setColor(Color.BLUE);
g.drawPolyline(xs, ys, 6);
Path2D path = new Path2D.Double();
for (int i = 0; i < xs.length; i++)
{
if (i == 0)
{
path.moveTo(xs[i], ys[i]);
}
else
{
path.lineTo(xs[i], ys[i]);
}
}
g.setColor(Color.RED);
BasicStroke stroke = new BasicStroke(
10.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g.draw(new Area(stroke.createStrokedShape(path)));
}
}
So I have a custom JComponent (An almost completed button, if you will). Here is the source code to the class:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LukeButton extends JComponent implements MouseListener{
//ArrayList of listeners
private final ArrayList<ActionListener> listeners = new ArrayList<ActionListener>();
Shape rec = new RoundRectangle2D.Float(10, 10, 110, 60, 50, 75);
BasicStroke border = new BasicStroke(5);
SpringLayout layout = new SpringLayout();
private String text;
public LukeButton(String text){
this.text = text;
this.setLayout(layout);
this.addMouseListener(this);
}
//Adds a listeners to the list
public void addActionListener(ActionListener e){
listeners.add(e);
}
//Called when button is provoked
public void fireActionListeners(){
if(!listeners.isEmpty()){
ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "LukeButton");
for(ActionListener l: listeners){
l.actionPerformed(evt);
}
}
}
//Listens for click on my component
public void mousePressed(MouseEvent e){
if(rec.contains(e.getPoint())){
rec = new RoundRectangle2D.Float(10, 10, 100, 55, 50, 75);
repaint();
fireActionListeners();
}
}
public void mouseReleased(MouseEvent e){
if(rec.contains(e.getPoint())){
rec = new RoundRectangle2D.Float(10, 10, 110, 60, 50, 75);
repaint();
}
}
//When mouse enters, make border bigger
public void mouseEntered(MouseEvent e){
border = new BasicStroke(8);
repaint();
}
//When mouse leaves, make border smaller
public void mouseExited(MouseEvent e){
border = new BasicStroke(5);
repaint();
}
public Dimension getPreferredSize(){
return new Dimension(130, 80);
}
//Draws my button
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.BLACK);
g2.setStroke(border);
g2.draw(rec);
g2.setColor(new Color(0, 204, 204));
g2.fill(rec);
g2.setColor(Color.BLACK);
g2.drawString(text, 47, 45);
}
//Methods that must be over written.
public void mouseClicked(MouseEvent e){
}
}
My problem is that I do not know how to center the text variable (More or less what the variable consists of) based on the size of the String. The beggining of the String is always in a fixed point. For example if the text variable is equal to something short, the string is going to be far on the left. But if the string is too long, it goes far off the right side of the component. Does anyone know how to center my text variable so it changes it's position based on the size of the string(or a different/better solution of coarse)? Thanks for taking your time to read :)
You can get the Rectangle required to paint the text by using:
FontMetrics fm = g2d.getFontMetrics();
Rectangle2D rect = fm.getStringBounds(text, g2d);
Then to center the text you would get the x/y positions using something like:
int x = (getSize().width - rect.width) / 2;
int y = ((getSize().height - rect.height / 2) + rect.height;
I'm working on a uni project which is to create a dice face using 2d graphics shapes. I have got that all done but I have a problem: I want my shape to change size when I adjust the window size, instead of just staying still also so that it stays in the middle.
I thought I could set the position so that is was center tried this but didn't work. I'm not sure but would I have to write co-ordinates so that its changes size with the window? Any help with both problems would be great.
GUI setup code
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class DiceSimulator {
public static void main(String[] args) {
JFrame frame = new JFrame("DiceSimulator");
frame.setVisible(true);
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
draw object = new draw();
frame.add(object);
frame.setLocationRelativeTo(null);
object.drawing();
}
}
Painting code
import javax.swing.*;
import java.awt.*;
//import java.util.Random;
public class draw extends JComponent {
public void drawing() {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Random1 random = new Random1();
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.BLACK);
Rectangle box = new Rectangle(115, 60, 150, 150);
g2.fill(box);
g.setColor(Color.WHITE);
g.fillOval(145, 75, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(205, 75, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(145, 115, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(205, 115, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(145, 155, 30, 30);
g.setColor(Color.WHITE);
g.fillOval(205, 155, 30, 30);
}
}
In the paintComponent() method you need to use
int width = getSize().width;
int height = getSize().height;
to get the current size of the component as it changes size when the frame changes size. Then based on this current size you can draw your components. This means that you can't hardcode the values in your drawing methods.
If you want to shift all the drawing coordinates with one command then you can use:
g.translate(5, 5);
at the top of the method. Then all hardcoded (x, y) values will be adjusted by 5 pixels each. This will allow you to change the centering of the drawing.