Why does the star rotation slows down automatically after some time - java

I have a program where I have to use internal Timer event to rotate a star in circular motion. There is a button in the frame that changes the direction of the star and a slider which changes the speed of the star rotation.
This is my Main Class
import javax.swing.JFrame;
public class Main {
public static void main(String args[]) {
// Making an instance of the class that makes the frame
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setVisible(true);
}
}
MainFrame Class that makes the frame.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
#SuppressWarnings("serial")
public class MainFrame extends JFrame{
private StarGraphics frameStar;
private JButton starToggle;
public JSlider starSpeed;
public MainFrame() {
// Setting some properties of the frame
setTitle("Star Moving with Internal Events");
setLayout(new BorderLayout());
// Initializing the panel which has rotating star
frameStar = new StarPainter(this);
// Adding the bottom toggle button and slider
addToggler();
}
// Getter for the slider value
public int sliderSpeed() {
return starSpeed.getValue();
}
// Adds Button to change the direction of the star
private void addToggler() {
// Adding another jpanel which has layout set to null so i can add button of my size
JPanel panel = new JPanel();
panel.setLayout(null);
panel.setPreferredSize(new Dimension(20,80));
panel.setBackground(Color.WHITE);
// Initializing button and its action listener to change star direction
starToggle = new JButton("Toggle");
starToggle.setBounds(190, 0, 80, 20);
starToggle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == starToggle) {
frameStar.ChangeDirections();
}
}
});
// Adding button to panel
panel.add(starToggle);
// Adding slider to the panel
addSlider(panel);
// Adding panel to the main Panel at the bottom
add(panel, BorderLayout.SOUTH);
}
private void addSlider(JPanel panel) {
// Adding Slider and it's properties
starSpeed = new JSlider(JSlider.HORIZONTAL, 0, 20, 5);
starSpeed.setMajorTickSpacing(10);
starSpeed.setMinorTickSpacing(1);
starSpeed.setPaintTicks(true);
starSpeed.setPaintLabels(true);
starSpeed.setBounds(70,30,400, 45);
// Adding Slider-ChangeListener to change the rotation speed of the star
starSpeed.addChangeListener(new ChangeListener() { // anonymous inner class
// handle change in slider value
#Override
public void stateChanged(ChangeEvent e) {
frameStar.ChangeSpeed(starSpeed.getValue());
}
}
);
// Adding label besides the slider
JLabel label = new JLabel("Speed : ");
label.setBounds(10 , 10, 80, 80);
panel.add(label);
panel.add(starSpeed);
}
}
Class to create the panel that has star rotation
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class StarGraphics extends JPanel{
private boolean toggleDir = false;
private int speed = 10;
private Timer timer;
protected double angleOfStarRotation = 0;
public StarGraphics(JFrame frame) {
setPreferredSize(new Dimension(500, 470));
setBackground(Color.BLACK);
setLayout(new BorderLayout());
frame.add(this, BorderLayout.CENTER);
startTimer();
}
public void startTimer() {
timer = new Timer(speed, new ActionListener() {
public void actionPerformed(ActionEvent e){
// System.out.println(angleOfStarRotation);
if(!toggleDir) //rotates clockwise
angleOfStarRotation = angleOfStarRotation + 1;
else //rotates counterclockwise
angleOfStarRotation = angleOfStarRotation - 1;
// if (angleOfStarRotation == 360 || angleOfStarRotation == -360) // If there is a full circle, it will reset the angle to zero
// angleOfStarRotation = 0;
repaint();
}});
timer.start();
}
public void ChangeSpeed(int newSpeed) {
this.speed = newSpeed;
timer.setDelay(speed);
}
public void ChangeDirections() {toggleDir = !toggleDir; }
}
And a class that paints the star into the panel
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.GeneralPath;
import java.security.SecureRandom;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class StarPainter extends StarGraphics{
private int[] starXPoints = {55, 67, 109, 73, 83, 55, 27, 37, 1, 43};
private int[] starYPoints = {0, 36, 36, 54, 96, 72, 96, 54, 36, 36};
GeneralPath starDesign = new GeneralPath();
public StarPainter(JFrame frame) {
super(frame);
}
public void drawStar(GeneralPath path) {
path.moveTo(starXPoints[0], starYPoints[0]);
for(int i=0; i<10; i++)
path.lineTo(starXPoints[i], starYPoints[i]);
path.closePath();
}
public void starActions(Graphics2D g) {
int startAngle = 360;
// For Random Color
SecureRandom random = new SecureRandom();
// rotate around origin and draw stars in random colors
for (int count = 1; count <= 1; count++)
{
double angle = startAngle - 90;
// rotate coordinate system
g.rotate(angleOfStarRotation * Math.PI / angle); //rotates as per the rotated angle //
// set random drawing color
g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
// draw filled star
g.fill(starDesign);
// dispose the star
g.dispose();
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
drawStar(starDesign);
g2d.translate(250,150);
starActions(g2d);
}
}
After running the code, the output will show a frame with coloring star rotating around the panel in circular motion, but after 2 full rotation, the star rotation slow down automatically. Does anyone know why that happens?

Your "core" problem is right here...
public void drawStar(GeneralPath path) {
path.moveTo(starXPoints[0], starYPoints[0]);
for(int i=0; i<10; i++)
path.lineTo(starXPoints[i], starYPoints[i]);
path.closePath();
}
This gets called each time you re-draw the component, which means, you're adding new points to the shape, making it infinitely more complex on each paint pass.
Instead, just create the shape in the constructor...
public StarPainter() {
starDesign.moveTo(starXPoints[0], starYPoints[0]);
for (int i = 0; i < 10; i++) {
starDesign.lineTo(starXPoints[i], starYPoints[i]);
}
starDesign.closePath();
}
As has already been pointed out, you're disposing of a Graphics context which you did not create.
If your going to change the transformation of the context, you should always create a copy of your own, for example...
public void starActions(Graphics2D g) {
int startAngle = 360;
// For Random Color
SecureRandom random = new SecureRandom();
g = (Graphics2D) g.create();
// rotate around origin and draw stars in random colors
//for (int count = 1; count <= 1; count++) {
double angle = startAngle - 90;
// rotate coordinate system
g.rotate(angleOfStarRotation * Math.PI / angle); //rotates as per the rotated angle //
// set random drawing color
g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
// draw filled star
g.fill(starDesign);
// dispose the star
g.dispose();
//}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(250, 150);
starActions(g2d);
g2d.dispose();
}
Also, passing a reference of the JFrame to the component...
public StarGraphics(JFrame frame) {
setPreferredSize(new Dimension(500, 470));
setBackground(Color.BLACK);
setLayout(new BorderLayout());
frame.add(this, BorderLayout.CENTER);
startTimer();
}
is a bad idea. The component has no need for, nor is it its responsibility too, interact with the frame. You're just exposing implementation detail unnecessarily.
Runnable 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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.GeneralPath;
import java.security.SecureRandom;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setVisible(true);
}
});
}
public class MainFrame extends JFrame {
private StarGraphics frameStar;
private JButton starToggle;
public JSlider starSpeed;
public MainFrame() {
// Setting some properties of the frame
setTitle("Star Moving with Internal Events");
setLayout(new BorderLayout());
// Initializing the panel which has rotating star
frameStar = new StarPainter();
add(frameStar);
// Adding the bottom toggle button and slider
addToggler();
}
// Getter for the slider value
public int sliderSpeed() {
return starSpeed.getValue();
}
// Adds Button to change the direction of the star
private void addToggler() {
// Adding another jpanel which has layout set to null so i can add button of my size
JPanel panel = new JPanel();
panel.setLayout(null);
panel.setPreferredSize(new Dimension(20, 80));
panel.setBackground(Color.WHITE);
// Initializing button and its action listener to change star direction
starToggle = new JButton("Toggle");
starToggle.setBounds(190, 0, 80, 20);
starToggle.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == starToggle) {
frameStar.ChangeDirections();
}
}
});
// Adding button to panel
panel.add(starToggle);
// Adding slider to the panel
addSlider(panel);
// Adding panel to the main Panel at the bottom
add(panel, BorderLayout.SOUTH);
}
private void addSlider(JPanel panel) {
// Adding Slider and it's properties
starSpeed = new JSlider(JSlider.HORIZONTAL, 0, 20, 5);
starSpeed.setMajorTickSpacing(10);
starSpeed.setMinorTickSpacing(1);
starSpeed.setPaintTicks(true);
starSpeed.setPaintLabels(true);
starSpeed.setBounds(70, 30, 400, 45);
// Adding Slider-ChangeListener to change the rotation speed of the star
starSpeed.addChangeListener(new ChangeListener() { // anonymous inner class
// handle change in slider value
#Override
public void stateChanged(ChangeEvent e) {
frameStar.ChangeSpeed(starSpeed.getValue());
}
}
);
// Adding label besides the slider
JLabel label = new JLabel("Speed : ");
label.setBounds(10, 10, 80, 80);
panel.add(label);
panel.add(starSpeed);
}
}
public class StarGraphics extends JPanel {
private boolean toggleDir = false;
private int speed = 10;
private Timer timer;
protected double angleOfStarRotation = 0;
public StarGraphics() {
setPreferredSize(new Dimension(500, 470));
setBackground(Color.BLACK);
setLayout(new BorderLayout());
startTimer();
}
public void startTimer() {
timer = new Timer(speed, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// System.out.println(angleOfStarRotation);
if (!toggleDir) //rotates clockwise
{
angleOfStarRotation = angleOfStarRotation + 1;
} else //rotates counterclockwise
{
angleOfStarRotation = angleOfStarRotation - 1;
}
System.out.println("tick");
// if (angleOfStarRotation == 360 || angleOfStarRotation == -360) // If there is a full circle, it will reset the angle to zero
// angleOfStarRotation = 0;
repaint();
}
});
timer.start();
}
public void ChangeSpeed(int newSpeed) {
this.speed = newSpeed;
timer.setDelay(speed);
}
public void ChangeDirections() {
toggleDir = !toggleDir;
}
}
public class StarPainter extends StarGraphics {
private int[] starXPoints = {55, 67, 109, 73, 83, 55, 27, 37, 1, 43};
private int[] starYPoints = {0, 36, 36, 54, 96, 72, 96, 54, 36, 36};
GeneralPath starDesign = new GeneralPath();
public StarPainter() {
starDesign.moveTo(starXPoints[0], starYPoints[0]);
for (int i = 0; i < 10; i++) {
starDesign.lineTo(starXPoints[i], starYPoints[i]);
}
starDesign.closePath();
}
public void starActions(Graphics2D g) {
int startAngle = 360;
// For Random Color
SecureRandom random = new SecureRandom();
g = (Graphics2D) g.create();
// rotate around origin and draw stars in random colors
//for (int count = 1; count <= 1; count++) {
double angle = startAngle - 90;
// rotate coordinate system
g.rotate(angleOfStarRotation * Math.PI / angle); //rotates as per the rotated angle //
// set random drawing color
g.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
// draw filled star
g.fill(starDesign);
// dispose the star
g.dispose();
//}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(250, 150);
starActions(g2d);
g2d.dispose();
}
}
}

