How to create a method in other class drawing on JPanel - java

My goal: create a method of class Point (code at the bottom), that allows me to draw multiple points (and lines in the future), so it looks like this:
Point pointA = new Point(0,0);
Point pointB = new Point(10,20);
pointA.draw(); // or probably: pointA.draw(someSurface);
pointB.draw();
I took code from a tutorial and modified it.
I found many similar problems while searching for answer, but either none of them had the answer for my case or I couldn't implement it.
As you can see below, I tried to create the method both inside the Point class and inside the Surface class (commented code), but without success.
What is the problem and what should I fix in my code?
Main:
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
BasicGraphics ex = new BasicGraphics();
ex.setVisible(true);
Point pointA = new Point(0,0);
Point pointB = new Point(10,10);
pointA.draw(ex.currentSurface);
pointB.draw(ex.currentSurface);
}
});
}
}
BasicGraphics:
public class BasicGraphics extends JFrame {
public Surface currentSurface;
public BasicGraphics() {
initUI();
}
private void initUI() {
currentSurface=new Surface();
add(currentSurface);
setTitle("My points");
setSize(100, 100);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Surface:
class Surface extends JPanel {
/*public void drawPoint(Graphics g, Point givenPoint) {
//I want to represent points as little circles
Ellipse2D circle = new Ellipse2D.Double();
Graphics2D g2d = (Graphics2D) g;
circle.setFrameFromCenter(givenPoint.x, givenPoint.y, 10,10);
g2d.draw(circle);
}*/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
}
Point:
public class Point {
public double x;
public double y;
public Point(double newX, double newY){
x=newX;
y=newY;
}
public void draw(Surface givenSurface){
Graphics g = givenSurface.getGraphics();
Ellipse2D circle = new Ellipse2D.Double();
Graphics2D g2d = (Graphics2D) g;
circle.setFrameFromCenter(x, y, 10,10);
g2d.draw(circle);
givenSurface.paint(g2d);
//givenSurface.drawPoint(g2d,this);
//givenSurface.repaint();
}
}
edit: Solutions, thanks to camickr. That's not the most elegant way I can imagine, but it works:
draw method in Point:
public void draw(Surface givenSurface){
givenSurface.pointsList.add(this);
}
Surface class:
class Surface extends JPanel {
public ArrayList<Point> pointsList = new ArrayList<>();
private void drawPoints(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
double diameter = 10;
for (Point currentPoint : pointsList) {
Ellipse2D circle = new Ellipse2D.Double(currentPoint.x - diameter / 2, currentPoint.y - diameter / 2, diameter, diameter);
g2d.fill(circle);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawPoints(g);
}
}

create a method of class Point (code at the bottom), that allows me to draw multiple points (and lines in the future).
All custom painting needs to be done in the paintComponent() method of the panel. So every time the panel is painted the points/lines are repainted.
So if you want to paint multiple object you need to:
Create an ArrayList of the Object you want to paint
Create a method to add Objects to this list
In the paintComponent() method you iterate through the list and paint each Object.
Check out the DrawOnCompont example from Custom Painting Approaches. It shows how you can dynamically add Rectangle to be painted.

Since the paintComponent method will be invoked at setVisible(true) but you draw your Points after that, You have to repaint. See code modification.
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
BasicGraphics ex = new BasicGraphics();
ex.setVisible(true);
Point pointA = new Point(0,0);
Point pointB = new Point(10,10);
pointA.draw(ex.currentSurface);
pointB.draw(ex.currentSurface);
ex.revalidate();
ex.repaint();
}
});
}
}

Related

How to create graphics using JFrame form?

