I am working on a java 2d game library. I want a method named paintImage() to do graphics.drawImage() every time paintImage() is called.
public void paintImage(image1, x, y){
//i want it to run graphics.drawImage every time it is called.
}
public void anotherMethod(){
paintImage(...);
paintImage(...);
//paint as many times as i want.
}
public void paintComponent(Graphics graphics){
graphics.drawImage();
super.paintComponents();
}
Thanks for your time and please leave a suggestion, sorry but its kind of hard to explain this.
For Single Image Display
public class DrawingDemo {
private JPanel panel;
private MyImage imageData;
public DrawingDemo() {
...
panel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (imageData != null) {
g.drawImage(imageData.getImage(), imageData.getX(), imageData.getY(), this);
}
}
};
...
}
public void paintImage(Image image1, int x, int y) {
imageData = new MyImage(image1, x, y);
panel.repaint();
}
public void anotherMethod() {
paintImage(...);
paintImage(...);
}
}
public class MyImage { // bean class for storing image information
private Image image;
private int x;
private int y;
public MyImage(Image image, int x, int y) {
this.image = image;
this.x = x;
this.y = y;
}
public Image getImage(){
return image;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
... you can add setter methods
}
UPDATE : For multiple image display
private JPanel panel;
private ArrayList<MyImage> imageData; // or any other data structure you like
public DrawingDemo() {
imageData = new ArrayList<>();
JFrame frame = new JFrame();
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (MyImage myImage : imageData) {
g.drawImage(myImage.getImage(), myImage.getX(), myImage.getY(), this);
}
}
};
frame.add(panel);
frame.setVisible(true);
}
public void paintImage(Image image1, int x, int y) {
imageData.add(new MyImage(image1, x, y));
panel.repaint();
}
public void anotherMethod() {
paintImage(new ImageIcon("/home/blackadmin/Desktop/image.jpg").getImage(), 0, 0);
paintImage(new ImageIcon("/home/blackadmin/Desktop/image2.jpg").getImage(), 50, 50);
paintImage(new ImageIcon("/home/blackadmin/Desktop/image3.jpg").getImage(), 100, 100);
}
OUTPUT :
Have a look at this answer
Comment if you don't understand anything, hope this will help
What I think you're looking to do is to make changes to some states in your class and then redrawing your images with changes based on those state changes -- in other words perhaps you're looking to do animation. If so, then your image drawing should all be done either within the paintComponent method using its Graphics object, or in another method called by paintComponent one that uses the Graphics object passed into paintCocalzmponent. This can be done by passing a Graphics parameter into the other method. Your anotherMethod would then request that the JVM repaint the GUI by calling repaint(). For example:
public void anotherMethod() {
x++;
y++;
repaint(); // this will stimulate JVM to call paint/paintComponent
}
private void paintImage(Graphics g, BufferedImage img, int x, int y2) {
g.drawImage(img, x, y2, this);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintImage(g, image1, x, y);
}
A complete example of this is as follows:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.Transient;
import javax.swing.*;
public class PaintEg extends JPanel {
private static final int IMG_W = 30;
private static final int IMG_H = IMG_W;
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 20;
private BufferedImage image1;
private int x;
private int y;
public PaintEg() {
image1 = createImg();
new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
anotherMethod();
}
}).start();
}
private BufferedImage createImg() {
BufferedImage img = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setBackground(Color.red);
g2.clearRect(0, 0, IMG_W, IMG_H);
g2.setColor(Color.blue);
g2.fillRect(IMG_W / 4, IMG_H / 4, IMG_W / 2, IMG_H / 2);
g2.dispose();
return img;
}
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void anotherMethod() {
x++;
y++;
repaint(); // this will stimulate JVM to call paint/paintComponent
}
private void paintImage(Graphics g, BufferedImage img, int x, int y2) {
g.drawImage(img, x, y2, this);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
paintImage(g, image1, x, y);
}
private static void createAndShowGUI() {
PaintEg paintEg = new PaintEg();
JFrame frame = new JFrame("PaintEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(paintEg);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Related
I made a fairly simple code and i got into an error which confused me.
So I have a class that creates two totally different variables and creating them using the new keyword
Player playerLeft = new Player(5,150);
Player playerRight = new Player( 150,150);
Player class:
import javax.swing.*;
import java.awt.*;
public class Player extends JComponent {
private int posY;
private int posX;
public Player(int x, int y) {
posX = x;
posY = y;
//repaint();
}
public float getMovementY() {
return movementY;
}
public void setMovementY(int movementY) {
this.movementY = movementY;
}
int movementY = 0;
public void paintComponent(Graphics g) {
Graphics2D _g2 = (Graphics2D) g;
Rectangle rect = new Rectangle(posX, posY, 20, 150);
_g2.fill(rect);
}
public void setLocation(int x, int y) {
posY = y;
posX = x;
repaint();
}
public void move() {
setLocation(posX, posY + movementY);
}
}
It's probably me not knowing something about Java but for me when I try to instantiate playerRight it just overwrites player left and drawsOut playerRight only.
Here is the complete code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;
public class mainJFrame extends JFrame implements KeyListener {
int relativeTimeMillsec = 0;
Player playerLeft = new Player(5, 150);
Player playerRight = new Player(150, 150);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
relativeTimeMillsec++;
refreshTimeText(relativeTimeMillsec);
calcMovements();
}
};
//components
JLabel timeCounterLabel = new JLabel("Time: " + 0, SwingConstants.CENTER);
public mainJFrame() {
createComponents();
addKeyListener(this);
}
public void createComponents() {
this.setTitle("The title");
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
timer.scheduleAtFixedRate(task, 0, 10);
JButton testButton = new JButton("Label");
testButton.setSize(100, 25);
testButton.setLocation(this.getWidth() / 2 - testButton.getWidth() / 2, this.getHeight() / 2 - testButton.getHeight() / 2);
timeCounterLabel.setSize(200, 25);
timeCounterLabel.setLocation(this.getWidth() / 2 - timeCounterLabel.getWidth() / 2, 10);
//playerRight = new Player(this.getWidth()-45,this.getHeight()/2);
// this.add(testButton);
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
}
public void paintComponent(Graphics g) {
{
super.repaint();
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_S) {
playerLeft.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_W) {
playerLeft.movementY = -2;
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
playerRight.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
playerRight.movementY = -2;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
private double calcRealRelativeTime(int _relTime) {
return relativeTimeMillsec / (double) 100;
}
private void refreshTimeText(int _relTime) {
timeCounterLabel.setText("Time: " + Math.round(calcRealRelativeTime(_relTime)));
}
private void calcMovements() {
playerLeft.move();
playerRight.move();
}
}
Understand that a JFrame's contentPane (the container that holds its components) uses BorderLayout by default, and this code:
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
is adding all components to the same default BorderLayout.CENTER position, meaning any components added will replace components added previously.
But more importantly, yours is a common problem and stems from your having your Player class extend from a GUI component. Don't do this, as then you will have a great deal of difficulty drawing multiple Player objects and having them interact easily (as you're finding out). Instead have Player be a logical (non-component) class, and have only one class extend JPanel and do all the drawing. This class can hold Player objects, perhaps held in a collection such as an ArrayList<Player>, and then iterate through the collection within its paintComponent method.
Other issues:
Do not use java.util.Timer and java.util.TimerTask for Swing animations since these classes do not follow Swing threading rules. Use instead a javax.swing.Timer.
Learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others
If/when you do override a painting method such as paintComponent, be sure to call the super's method within your override, usually on the first line, so as not to break the painting chain. Also, use the #Override annotation before this method and any other methods that you think that you may be overriding so that the compiler catches possible errors with this.
For example (but not a complete example)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleAnimation extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static final int TIMER_DELAY = 20;
private Player2 playerLeft = new Player2(5, 150, Color.RED);
private Player2 playerRight = new Player2(150, 150, Color.BLUE);
public SimpleAnimation() {
playerLeft.setySpeed(1);
playerRight.setySpeed(-1);
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
playerLeft.draw(g);
playerRight.draw(g);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
playerRight.move();
playerLeft.move();
repaint();
}
}
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
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 Player2 {
private static final int RECT_WIDTH = 20;
private static final int RECT_HEIGHT = 50;
private int x;
private int y;
private int ySpeed;
private Color color;
public Player2(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setySpeed(int ySpeed) {
this.ySpeed = ySpeed;
}
public int getySpeed() {
return ySpeed;
}
public void setLocation(int x, int y) {
setX(x);
setY(y);
}
public void move() {
setLocation(x, y + ySpeed);
}
public void draw(Graphics g) {
g.setColor(color);
g.fillRect(x, y, RECT_WIDTH, RECT_HEIGHT);
}
}
I am trying to make this program that has two images that move in the straight line and when they read the end of frame, they turn their direction... But the thing is, the images aren't appearing on the screen idk why.. Here is my code for Actor class
public class Actor {
private Image img;
private int x,y,width,height;
private final int RIGHT=1,LEFT=-1;
private byte direction=RIGHT;
public Actor(Image img, int x,int y, int width, int height){
this.x=x;
this.y=y;
this.width=width;
this.height=height;
}
public Image getImg() {
return img;
}
public void setImg(Image img) {
this.img = img;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public void movement(int frameWidth){
setX(getX()+direction);
if(getX()<0) direction= RIGHT;
if(getX()>(frameWidth-width)) direction= LEFT;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
and here is my main class:
public class game extends JFrame implements Runnable{
private int framewidth=1000;
private int frameheight=1500;
Image image= new ImageIcon("pics/buffy.png").getImage();
Image image2= new ImageIcon("pics/buffythelayer.jpg").getImage();
private Thread thread;
private int picX=100;
private int c=1;
private int xSpeed=3;
private int xFly=1;
private int yFly=100;
private Actor greenCar,pinkCar;
public game(){
setBounds(100,100,framewidth,frameheight);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
thread= new Thread(this);
thread.start();
greenCar=new Actor(image,30,70,98,40);
pinkCar=new Actor(image2,400,70,98,40);
}
public void paint(Graphics g){
g.fillRect(xFly, yFly, 10, 10);
g.drawImage(greenCar.getImg(), greenCar.getX(), greenCar.getY(), null);
g.drawImage(pinkCar.getImg(), pinkCar.getX(), pinkCar.getY(), null);
if(c==2){
g.setColor(Color.CYAN);
g.fillOval(100, 200, 150, 200);
}
}
public static void main(String[] args) {
new game();
}
public void run() {
while(true)
{
xFly++;
greenCar.movement(framewidth);
pinkCar.movement(framewidth);
/*if(picX>280){
xSpeed=-xSpeed;
picX=picX+xSpeed;
c=2;
}
if(picX>=100){
xSpeed=3;
picX=picX+xSpeed;
}*/
repaint();
try{
thread.sleep(13);
}
catch(InterruptedException e){
}
}
}
}
I think I see the problem. When you run the code below, you set the last value, the ImageObserver, to null.
g.drawImage(greenCar.getImg(), greenCar.getX(), greenCar.getY(), null);
g.drawImage(pinkCar.getImg(), pinkCar.getX(), pinkCar.getY(), null);
Instead, you should write it like this:
g.drawImage(greenCar.getImg(), greenCar.getX(), greenCar.getY(), this);
g.drawImage(pinkCar.getImg(), pinkCar.getX(), pinkCar.getY(), this);
Therefore, the JFrame is the object that is notified as the image loads and can be drawn on the screen correctly.
If that's not the case, then you should add super.paint(g) to your paint method.
Your paint(g) method should look like this:
public void paint(Graphics g){
super.paint(g);
g.fillRect(xFly, yFly, 10, 10);
g.drawImage(greenCar.getImg(), greenCar.getX(), greenCar.getY(), this);
g.drawImage(pinkCar.getImg(), pinkCar.getX(), pinkCar.getY(), this);
if(c==2){
g.setColor(Color.CYAN);
g.fillOval(100, 200, 150, 200);
}
}
I hope this helps.
The problem is you run thread before you construct the car object, so
creat object first, the run the thread
greenCar=new Actor(image,30,70,98,40);
pinkCar=new Actor(image2,400,70,98,40);
thread.start();
and you forget set image in Actor constructor
public Actor(Image img, int x,int y, int width, int height){
this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.img = img;
}
I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event){
frame.repaint();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I cannot figure out why my graphics program's render function is not displaying my rectangle. Also, if I change the bufferstrategy to '3' I get funky behavior. Currently, my project has two main classes on called Main and the second called UniversalJframe. The UniversalJfame class should be called display, well at least you can think of it as being display. Please keep in mind I am still new to java programming.
public class Main extends Canvas implements Runnable{
public int w = 200;
public int h = 200;
public String t = "Hello";
private boolean running = false;
private UniversalJframe frame;
private Thread thread;
private BufferStrategy bs;
private Graphics g;
private PauseTest pause;
public void run(){
System.out.println("Run Method");
while(running){
render();
tick();
pause.pause();
}
stop();
}
public synchronized void start(){
if(running) { return; }
System.out.println("Starting Main Program");
running = true;
thread = new Thread(this);
thread.start();
}
public synchronized void stop(){
if(!running) { return; }
System.out.println("Stopping");
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void render(){
bs = frame.getCanvas().getBufferStrategy();
if(bs == null){
frame.getCanvas().createBufferStrategy(2);
return;
}
g = bs.getDrawGraphics();
//Draw Here
g.setColor(Color.GREEN);
g.fillRect(10, 10, 10, 10);
//End Draw
bs.show();
g.dispose();
}
public void tick(){
}
public Main(){
frame = new UniversalJframe(h, w, t, this);
pause = new PauseTest();
pause.setDuration(500000);
start();
}
public static void main(String args[]){
new Main();
System.out.println("Running Main Program");
}
}
public class UniversalJframe extends Canvas {
private static final long serialVersionUID = 1L;
private JFrame jFrame;
private Canvas canvas;
int height = 200; int width = 200;
String title = "";
Main obj;
public UniversalJframe(int height, int width, String title, Main obj){
this.height = height;
this.width = width;
this.obj = obj;
this.title = title;
init();
}
public Canvas getCanvas(){
return canvas;
}
private void init(){
jFrame = new JFrame(title);
jFrame.setSize(new Dimension(width, height));
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setResizable(false);
jFrame.add(obj);
jFrame.setLocationRelativeTo(null);
jFrame.setVisible(true);
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(width, height));
jFrame.add(canvas);
jFrame.pack();
}
}
You're using Canvas, which is an old AWT class.
Here's a fun example of a simple graphics application using Java Swing.
The eye balls follow the cursor around the drawing panel.
Here's the code. You can use the JFrame and drawing JPanel as a base for any graphics application.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MovingEyes implements Runnable {
private static final int drawingWidth = 400;
private static final int drawingHeight = 400;
private static final int eyeballHeight = 150;
private static final int eyeballWidthMargin = 125;
private static final int eyeballOuterRadius = 50;
private static final int eyeballInnerRadius = 20;
private DrawingPanel drawingPanel;
private Eye[] eyes;
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingEyes());
}
public MovingEyes() {
this.eyes = new Eye[2];
this.eyes[0] = new Eye(new Point(eyeballWidthMargin, eyeballHeight));
this.eyes[1] = new Eye(new Point(drawingWidth - eyeballWidthMargin,
eyeballHeight));
}
#Override
public void run() {
frame = new JFrame("Moving Eyes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel();
frame.add(drawingPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -2977860217912678180L;
public DrawingPanel() {
this.addMouseMotionListener(new EyeballListener());
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(drawingWidth, drawingHeight));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (Eye eye : eyes) {
drawCircle(g, eye.getOrigin(), eyeballOuterRadius);
fillCircle(g, eye.getEyeballOrigin(), eyeballInnerRadius);
}
}
private void drawCircle(Graphics g, Point origin, int radius) {
g.drawOval(origin.x - radius, origin.y - radius, radius + radius,
radius + radius);
}
private void fillCircle(Graphics g, Point origin, int radius) {
g.fillOval(origin.x - radius, origin.y - radius, radius + radius,
radius + radius);
}
}
public class Eye {
private final Point origin;
private Point eyeballOrigin;
public Eye(Point origin) {
this.origin = origin;
this.eyeballOrigin = origin;
}
public Point getEyeballOrigin() {
return eyeballOrigin;
}
public void setEyeballOrigin(Point eyeballOrigin) {
this.eyeballOrigin = eyeballOrigin;
}
public Point getOrigin() {
return origin;
}
}
public class EyeballListener extends MouseMotionAdapter {
private final double eyeballDistance = eyeballOuterRadius
- eyeballInnerRadius - 5;
#Override
public void mouseMoved(MouseEvent event) {
Point p = event.getPoint();
for (Eye eye : eyes) {
Point origin = eye.getOrigin();
double theta = Math.atan2((double) (p.y - origin.y),
(double) (p.x - origin.x));
int x = (int) Math.round(Math.cos(theta) * eyeballDistance)
+ origin.x;
int y = (int) Math.round(Math.sin(theta) * eyeballDistance)
+ origin.y;
eye.setEyeballOrigin(new Point(x, y));
}
drawingPanel.repaint();
}
}
}
I have some code to draw rectangles. It's used to draw rectangles on a JPanel, to mark boundaries of widgets. Here the code first, after that I'll explain my problem cq. question.
First off, I have a class (WidgetDrawingPanel) which extends JPanel.
public WidgetDrawingPanel(int width, int height) {
/*To make things visible at least*/
widgets.add(new Widget(10,10,100,100, WidgetType.TextField));
widgets.add(new Widget(50,50,100,200, WidgetType.TextField));
this.width = width;
this.height = height;
this.setBackground(Color.BLUE);
addListener(); //adds both MouseMotionListener and MouseListener
}
Below you'll see me reference ch a lot. This is a CoordinateHolder, which holds start and current coordinates of my mouse movement.
private void addListener() {
this.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent arg0) {
ch.currentX = arg0.getX();
ch.currentY = arg0.getY();
System.out.println("dragging " + ch.currentX + ","+ch.currentY);
WidgetDrawingPanel.this.repaint();
}
});
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent event) {
ch.endX = event.getX();
ch.endY = event.getY();
try {
checkCoords();
} catch (OutsidePanelException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "drawn Outside Panel");
}
}
#Override
public void mousePressed(MouseEvent event) {
ch = new CoordinateHolder(event.getX(), event.getY());
}
});
}
and, finally, the paintComponent(Grapics) method. There's loop through Widgets, which are actually just already drawn Rects (x, y, w, h attributes), but which a little more information, which is not useful in the drawing part of the application. Everytime you release the mouse, the CoordinateHolder is converted into a Widget, and added to widgets.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint");
g.setColor(Color.BLUE);
g.fillRect(0, 0, width, height); //making the whole panel blue
g.setColor(Color.RED);
Graphics2D g2 = (Graphics2D)g;
g2.setStroke(new BasicStroke(3));
for (Widget w : widgets) {
g.drawRect(w.getX(), w.getY(), w.getW(), w.getH());
}
if (ch != null)
g.drawRect(ch.startX, ch.startY, ch.currentX - ch.startX, ch.currentY - ch.startY);
}
This code is working, but I suspect this is highly inefficient and inperformant, as above code continually refreshes the JPanel on mouse drag, which is, say, once every 10ms? I suppose it'll get slow really soon, especially when the user creates a heck of a lot rectangles (which are also continally redrawn, as seen in painComponent(Graphics)).
Question cq. Problem
Is there a better, less resource consuming method, where the user can drag rectangles smoothly?
I read an answer to this Drag rectangle on JFrame in Java, but the author of that answer seems to do it the same as me. But again, that's way inperformant, right? Or should computers be easily able to redraw the component continually, and is this actually a valid approach?
To show lots of non-changing background shapes, draw them to a BufferedImage and then show that BufferedImage in the paintComponent(...) method. So while a shape is being drawn, draw it in paintComponent(...) but once the shape is done being drawn, perhaps on mouseRelease, then draw it in the background BufferedImage.
Note that what will slow your current drawing code the most may be your debugging SOP statements, but I assume that these will be removed from the finished code.
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private BufferedImage backgroundImg;
private Point startPt = null;
private Point endPt = null;
private Point currentPt = null;
public DrawingPanel() {
backgroundImg = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
Graphics g = backgroundImg.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, PREF_W, PREF_H);
g.dispose();
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseMotionListener(myMouseAdapter);
addMouseListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, this);
}
if (startPt != null && currentPt != null) {
g.setColor(DRAWING_COLOR);
int x = Math.min(startPt.x, currentPt.x);
int y = Math.min(startPt.y, currentPt.y);
int width = Math.abs(startPt.x - currentPt.x);
int height = Math.abs(startPt.y - currentPt.y);
g.drawRect(x, y, width, height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void drawToBackground() {
Graphics g = backgroundImg.getGraphics();
g.setColor(FINAL_DRAWING_COLOR);
int x = Math.min(startPt.x, endPt.x);
int y = Math.min(startPt.y, endPt.y);
int width = Math.abs(startPt.x - endPt.x);
int height = Math.abs(startPt.y - endPt.y);
g.drawRect(x, y, width, height);
g.dispose();
startPt = null;
repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent mEvt) {
currentPt = mEvt.getPoint();
DrawingPanel.this.repaint();
}
#Override
public void mouseReleased(MouseEvent mEvt) {
endPt = mEvt.getPoint();
currentPt = null;
drawToBackground();
}
#Override
public void mousePressed(MouseEvent mEvt) {
startPt = mEvt.getPoint();
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = new DrawingPanel();
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}