Related

Drawing rectangle within the loop?

I'm trying to animate a rectangle based on a coordinate determined by for-loop, inside a button. Here is my JComponent Class:
public class Rect extends JComponent {
public int x;
public int y;
public int w;
public int h;
public Rect (int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
repaint();
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g);
g2.setColor(Color.green);
g2.drawRect(x+15, y+15, w, h);
}
}
and here is my button and button inside JFrame class:
public class MainFrame extends JFrame {
Rect R = new Rect(15, 15, 50, 50);
JPanel lm = new JPanel();
LayoutManager lay = new OverlayLayout(lm);
JButton animate = new JButton("animate");
public MainFrame () {
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lm.setLayout(lay);
lm.add(R);
}
animate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int k = 0; k < 500; k+=50) {
R = new Rect(k, k, 50, 50);
validate();
repaint();
}
}
});
}
But when I run the code and click the button, nothing happens. What's wrong?
EDIT: I run the frame inside my main class like this:
public class OrImage {
public static void main(String[] args) throws Exception
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame mf = new MainFrame();
mf.setVisible(true);
}
});
}
}
I changed the code of class MainFrame such that when you press the animate button, something happens, but I don't know if that is what you want to happen.
I did not change class Rect and I added main() method to MainFrame just to keep everything in one class.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.LayoutManager;
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.OverlayLayout;
public class MainFrame extends JFrame {
Rect R = new Rect(15, 15, 50, 50);
JPanel lm = new JPanel();
LayoutManager lay = new OverlayLayout(lm);
JButton animate = new JButton("animate");
public MainFrame () {
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lm.setLayout(lay);
lm.add(R);
animate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int k = 0; k < 500; k+=50) {
R = new Rect(k, k, 50, 50);
lm.add(R);
}
lm.revalidate();
lm.repaint();
}
});
add(lm, BorderLayout.CENTER);
add(animate, BorderLayout.PAGE_END);
setLocationByPlatform(true);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MainFrame());
}
}
The main change is in method actionPerformed(). You need to add R to the JPanel. You need to call revalidate() on the JPanel because you have changed the number of components that it contains. And after calling revalidate() you should call repaint() (again, on the JPanel) to make it redraw itself.
This is how it looks before pressing animate.
And this is how it looks after pressing animate
EDIT
As requested – with animation.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.LayoutManager;
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.OverlayLayout;
import javax.swing.Timer;
public class MainFrame extends JFrame {
Rect R = new Rect(15, 15, 50, 50);
JPanel lm = new JPanel();
LayoutManager lay = new OverlayLayout(lm);
JButton animate = new JButton("animate");
private int x;
private int y;
private Timer timer;
public MainFrame () {
setSize(1200, 700);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
lm.setLayout(lay);
lm.add(R);
timer = new Timer(500, event -> {
if (x < 500) {
lm.remove(R);
x += 50;
y += 50;
R = new Rect(x, y, 50, 50);
lm.add(R);
lm.revalidate();
lm.repaint();
}
else {
timer.stop();
}
});
timer.setInitialDelay(0);
animate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
add(lm, BorderLayout.CENTER);
add(animate, BorderLayout.PAGE_END);
setLocationByPlatform(true);
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MainFrame());
}
}