I want to know how to create graphics (draw shapes, move shapes) by using the JFrame form in NetBeans. I have already watched the tutorials in youtube, about how to create shapes and move them. But all the tutorials are done in normal java class file. I want to know how to do it on a JFrame Form (The java class file that you can edit the interface of JFrame visually) like in this image http://image.ohozaa.com/i/b84/mOu1ku.png
I try to do the code similar to the tutorial but it isn't work
Ball.java
public class Ball extends JPanel{
int x = 0;
int y = 0;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D gra = (Graphics2D) g;
gra.setColor(Color.red);
gra.fillOval(x, y, 50, 50);
x++;
y++;
repaint();
}
Interface.java
private void DrawButtonActionPerformed(java.awt.event.ActionEvent evt) {
Ball ball1 = new Ball();
animationPanel.add(ball1);
}
Main class
public static void main(String[] args) {
Interface f = new Interface();
f.setVisible(true);
}

Drawing with a JPanel's getGraphics()?

I've run into a bit of a wall here. I read elsewhere while trying to fix this issue that you are never supposed to getGraphics(). The problem is, I can't use the provided Graphics context from the paint() / paintComponent() methods. I require it to only call my generate(Graphics g) function once, and I can not provide Graphics outside of the override functions.
Any tips? Trimmed for your convenience.
public class Main extends JPanel {
...
static JFrame displayFrame, inputFrame;
...
...
// Generator node list
ArrayList<Node> nodes = new ArrayList<Node>();
public static void main(String[] args) {
// Set up the frame
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
displayFrame = new JFrame("City generator");
displayFrame.setSize(screenSize.width / 3, screenSize.width / 3);
displayFrame.setLocation(screenSize.width / 2 - displayFrame.getWidth()
/ 2, screenSize.height / 2 - displayFrame.getHeight() / 2);
displayFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
displayFrame.add(new Main());
// displayFrame.setUndecorated(true);
displayFrame.setBackground(Color.lightGray);
displayFrame.setVisible(true);
displayFrame.addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
// Mouse movement events here
}
});
}
// Override function
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Calls multiple times
generate(g);
}
private void generate(Graphics g) {
............................
Instead of painting directly to the Graphics context, you could generate a BufferedImage image of what you want painted and paint to that instead...
private BufferedImage buffer;
public BufferedImage generate() {
if (buffer == null) {
buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_IMAGE_ARGB);
Graphics2D g2d = buffer.createGraphics();
// Paint away...
g2d.dispose();
}
return buffer;
}
Then you would paint the result within the paintComponent method...
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Calls multiple times
BufferedImage img = generate();
g.drawImage(img, 0, 0, this);
}
As an example...
Why not just keep a boolean value to track if generate() has been called.
public boolean calledGenerate = false;
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (!calledGenerate) generate(g);
}
void generate(Graphics g) {
calledGenerate = true;
....
}
also you are calling AWT/Swing code outside of the EDT which is a bad idea.
In your main function you normally should call:
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
//build your awt/swing objects here
}
});
What does generate() do though? There is probably a better way to do it.

How to draw objects stored in a vector with paintComponent(Graphics g)

