What I Want To Do: Animate the rectangles so that they go from the right of the screen to the left side of the screen. Here's the code for painting:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class graphics extends JPanel{
public static Timer a;
public static int animation = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(new Color(40,40,40));
g.setColor(new Color(197,255,172));
g.fillRect(animation, 0, 800, 35);
g.setColor(new Color(141,229,123));
g.fillRect(animation, 35, 800, 35);
g.setColor(new Color(112,183,98));
g.fillRect(animation, 70, 800, 35);
g.setColor(new Color(84,137,73));
g.fillRect(animation, 105, 800, 35);
g.setColor(new Color(42,68,36));
g.fillRect(animation, 140, 800, 35);
g.setFont(new Font("Dekar Light", Font.PLAIN, 30));
g.setColor(Color.WHITE);
g.drawString("Graphics Test", 326, 300);
a = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
repaint();
int velx = 5;
animation = animation - velx;
System.out.println(animation);
}
});
a.start();
}
}
And here is the frame:
My Problem: As you can see, it seems that the rectangles move double the distance than what they last moved.
My Question: What am I doing wrong? I need to know if it's something with either the timer or the equation I'm using.
I need to know if it's something with either the timer
A painting method should only ever do painting!
It should NOT start the Timer. Every time you paint the component you start another Timer so you end up generating multiple repaint() requests. The RepaintManager will then combine multiple requests into a single repaint of the component.
The Timer should be started in the constructor of the class or you should create a startAnimation() method method and add it to your panel. Then that method can be invoked after the frame is made visible (or as required).
Also, class names should ALWAYS start with an upper case character. However you should NOT use "Graphics" since there is already a Java class with that name. Make your class name more descriptive.
Related
The Situation
I am currently trying to build a 2D game with Java's Swing. For this, I have my main class Puzzle which is subclassing JFrame. To my frame I add my main JPanel which consists of several JPanels added together (each of them being a new piece).
EDIT 2: PlayingField is my model which will store the current location of each piece.
One can select a piece with the mouse (the plan is to highlight it) and move it with the arrow keys as long as the next step (a full cell, so approx. 100 pixel) isn't the location of one of the other pieces. As of right now, PlayingFielddoes not store any data since the pieces are missing.
private void createAndShowGui() {
// The playing-field with a 4x6 grid.
PlayingField field = new PlayingField(4, 6);
JPanel mainPanel = new ComputerView(field);
setTitle("Puzzle");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 600);
add(mainPanel);
setVisible(true);
}
The method above will create my frame and adds the main panel. The following method is my main panel which adds several JPanels to itself.
public ComputerView(PlayingField field) {
this.field = field;
this.setLayout(null);
JPanel topLeft = new GamingPiece(PlayingField.TOP_LEFT);
add(topLeft);
JPanel topRight = new GamingPiece(PlayingField.TOP_RIGHT);
add(topRight);
JPanel bottomLeft = new GamingPiece(PlayingField.BOTTOM_LEFT);
add(bottomLeft);
}
Each GamingPiece or rather my sub-JPanels are drawing a basic piece (I only drawing one and rotating the others, since all consists of the same arbitrary shape). The GamingPiece class also subclasses JPanel and invokes the JPanel#paintComponent() method to draw the piece.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.YELLOW);
g2.fillPolygon(pieceX, pieceY, pieceX.length);
}
The Problem And My Questions
Since I am pretty new to Java I really do not know how to do it properly. If I add my pieces by creating a new object and adding it to the main panel, it won't show all of them, only the last one added. Some don't even seem to work, even if they're the only ones added (to explain my situation: I have for pieces which are the same arbitrary shape just rotated differently but using Graphics2D#rotate() doesn't seem to work fine).
I hope I explained my situation and my problem well enough fo you guys to help me. Thanks in advance!
EDIT:
My Codes
Puzzle.java
package programming.schimmler;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import programming.schimmler.model.PlayingField;
import programming.schimmler.view.ComputerView;
public class Puzzle extends JFrame {
...
Invoking the createAndShowGui()
...
private void createAndShowGui() {
// The playing-field with a 4x6 grid.
PlayingField field = new PlayingField(4, 6);
JPanel mainPanel = new ComputerView(field);
setTitle("Puzzle");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 600);
add(mainPanel);
setVisible(true);
}
}
ComputerView.java
package programming.schimmler.view;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JPanel;
import programming.schimmler.model.PlayingField;
public class ComputerView extends JPanel {
...
Instance variables
....
public ComputerView(PlayingField field) {
this.field = field;
this.setLayout(null);
JPanel topLeft = new GamingPiece(PlayingField.TOP_LEFT);
add(topLeft);
JPanel topRight = new GamingPiece(PlayingField.TOP_RIGHT);
add(topRight);
JPanel bottomLeft = new GamingPiece(PlayingField.BOTTOM_LEFT);
add(bottomLeft);
}
}
GamingPiece.java
package programming.schimmler.view;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import programming.schimmler.model.PlayingField;
/**
*
*/
public class GamingPiece extends JPanel {
...
public GamingPiece(int type) {
switch (type) {
// Need to draw each polygon from different coordinates since rotating did not work yet.
case PlayingField.TOP_LEFT:
pieceX = new int[] { 100, 100, 300, 300, 200, 200, 100 };
pieceY = new int[] { 100, 100, 100, 200, 200, 300, 300 };
break;
case PlayingField.TOP_RIGHT:
pieceX = new int[] { 400, 400, 300, 300, 200, 200 };
pieceY = new int[] { 0, 200, 200, 100, 100, 0 };
break;
case PlayingField.BOTTOM_LEFT:
pieceX = new int[] { 0, 200, 200, 100, 100, 0 };
pieceY = new int[] { 400, 400, 300, 300, 200, 200 };
break;
case PlayingField.BOTTOM_RIGHT:
pieceX = new int[] { 400, 400, 300, 300, 200, 200 };
pieceY = new int[] { 400, 200, 200, 300, 300, 400 };
break;
case PlayingField.SQUARE:
pieceX = new int[] { 100, 300, 300, 100 };
pieceY = new int[] { 100, 100, 300, 300 };
break;
}
setLayout(null);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.YELLOW);
g2.fillPolygon(pieceX, pieceY, pieceX.length);
}
}
These classes above are the only classes interacting with my GUI, no other classes take part.
You are overcomplicating things by trying to add JPanel puzzle pieces to your JPanel puzzle board. Problems will rapidly become apparent with your non-rectangular puzzle pieces, like the TOP_LEFT shape, which has a cut-out from the lower right side of the piece. When it comes time to overlap the puzzle pieces, to fit the cut-away portions together, the piece added later will occlude pieces drawn earlier.
For instance, if you try to nest a TOP_LEFT piece (A) and a BOTTOM_RIGHT piece (B) together.
AAAAAA BBB
AAAAAA BBB
AAA BBBBBB
AAA BBBBBB
You will only see:
+---+------+
|AAA| BBB|
|AAA| BBB|
|AAA|BBBBBB|
|AAA|BBBBBB|
+---+------+
The overlapping area will be drawn by only one of the panels. The entire area of the B piece, including the blank space, will be drawn in the area of the second piece, on top of whatever the A piece hoped to display.
You might be able to get around this by setting the JPanel to be transparent, and not calling super.paintComponent() which paints the entire component the background colour. But then you still have to draw the shape on the JPanel, and position the JPanel properly on the parent JPanel.
Forget panels within panels! Just draw the pieces on the parent JPanel.
Example:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Puzzle {
public static void main(String[] args) {
SwingUtilities.invokeLater(Puzzle::new);
}
private final static int shape_x[] = { -100, 100, 100, 0, 0, -100 };
private final static int shape_y[] = { -100, -100, 0, 0, 100, 100 };
public Puzzle() {
JFrame frame = new JFrame("Puzzle");
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
View view = new View();
frame.setContentPane(view);
Shape l_shape = new Polygon(shape_x, shape_y, shape_x.length);
view.pieces.add(new Piece(Color.YELLOW, l_shape, 0, 100, 100));
view.pieces.add(new Piece(Color.GREEN, l_shape, 180, 300, 300));
view.pieces.add(new Piece(Color.RED, l_shape, 65, 450, 145));
frame.setVisible(true);
}
}
#SuppressWarnings("serial")
class View extends JPanel {
final List<Piece> pieces = new ArrayList<>();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gc = (Graphics2D) g;
for (Piece piece : pieces)
piece.draw(gc);
}
}
class Piece {
final Color color;
final Shape shape;
final int angle;
int x, y; // Move pieces by by changing these
Piece(Color color, Shape shape, int angle, int x, int y) {
this.color = color;
this.shape = shape;
this.angle = angle;
this.x = x;
this.y = y;
}
void draw(Graphics2D gc) {
AffineTransform tf = gc.getTransform();
gc.translate(x, y);
gc.rotate(Math.toRadians(angle));
gc.setColor(color);
gc.fill(shape);
gc.setTransform(tf);
}
}
Note that this is also a Minimal, Complete and Verifiable example. Minimal: No package statement; No extra HashSet and Set imports which aren't used; doesn't try to do anything except show 3 game pieces. It is complete: a single file you can compile, and run. It is verifiable: you can run it and see it shows 3 game pieces.
As a bonus, it shows Graphics2D.rotate() working, with one piece rotated 180°, and another rotated at an angle that is not appropriate for your puzzle but useful in demonstrating the that rotate works just fine.
I am trying to add a border to a Rectangle element and for some reason it will not work, is it not compatible with JFrame? I can set my entire JFrame to having a border, but it can't find setBorder with my rectangles. Here is my code:
package trivia;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.border.Border;
#SuppressWarnings("serial")
public class Main extends JFrame{
boolean mainMenu = true;
static Color tan = Color.decode("#F4EBC3");
static Color darkGreen = Color.decode("#668284");
static Color buttonColor = Color.decode("#A2896B");
Rectangle header = new Rectangle(0, 0, 500, 100);
Rectangle body = new Rectangle(0, 100, 500, 400);
Rectangle start = new Rectangle(150, 150, 200, 40);
public Main() {
setTitle("Trivia Game!");
setSize(500, 500);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
#Override
public void paint(Graphics g) {
Dimension d = this.getSize();
Border blackline;
blackline = BorderFactory.createLineBorder(Color.black);
if(mainMenu = true){
g.setColor(darkGreen);
g.fillRect(header.x, header.y, header.width, header.height);
g.setFont(new Font("Courier", Font.BOLD, 24));
g.setColor(Color.BLACK);
drawCenteredString("Trivia Game!", d.width, 125, g);
g.setColor(tan);
g.fillRect(body.x, body.y, body.width, body.height);
g.setColor(buttonColor);
g.fillRect(start.x, start.y, start.width, start.height);
}
}
public void drawCenteredString(String s, int w, int h, Graphics g) {
FontMetrics fm = g.getFontMetrics();
int x = (w - fm.stringWidth(s)) / 2;
int y = (fm.getAscent() + (h- (fm.getAscent() + fm.getDescent())) / 2);
g.drawString(s, x, y);
}
public static void main(String[] args) {
#SuppressWarnings("unused")
Main m = new Main();
}
}
And when I add this in my paint function:
start.setBorder(blackline);
It gives me the error:
The method setBorder(Border) is undefined for the type Rectangle
I am not sure how I can make it recognize the setBorder function, can anyone help? All help is much appreciated!
Rectangle does not have a setBorder method, instead, set the color of the Graphics context using Graphics#setColor(Color) and either use Graphics#drawRect(int, int, int, int) or Graphics2D#draw(Shape)
You're breaking the paint chain. Painting is made up of a series of chained method calls, which when called correctly, paint the current component and its child components. By not calling super.paint you're preventing from doing this and could cause any number of nasty side effects, none of which you really want...
You should avoid overriding paint of top level containers, like JFrame, for a number of reasons; they're not double buffered; there a bunch of other components sitting on top of the frame which may paint over it; etc. Instead, create a custom component, extending from something like JPanel and override it's paintComponent method instead (ensuring that you call super.paintComponent first)...
See Painting in AWT and Swing,
Performing Custom Painting and 2D Graphics for more details
Sounds like you're trying to draw the rectangle referenced by start. In that case, you want to be invoking a method on a Graphics, not on a Rectangle. So:
g.drawRect(start.x, start.y, start.width, start.height);
You Can run this program ... but , i have a problems in understanding some of the codes in Graphics class .. I don't blame anyone for this but this is a part of a lab exercise .. i need to make my snowman wave his (g.drawline) hands . using a button.. or if you can provide another way .. please do .. i'll appreciate it.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
public class SnowMan extends Applet implements ActionListener
{
Button button1 = new Button ("Wave!");
public void init()
{
setLayout(new BorderLayout());
add(button1, BorderLayout.NORTH);
button1.addActionListener(this);
}
public void paint(Graphics g)
{
g.fillRoundRect(240,100,10,10,10,10);
g.fillRoundRect(264,100,10,10,10,10);
/**
*eyes
*/
g.fillRoundRect(248,180,10,10,10,10);
g.fillRoundRect(248,210,10,10,10,10);
g.fillRoundRect(248,240,10,10,10,10);
g.fillRoundRect(248,270,10,10,10,10);
g.drawArc(232,65,50,70,245,50);
/**
*mouth
*/
g.drawLine(200,169, 150, 125);
g.drawLine(165,139, 145, 139);
g.drawLine(165,139, 160, 120);
/**
*
*right hands
*/
g.drawLine(300,169, 350, 125);
g.drawLine(333,139, 338, 120);
g.drawLine(333,139, 355, 138);
g.drawRoundRect(219,74,75,75,75,75);
g.setXORMode(Color.red);
g.fillRoundRect(175,148,150,150,150,150);
g.setPaintMode();
}
public void actionPerformed (ActionEvent e)
{
Object source = e.getSource();
if (button1 == source)
{
}
}
}
For starters your white space scares me a little bit. You might want to think about fixing your indentation and line skips. People are much more likely to help you if your code is neat.
More to the point, what you need to do is change the location of the tip of the arm and base the location of the other two fingers on that location. Simply rewrite the location of the points of the fingers in terms of the variable location. Then all you have to do is trigger an animation by using a boolean and the button you already set up.
For a project as simple as this you should be okay with just adding in a little block of code in the paint function which increments the location (if you want circular motion think sin and cosine for your x and y values of the arm). Just increment this until you reach the desired limit then turn around (maybe use a delta variable to consolidate this).
This is my final output ^^ ...
/**
*
*Author : Mark Dave Soriano
*Studied # STI General Santos City , Philippines
*Copro - Exercise
* "Snow Man"
*Objective: Wave the hands of the snowman
*
*
*/
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
public class SnowMan extends Applet implements ActionListener
{
Button button2Wave = new Button ("Wave");
int xStart = 198;
int yEnd = 169;
int xEndingsStart = 150;
int yStartEndings = 125;
int ctr;
int ctr2nd;
public void init ()
{
setLayout(new BorderLayout());
add(button2Wave, BorderLayout.SOUTH);
button2Wave.addActionListener(this);
}
public void paint(Graphics g)
{
g.setColor(Color.blue);
g.fillRect(0, 250, 500,150 ); // ground
g.setColor (Color.yellow);
g.fillOval (-40, -40, 80, 80); // sun
g.setColor(Color.white);
g.fillRoundRect(219,74,75,75,75,75);//head
g.fillRoundRect(175,148,150,150,150,150); //body
g.fillRoundRect(63,10,130,60,80,80);//dialog oval
g.setColor(Color.black);
g.fillRoundRect(248,180,10,10,10,10);//body buttons
g.fillRoundRect(248,210,10,10,10,10);//body buttons
g.fillRoundRect(248,240,10,10,10,10);//body buttons
g.fillRoundRect(248,270,10,10,10,10);//body buttons
g.drawString("Hi! I'm Olaf ", 97,35);//body buttons
g.drawString("and I like Warm Hugs ", 70,45);
g.fillRoundRect(240,100,10,10,10,10);//eyes
g.fillRoundRect(264,100,10,10,10,10);//eyes
g.drawArc(232,65,50,70,245,50);//smile
g.drawLine (230, 75, 280, 75); // brim of hat
g.fillRect(235,50, 40, 25); // top of hat
g.drawLine(xStart,yEnd, xEndingsStart, yStartEndings);//right arm
g.drawLine(300,169, 350, 125);//left arm
setBackground( Color.cyan);
if (ctr==1)
{
g.clearRect(98,70,100,100);
ctr +=1;
g.clearRect(300,70,100,100);
if (ctr==2)
{
g.drawLine(xStart,yEnd, 131, 169);
g.drawLine(300,169,369,169);
ctr2nd+=1;
if(ctr2nd==4)
{
ctr2nd-=3;
}
}
}
//wave down
if(ctr2nd==2)
{
g.clearRect(98,70,100,100);
ctr2nd +=1;
g.clearRect(300,70,100,100);
if(ctr2nd==3)
{
g.drawLine(xStart,yEnd, xEndingsStart, yStartEndings);
g.drawLine(300,169, 350, 125);
ctr-=2;
}
}
//wave up
}
public void actionPerformed (ActionEvent e)
{
Object source = e.getSource();
if (source==button2Wave)
{
System.out.println ("Counter Value: " + ctr);
if (ctr==0)
{
this.repaint(98,70,100,100);
this.repaint(300,70,100,100);
ctr +=1;
}
if(ctr2nd==1)
{
this.repaint(98,70,100,100);
this.repaint(300,70,100,100);
ctr2nd+=1;
}
}
}
}
I was trying to use XOR mode in Graphics to draw a 1bit texture in color against a flat background, when I encountered a behaviour in Graphics I don't understand.
Here is an example of what I mean, isolated:
package teststuff;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class XORTest extends JFrame {
public XORTest() {
super("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 400);
setIgnoreRepaint(true);
setResizable(false);
setVisible(true);
createBufferStrategy(2);
Graphics graphics = getBufferStrategy().getDrawGraphics();
graphics.setColor(Color.black);
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(Color.green);
graphics.fillRect(30, 40, 100, 200);
graphics.setXORMode(Color.white); // (*)
graphics.fillRect(60, 80, 100, 200);
graphics.dispose();
getBufferStrategy().show();
}
public static void main(String[] args) {
XORTest test = new XORTest();
}
}
If I uncomment the line marked with (*), two green rectangles are drawn as expected. If I leave it, nothing is drawn into the component, not even the black background or green rectangle that is drawn beforehand. Even more odd, it worked once. I had the color as Color.green instead of white before. After I changed it, it drew as expected. But when I closed the application and started it again, it didn't work anymore and it hasn't since.
Is this a bug in java? In my jre? Undocumented behaviour for Graphics? I'm on Windows and running the example on the jdk7.
Screenshots: Imgur album because it won't let me post 3 links
The third screenshot is the code as it is above, the first with (*) commented and the second is how it looked the one time it worked (I created that in GIMP because I didn't take a screenshot then).
Without a compelling reason to the contrary, it's easier and more reliable to override paintComponent() in JPanel, which is double buffered by default. With a compelling reason, follow the guidelines in BufferStrategy and BufferCapabilities. Also note,
Override getPreferredSize() to specify the preferred size of a component.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/16721780/230513
*/
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new XORPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class XORPanel extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(190, 320);
}
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.setColor(Color.black);
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.setColor(Color.green);
graphics.fillRect(30, 40, 100, 200);
graphics.setXORMode(Color.white);
graphics.fillRect(60, 80, 100, 200);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}
Excuse me,May I ask java repaint repeatlyed questions,
I meet a trouble that I use drafting repeatedly to express Pacman Open&Close mouth motion.
But this program Only once will not move...
Somebody can help me to solve this problem...
Very thanks so much!:D
My code as below:
package Strive;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
class CDrawF extends JFrame {
CDrawF (){
setTitle("繪製各式圖形"); //JFrame interface
setBounds(50, 50, 490, 260); setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i<= 360; i++){ //repeatly 360 times
repaint();
g2.setColor(new Color(1.0f, 0.0f, 1.0f));
g2.fill(new Arc2D.Double(100, 100, 80, 80, 30, 300, Arc2D.PIE));
//PacMan close mouth
repaint();
try{ //Delay setions
Thread.sleep(1000);
}catch(InterruptedException ex){}
g2.fill(new Arc2D.Double(100, 100, 80, 80, 10, 340, Arc2D.PIE));
//PacMan open mouth
repaint();
}
}
}
public class NewClass { //Main
public static void main(String[] args){
new CDrawF ();
}
}
Suggestions:
Don't draw directly in the JFrame
Don't draw in the paint(...) method.
Never call Thread.sleep(...) on the Swing event thread
And especially don't call it in a paint(...) or paintComponent(...) method.
Instead draw in a JPanel or JComponent's paintComponent(...) method
Read the Java graphics tutorials as they will explain all of this.
Don't call repaint() inside of paint(...) or paintComponent(...)
Use a Swing Timer for your animation loop. Again the tutorials will help you do this.