Java JFrame distance between ovals not equal

I had to implement a JFrame with the size of 500 x 500 pixels that should have a 9 x 9 "field" of circles, but as you can see in the picture the distance between the first line of ovals and the second line of ovals is not equal.
The diameter should be 20 pixel and the distance between the center of one oval to another oval should be 40 pixel, but I don't know if I did this correctly:
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.Color;
public class KreisFrame extends JFrame {
public KreisFrame() {
//Set JFrame size
setSize(500,500);
//Make JFrame visible
setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.GREEN);
g.fillRect(0, 0, 500, 500);
for (int j = 0; j < 500; j += 60){
for (int i = 0; i < 500; i += 60) {
// draw circle
g.drawOval(i, 20, 20, 20);
g.drawOval(i, j, 20, 20);
// fill circle
g.fillOval(i, 20, 20, 20);
g.fillOval(i, j, 20, 20);
g.setColor(Color.BLUE);
}
}
}
public static void main(String[]args) {
KreisFrame myframe = new KreisFrame();
}
}
Can someone tell me what I did wrong?
You should really subclass a JPanel, not the JFrame. And all of this should be done on the EventDispatchThread, not the main thread. Further, don't use "magic" numbers like 500, 20, and 40. Here's a solution that paints the entire panel, regardless of what size it is (note that there is no provision here for having a border on the panel).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Kreis extends JPanel {
protected int dia = 20;
protected int sep = 40;
public Kreis() {
// Set JFrame size
setPreferredSize(new Dimension(500, 500));
setBackground(Color.green);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension d = getSize();
g.setColor(Color.BLUE);
for (int y = 0; y < d.height; y += sep) {
for (int x = 0; x < d.width; x += sep) {
// draw circle
g.fillOval(x, y, dia, dia);
}
}
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Create and set up the window.
JFrame jf = new JFrame();
Kreis panel = new Kreis();
jf.add(panel);
jf.pack();
jf.setVisible(true);
jf.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing( WindowEvent arg0) {
System.exit(0);
}
});
}
});
}
}