I'm wondering how can i store objects in a vector and then draw them with the paintComponent(Graphics g) method later.
My drawing class is atm:
import java.awt.*;
import java.util.Vector;
import javax.swing.*;
public class PaintingPanel extends JPanel{
public static Rectangle playField = new Rectangle(10, 10, 800, 800);
public static int playFieldCentreX = 399;
public static int playFieldCentreY = 399;
public static Vector contents;
public PaintingPanel(Vector contents)
{
contents = new Vector();
contents.addElement(Breakout.paddle);
contents.addElement(Breakout.ball);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2d.draw(playField);
Breakout.paddle.paintThis(g2d);
Breakout.ball.paintThis(g2d);
}
}
I'm also not sure if i have added the objects correct in the vector, if i do
System.out.println(contents); i just get a vector with 2 null elements.
the Breakout.ball, and paddle elements is both 2 Rectangles. If i try to draw them directly as i have done so far in the bottom it works fine, but its when i want to add in like 20+ bricks i would need to store all the objects in a vector and then draw it all out.
If anyone got some good pointers, or a link to a good tutorial that would be great!
Edit: parts of breakout class
public class Breakout extends TimerTask implements KeyListener {
public static Ball ball;
public static Paddle paddle;
public static PaintingPanel panel;
public static boolean gameRunning;
public static Breakout game;
public Breakout()
{
}
public static void setupGame()
{
paddle = new Paddle(350, 790, 100, 10); //initial paddle creation
ball = new Ball(393, 400, 7);
showGUI();
}
public static void beginGame()
{
gameRunning = true;
}
public void run()
{
if(gameRunning == true)
{
Ball.move();
Paddle.move();
}
}
}
public static void main(String[] args)
{
Timer t = new Timer();
t.schedule(new Breakout(), 0, 40);
panel = new PaintingPanel(PaintingPanel.contents);
setupGame();
beginGame();
while (gameRunning == true)
{
panel.repaint();
}
// TODO a lot
}
private static void showGUI()
{
JFrame frame = new JFrame("Breakout");
game = new Breakout();
frame.addKeyListener(game);
frame.add(panel);
frame.setSize(1000,1000);
frame.setLocationRelativeTo(null); // create game window
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
What you should do is have each object class (if they are even classes, which IMO, they should be), implement a Drawable interface, with a single draw(Graphics g) method.
public interface Drawable {
public void draw(Graphics g);
}
public class Ball implenents Drawable {
#Override
public void draw(Graphics g) {
// draw ball
}
}
Then the Vector (I recommend a List instead) could be typed Drawable. Then you can just loop through it, calling the draw method of each object. (That is after adding all your Drawable objects to it.
public class PaintingPanel extends JPanel {
private List<Drawable> objects;
BreakOut breakOut;
public PaintingPanel(BreakOut breakout) {
this.breakOut = breakOut;
this.objects = breakOut.getSomething();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drawable object: objects) {
object.draw(g);
}
}
}
IMO, this would be the better design. So you can add any object that implements the Drawable interface into the List<Drawable>
See some example here and here and here and here and here and here that draw multiple objects and use a javax.swing.Timer for animation, which maybe you want to consider doing.
We'd need to see your Breakout class to know whether you actually added anything or not. If they're null objects, then it means they were never instantiated. So, basically, call the constructor and use its returned object reference to assign Breakout.paddle and Breakout.ball, respectively.

java 2d puzzle application, cannot draw more then one piece

i'm trying to write a jigsaw puzzle application where an image is cut in pieces, scrambled, and the user have to rearrange them with drag&drop to reassemble the original image. (something like this: http://www.jigzone.com/puzzles/74055D549FF0?z=5).
i have to write this in java with Graphics2d.
so, at first i'm trying to make some kind of component which can show a part of the image (a rectangle for now), and can be dragged with mouse.
the code below works well when there is only one one such component. the problem is, when i add the second component, the first one is no longer visible.
i'm really stuck here. i have a feeling i'm missing something really basic. or maybe i'm on a wrong way. any help will be greatly appreciated.
edit: i changed a bit the code according to suggestions, however, still not working as expected.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
public class GraphicDragAndDrop extends JPanel {
Rectangle rect;
Image img;
public GraphicDragAndDrop(String imgFile, int x0, int y0){
rect = new Rectangle(x0, y0, 150, 75);
img = new ImageIcon(imgFile).getImage();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
g2d.drawImage(img, x, y, this);
}
public void setRect(int x, int y) {
rect.setLocation(x, y);
repaint();
}
public static void main(String[] args) {
// first piece
GraphicDragAndDrop piece1 = new GraphicDragAndDrop("a.png", 0, 0);
piece1.setRect(0, 0);
new GraphicDragController(piece1);
// second piece --> only this will be visible
GraphicDragAndDrop piece2 = new GraphicDragAndDrop("a.png", 200, 200);
//GraphicDragAndDrop piece2 = new GraphicDragAndDrop("b.png", 200, 200); // does'n work either
piece2.setRect(150, 150);
new GraphicDragController(piece2);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(piece1);
f.add(piece2);
f.setSize(500,500);
f.setLocation(300,100);
f.setVisible(true);
}
}
class GraphicDragController extends MouseInputAdapter {
GraphicDragAndDrop component;
Point offset = new Point();
boolean dragging = false;
public GraphicDragController(GraphicDragAndDrop gdad) {
component = gdad;
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
Rectangle r = component.rect;
if(r.contains(p)) {
offset.x = p.x - r.x;
offset.y = p.y - r.y;
dragging = true;
}
}
public void mouseReleased(MouseEvent e) {
dragging = false;
}
public void mouseDragged(MouseEvent e) {
if(dragging) {
int x = e.getX() - offset.x;
int y = e.getY() - offset.y;
component.setRect(x, y);
}
}
}
Your code above is written to draw only one image:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setClip(rect);
int x = rect.x;
int y = rect.y;
// here
g2d.drawImage(new ImageIcon("a.png").getImage(), x, y, this);
}
If you need to draw more than one image, then consider creating a collection of images and iterating through the collection in paintComponent using a for loop:
also, never read in the image from within paintComponent since this method should be lean, mean and fast, and should concern itself with painting only. Also, there's no need to read the image in each time your program has to draw it as that's very inefficient and will slow the program unnecessarily. Instead read the image in once in the constructor or a similar init method.
For example,
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Image img: myImageCollection) {
g2d.drawImage(img, 0, 0, this);
}
}
Edit
You state:
also, my plan was to have more objects of GraphicDragAndDrop class, each of them showing a different piece of the original image. is my approach wrong?
You could use components, but I have a feeling that it would be easy to drag images. I think it would be easier to rotate them for instance if you want your program to have this functionality. If not, though then sure use a component, but if you go this route, I would recommend using a JLabel and simply setting its ImageIcon rather than dragging JPanels.

Drag rectangle on JFrame in Java

I want to draw rectangle based on mousedrag event. if user dragging the mouse, then the rectangle on the applet should increase or decrease basing on current mouse coordinates.
i have the following code.
in the following code i am using SelectionArea class which extends a canvas on which i am performing drawing operation. i am using image variable in this class for double buffering to reduce flickering and to save the applet's previous state(i.e drawing content of applet)
but the code is working fine if i draw first rectangle. if i start to draw second rectangle the previously drawn rectangle is disappearing. i want the previously drawn rectangle to be on the screen
plz tell me how to solve this.
What you need to do, is save the previously drawn rectangle in some sort of data structure, so you can draw it again later.
This code (sorry about the length, will do something similar to what you are describing.
To use it, just slap the JPanel inside of a JFrame.
public class DrawPane extends JPanel {
private List<DrawnShape> drawings;
private DrawnShape curShape;
public DrawPane() {
drawings = new ArrayList<DrawnShape>();
setBackground(Color.WHITE);
setPreferredSize(new Dimension(300, 300));
addMouseListener(clickListener);
addMouseMotionListener(moveListener);
}
#Override
protected void paintComponent(Graphics g2) {
super.paintComponent(g2);
Graphics2D g = (Graphics2D) g2;
for (DrawnShape s : drawings) {
s.draw(g);
}
g.setColor(Color.BLACK);
g.setStroke(new BasicStroke(2));
if (curShape == null)
return;
curShape.draw(g);
}
private MouseListener clickListener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
curShape = new DrawnShape(e.getPoint(), e.getPoint());
}
#Override
public void mouseReleased(MouseEvent e) {
drawings.add(new DrawnShape(curShape.getClickP(), e.getPoint()));
curShape = null;
}
};
private MouseMotionListener moveListener = new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
curShape = new DrawnShape(curShape.getClickP(), e.getPoint());
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
};
}
class DrawnShape {
private Point p1, p2;
public DrawnShape(Point p1, Point p2) {
this.p1 = p1;
this.p2 = p2;
}
public Point getClickP() {
return p1;
}
public void draw(Graphics2D g) {
g.drawLine(p1.x, p1.y, p2.x, p1.y);
g.drawLine(p1.x, p1.y, p1.x, p2.y);
g.drawLine(p2.x, p2.y, p2.x, p1.y);
g.drawLine(p2.x, p2.y, p1.x, p2.y);
}
}
Custom Painting Approaches shows two techniques for doing this.

Categories