How I can change the background color of JFrame in this program?

I use the setBackground() method in the Driver Class to change the background color but it does not work.
package paint1;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JPanel;
/**
*
* #author Rehan Shakir
*/
public class PaintPanel extends JPanel {
private final ArrayList<Point> point = new ArrayList<>();
private Color color;
private final JButton red,blue,yellow,green,gray;
private int x = 0;
private int y = 0;
public PaintPanel()
{
setLayout(null);
red = new JButton(" Red ");
red.setBounds(0, 0, 80, 50);
red.setBackground(Color.red);
add(red);
blue = new JButton(" Blue ");
blue.setBounds(82,0 , 80, 50);
blue.setBackground(Color.BLUE);
add(blue);
yellow = new JButton("Yellow");
yellow.setBounds(163,0 , 80, 50);
yellow.setBackground(Color.yellow);
add(yellow);
green = new JButton(" Green");
green.setBounds(242,0 , 80, 50);
green.setBackground(Color.green);
add(green);
gray = new JButton(" Gray ");
gray.setBounds(322,0 , 80, 50);
gray.setBackground(Color.gray);
add(gray);
handler h = new handler();
red.addActionListener(h);
blue.addActionListener(h);
yellow.addActionListener(h);
green.addActionListener(h);
gray.addActionListener(h);
setBackground(Color.RED);
addMouseMotionListener(
new MouseMotionAdapter()
{
#Override
public void mouseDragged(MouseEvent e)
{
x = e.getX();
y = e.getY();
repaint();
}
}
);
}
private class handler implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
String s = e.getActionCommand();
if(s.equals(" Red "))
color = Color.RED;
else if(s.equals(" Blue "))
color = Color.blue;
else if(s.equals("Yellow"))
color = Color.yellow;
else if(s.equals(" Green"))
color = Color.green;
else if(s.equals(" Gray "))
color = Color.gray;
}
}
#Override
public void paintComponent(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, 20, 5);
}
}
<<>>
Here, I use the setBackground() method to change the color but it does not work.
package paint1;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
*
* #author Rehan Shakir
*/
public class Paint1 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
JFrame Jf = new JFrame("A Simple Paint Program");
PaintPanel f = new PaintPanel();
f.setBackground(Color.red); //To Change BACKGROUND COLOR
Jf.add(f,BorderLayout.CENTER);
Jf.add(new JLabel("Drag The Mouse to Draw"),BorderLayout.SOUTH);
Jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Jf.setBackground(Color.black);
Jf.setVisible(true);
Jf.setSize(800,600);
}
}
Please provide me the solution, how can I change the background color of my JFrame? I just want to make the background color of JFrame from the default color to White color.
You've forget to call the parent method in paintComponent.
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g); // add this line to consider background!!!
g.setColor(color);
g.fillOval(x, y, 20, 5);
}
Important: Don't use setBounds() but rather learn the LayoutManager concept. This will help you to make your UI independed to OS, display resolution and window resizing.

Draw a ball in a Panel and move the ball using mouseDragged

I am practicing to draw a ball in the Panel and show the ball's coordinate when the ball is dragged.
This is my first time to practice a drawing exercise(?)
This is my code.
import java.awt.*;
import java.awt.event.*;
public class MovingBall extends Frame {
Panel ballPanel = new Panel();
Label ballLabel = new Label();
Panel coordinatePanel = new Panel();
Label coordinateLabel = new Label();
int x0=0,y0 =0, x=20,y=30;
int nowX, nowY;
Label nowXcoordinateLabel = new Label("Now X :"+nowX);
Label nowYcoordinateLabel = new Label("Now Y :"+nowY);
MovingBall(){
setLayout(new GridLayout(1,1));
ballPanel.add(ballLabel); coordinatePanel.add(coordinateLabel);
add(ballPanel);
add(coordinatePanel);
ballPanel.setBackground(Color.white);
coordinatePanel.setBackground(Color.LIGHT_GRAY);
nowXcoordinateLabel.setBackground(Color.WHITE);
nowYcoordinateLabel.setBackground(Color.WHITE);
coordinatePanel.add(nowXcoordinateLabel);
coordinatePanel.add(nowYcoordinateLabel);
setVisible(true);
setSize(400,400);
MouseMotionListener ml = new MouseMotionAdapter(){
public void mouseDragged(MouseEvent e){
Point p = new Point();
nowX = (int) p.getX();
nowY = (int) p.getY();
}
};
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
dispose();
}
}
);
}
public void paintComponent(Graphics2D gg){
// super.paintComponents(gg);
ballPanel.paintComponents(gg);
gg.setColor(Color.BLUE);
gg.fillOval(x0, y0, 10, 10);
}
public static void main(String[]arg){
MovingBall mb = new MovingBall();
}
}
I have two problems
I used fillOval and paintComponent to draw and display a ball but I don't see that on the screen. Why?
Any idea how to move the ball if I want to move that using mouseDragged? Do I need some thread?
Let's start with
Frame doesn't have a paintComponent method, so nothing is ever going to call it.
Even if it did, position 0x0 would paint the circle under the frame's borders, so you wouldn't see it
You should be getting the Point from the MouseEvent, not from the new Point object you've created
It's not the responsibility of the frame to manage the mouse dragged or painting, the frame is responsible for providing the initial container onto which everything else added
From that, you should move the functionality of the painting and mouse dragged management to it's own class. This provides you with two things, first, a surface onto which you can paint, and which will contained within the frame borders and the mouse events will automatically be converted to the panels context (0x0 will be the top left corner of the panel)
This raises the question about how to update the labels. Well, you could take a leaf from the AWT API and use a simple observer pattern to generate events when the coordinates are changed, for example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Label;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
public class MovingBall extends Frame {
BallPane ballPanel = new BallPane();
Label ballLabel = new Label();
int x0 = 0, y0 = 0, x = 20, y = 30;
int nowX, nowY;
Label nowXcoordinateLabel = new Label("Now X :" + nowX);
Label nowYcoordinateLabel = new Label("Now Y :" + nowY);
MovingBall() {
setLayout(new BorderLayout());
ballPanel.add(ballLabel);
add(ballPanel);
ballPanel.setBackground(Color.white);
nowXcoordinateLabel.setBackground(Color.WHITE);
nowYcoordinateLabel.setBackground(Color.WHITE);
setVisible(true);
setSize(400, 400);
addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
dispose();
}
});
Panel coordinates = new Panel(new FlowLayout());
coordinates.add(nowXcoordinateLabel);
coordinates.add(nowYcoordinateLabel);
coordinates.setBackground(Color.LIGHT_GRAY);
add(coordinates, BorderLayout.SOUTH);
ballPanel.addCoordinateListene(new CoordinateListener() {
#Override
public void coordinatesChanged(CoordinateEvent evt) {
nowXcoordinateLabel.setText("Now X: " + evt.getCoordinate().getX());
nowYcoordinateLabel.setText("Now X: " + evt.getCoordinate().getY());
revalidate();
repaint();
}
});
}
public static void main(String[] arg) {
MovingBall mb = new MovingBall();
}
public class CoordinateEvent extends EventObject {
private final Point p;
public CoordinateEvent(Object source, Point p) {
super(source);
this.p = p;
}
public Point getCoordinate() {
return p;
}
}
public interface CoordinateListener {
public void coordinatesChanged(CoordinateEvent evt);
}
public class BallPane extends Panel {
int x0 = 0, y0 = 0, x = 20, y = 30;
private List<CoordinateListener> coordinateListeners;
public BallPane() {
MouseMotionListener ml = new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
x0 = (int) e.getX();
y0 = (int) e.getY();
fireCoordinateChange(new Point(e.getPoint()));
repaint();
}
};
addMouseMotionListener(ml);
coordinateListeners = new ArrayList<>(5);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.BLUE);
g.fillOval(x0, y0, 10, 10);
}
public void addCoordinateListene(CoordinateListener listener) {
coordinateListeners.add(listener);
}
public void removeCoordinateListene(CoordinateListener listener) {
coordinateListeners.remove(listener);
}
protected void fireCoordinateChange(Point p) {
CoordinateEvent evt = new CoordinateEvent(this, p);
for (CoordinateListener listener : coordinateListeners) {
listener.coordinatesChanged(evt);
}
}
}
}
Make your class extending Panel and make it ready to drawing with overriding paint method and add the MouseMotionListener to listining your panel.Get X and Y coordinates for using in paint method, at last add your drawing panel to Frame.
Simple code : UPDATED
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class TestClass extends Panel {
/**
*
*/
private static final long serialVersionUID = 1L;
Panel ballPanel = new Panel();
Label ballLabel = new Label();
Panel coordinatePanel = new Panel();
Label coordinateLabel = new Label();
int nowX, nowY;
Label nowXcoordinateLabel = new Label("Now X :");
Label nowYcoordinateLabel = new Label("Now Y :");
TestClass() {
coordinatePanel.add(coordinateLabel);
nowXcoordinateLabel.setBackground(Color.WHITE);
nowYcoordinateLabel.setBackground(Color.WHITE);
nowXcoordinateLabel.setPreferredSize(new Dimension(100, 25));
nowYcoordinateLabel.setPreferredSize(new Dimension(100, 25));
coordinatePanel.setPreferredSize(new Dimension(400, 30));
coordinatePanel.setBackground(Color.LIGHT_GRAY);
coordinatePanel.add(nowXcoordinateLabel);
coordinatePanel.add(nowYcoordinateLabel);
MouseAdapter ml = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
nowXcoordinateLabel.setText("Now X :" + e.getX());
nowYcoordinateLabel.setText("Now Y :" + e.getY());
nowX = e.getX();
nowY = e.getY();
repaint();
super.mouseMoved(e);
}
};
setLayout(new GridLayout(1, 1));
setBackground(Color.WHITE);
addMouseMotionListener(ml);
setVisible(true);
setSize(400, 400);
}
#Override
public void paint(Graphics g) {
Graphics2D gg = (Graphics2D) g;
gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gg.setColor(Color.BLUE);
gg.fillOval(nowX, nowY, 20, 20);
}
public static void main(String[] arg) {
TestClass mb = new TestClass();
Frame frame = new Frame("Test drawing");
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
frame.dispose();
super.windowClosing(e);
}
});
frame.setLayout(new GridLayout(1, 1));
frame.add(mb);
frame.add(mb.coordinatePanel);
frame.setSize(800, 600);
frame.setVisible(true);
}
}
To get the position of the mouse instead of:
nowX = (int) p.getX();
Write this:
nowX = (int) e.getX();
You also need to redraw the oval every time the user triggers a Mouse Drag event

JMenuBar with paint method

I'm having trouble getting my JMenuBar to appear alongside my paint() method.
package Main;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
public class Main extends JFrame{
int x, y; // axis position for oval
//JMenuBar Variables
JMenuBar menuBar;
JMenu file;
JMenuItem newGame;
JMenuItem checkScore;
JMenuItem exitGame;
// DOUBLE BUFFERING
private Image dbImage;
private Graphics dbGraphics;
Font font = new Font("Arial", Font.ITALIC, 30);
// KeyListener Class
public class KeyListener extends KeyAdapter {
public void keyPressed(KeyEvent e){
int keyCode = e.getKeyCode();
if(keyCode == e.VK_LEFT){
if(x <=0){ // FRAME COLLISION DETECTION
x=0;
}else
x += -5; //decrement position to the left
}
if(keyCode == e.VK_RIGHT){
if(x >= 380){
x = 380;
}else
x += +5; //incrementing position to the right
}
if(keyCode == e.VK_UP){
if (y <= 25){
y = 25;
}else
y += -5; //decrementing position up
}
if(keyCode == e.VK_DOWN){
if(y >= 380){
y = 380;
}else
y += +5; //incrementing position down
}
}
}
// CONSTRUCTOR
public Main(){
// Window Properties
addKeyListener(new KeyListener()); // creates instance of KeyListener class
setTitle("Tower Defence");
setSize(400, 400);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setBackground(Color.CYAN);
// JMenuBar
menuBar = new JMenuBar();
file = new JMenu("File");
newGame = new JMenuItem("New Game");
checkScore = new JMenuItem("Check High Scores");
exitGame = new JMenuItem("Close Game");
menuBar.add(file);
file.add(newGame);
file.add(checkScore);
file.addSeparator();
file.add(exitGame);
setJMenuBar(menuBar);
// Display frame after all components added
setVisible(true);
// default position for oval
x = 150;
y = 150;
}
public void paint(Graphics g){
dbImage = createImage(getWidth(), getHeight()); // creates image of screen
dbGraphics = dbImage.getGraphics(); // gets graphics to be drawn in off screen image
paintComponent(dbGraphics); // paints graphics
g.drawImage(dbImage, 0, 0, this); // draw image to the visible screen
}
// PAINT GRAPHICS TO SCREEN
public void paintComponent(Graphics g){
g.setFont(font);
g.drawString("Hello World", 100, 200);
g.setColor(Color.red);
g.drawOval(x, y, 15, 15);
g.fillOval(x, y, 15, 15);
repaint();
}
// MAIN METHOD
public static void main(String[] args) {
new Main();
}
}
I saw a different question where the solution was to override the paint method and add super.paint(g);
when i tried this the JMenuBar appears but the frame keeps flickering constantly.
Heck, I'll throw my code in the ring. Recs as per my comments. Also, don't use KeyListeners but rather Key Bindings.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.RenderingHints;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class Main2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int OVAL_W = 15;
private int x = 150;
private int y = 150;
private JMenuBar menuBar;
private JMenu file;
private JMenuItem newGame;
private JMenuItem checkScore;
private JMenuItem exitGame;
private Font font = new Font("Arial", Font.ITALIC, 30);
public Main2() {
menuBar = new JMenuBar();
file = new JMenu("File");
newGame = new JMenuItem("New Game");
checkScore = new JMenuItem("Check High Scores");
exitGame = new JMenuItem("Close Game");
menuBar.add(file);
file.add(newGame);
file.add(checkScore);
file.addSeparator();
file.add(exitGame);
addKeyBinding();
}
public JMenuBar getMenuBar() {
return menuBar;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(font);
g.drawString("Hello World", 100, 200);
g.setColor(Color.red);
g.drawOval(x, y, OVAL_W, OVAL_W);
g.fillOval(x, y, OVAL_W, OVAL_W);
}
private void addKeyBinding() {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
for (final MyDirection dir : MyDirection.values()) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(dir.getKeyCode(), 0);
inputMap.put(keyStroke, dir.toString());
actionMap.put(dir.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent evt) {
int newX = x + dir.getxTrans();
int newY = y + dir.getyTrans();
newX = Math.min(newX, PREF_W - 2 * OVAL_W);
newX = Math.max(newX, OVAL_W);
newY = Math.min(newY, PREF_H - 2 * OVAL_W);
newY = Math.max(newY, OVAL_W);
x = newX;
y = newY;
repaint();
}
});
}
}
enum MyDirection {
UP(KeyEvent.VK_UP, 0, -5), DOWN(KeyEvent.VK_DOWN, 0, 5),
LEFT(KeyEvent.VK_LEFT, -5, 0), RIGHT(KeyEvent.VK_RIGHT, 5, 0);
private int keyCode;
private int xTrans;
private int yTrans;
private MyDirection(int keyCode, int xTrans, int yTrans) {
this.keyCode = keyCode;
this.xTrans = xTrans;
this.yTrans = yTrans;
}
public int getKeyCode() {
return keyCode;
}
public int getxTrans() {
return xTrans;
}
public int getyTrans() {
return yTrans;
}
}
private static void createAndShowGui() {
Main2 main = new Main2();
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(main);
frame.setJMenuBar(main.getMenuBar());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Dont extend JFrame unnecessarily
Dont overide any paint method of JFrame unnecessarily rathe add Jpanel and override paintComponent
Dont call repaint(...) in paintComponent(...) as this will cause a loop i.e repaint will re-call paintComponent and the cycle will carry on
Create and manipulate Swing components on Event Dispatch Thread
Dont call setSize(..) on JFrame rather override getPreferredSize and return an appropriate size which fits all drawings and than call JFrame#pack() before setting JFrame visible
Dont use KeyListener/KeyAdapter for Swing components rather use KeyBindings
Here is your code fixed:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Main extends JPanel {
int x, y; // axis position for oval
//JMenuBar Variables
JMenuBar menuBar;
JMenu file;
JMenuItem newGame;
JMenuItem checkScore;
JMenuItem exitGame;
Font font = new Font("Arial", Font.ITALIC, 30);
// KeyBindings Class
public class KeyBindings {
public KeyBindings(final JComponent jc) {
jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "Right");
jc.getActionMap().put("Right", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
if (x >= 380) {
x = 380;
} else {
x += +5; //incrementing position to the right
}
jc.repaint();
}
});
jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "Left");
jc.getActionMap().put("Left", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
if (x <= 0) { // FRAME COLLISION DETECTION
x = 0;
} else {
x += -5; //decrement position to the left
}
jc.repaint();
}
});
jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "Up");
jc.getActionMap().put("Up", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
if (y <= 25) {
y = 25;
} else {
y += -5; //decrementing position up
}
jc.repaint();
}
});
jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "Down");
jc.getActionMap().put("Down", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
if (y >= 380) {
y = 380;
} else {
y += +5; //incrementing position down
}
jc.repaint();
}
});
}
}
// CONSTRUCTOR
public Main() {
// Window Properties
JFrame frame = new JFrame();
frame.setTitle("Tower Defence");
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//setBackground(Color.CYAN);
// JMenuBar
menuBar = new JMenuBar();
file = new JMenu("File");
newGame = new JMenuItem("New Game");
checkScore = new JMenuItem("Check High Scores");
exitGame = new JMenuItem("Close Game");
menuBar.add(file);
file.add(newGame);
file.add(checkScore);
file.addSeparator();
file.add(exitGame);
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(font);
g.drawString("Hello World", 100, 200);
g.setColor(Color.red);
g.drawOval(x, y, 15, 15);
g.fillOval(x, y, 15, 15);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
};
//add keybindings
new KeyBindings(panel);
frame.add(panel);
frame.setJMenuBar(menuBar);
frame.pack();
// Display frame after all components added
frame.setVisible(true);
// default position for oval
x = 150;
y = 150;
}
// MAIN METHOD
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Main();
}
});
}
}

Categories

Resources