Passing a drawn graphic - java

I'm trying to pass a graphics element up the chain. I need to have the ability to draw a number of different die and return the correct one for each instance. I can get this to compile but I don't know if i'm doing this correctly. I want to pass the graphics component to a panel to be displayed.
My shortened paint class
import java.awt.*;
import javax.swing.*;
class DiePaint extends JPanel
{
Graphics g;
public Graphics dieSwitch(int inInt)
{
return die1();
}
private Graphics die1()
{
//reset drawing
repaint();
//draw a die with 1 pip
g.setColor(Color.BLACK);
g.drawRect(0,0,50,50);
g.drawOval(24,24,2,2);
g.fillOval(24,24,2,2);
//return graphic
return g;
}
}
A method in my other class i'm trying to use to call it.
private void setDie()
{
//set die labels
die1P.paint(drawDie.dieSwitch(game.getDie(0)));
}

"I want to pass the graphics component to a panel to be displayed."
No, you don't.
You need to see how to Perform Custom Painting. You're going to need a paintComponent method in your panel
#Override
protected void paintComponent(Graphic s) {
super.paintComponent(g);
// draw here
}
You don't explicitly call paint like you are doing here die1P.paint(drawDie.dieSwitch
If you want to be able to set what is being painted, you can have a Die object that you use to draw. Something like
class Die {
public void draw(Graphics g) {
// draw die here
}
}
Then in your panel class have a setter for the Die object, that will repaint the new Die. You will probably want to have a way to differentiate each die. Pass some arguments to a constructor to do that. Then paint the one die.
public class DiePanel extends JPanel {
private Die die;
public void setDie(Die die) {
this.die = die;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (die != null) {
die.draw(g);
}
}
}
You may also, instead, want to make Die an interface, so you can implement difference Die objects, like DieOne, DieTwo, etc. Something like
public interface Die {
void Draw(Grapchis g);
}
public class DieOne {
#Override
public void draw(Graphics g) {
// draw die one
}
}
When you call setDie(), you can pass a specific Die, like
DieOne dieOne = new DieOne();
...
diePanel.setDie(dieOne);

Yes you can pass the drawn component by using BufferedImage....first you draw the graphic on this BufferedImage then pass this image and then draw this image to the Panel...Here is the code:
import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
class DiePaint extends JPanel
{
int width=500; int height=500; //you can change as requred
BufferedImage buffg=new BufferedImage(width,height , BufferedImage.TYPE_INT_ARGB);
Graphics g;
public BufferedImage dieSwitch(int inInt) //change return type to BufferedImage
{
return die1();
}
private BufferedImage die1() // change return type to BufferedImage
{
g=buffg.getGraphics(); //get Graphics from buffg..
//reset drawing
repaint();
//draw a die with 1 pip
g.setColor(Color.BLACK); //draw all the component on buffg using its Graphics g
g.drawRect(0,0,50,50);
g.drawOval(24,24,2,2);
g.fillOval(24,24,2,2);
//return graphic
return buffg;
}
}
now once you get this "buffg" now you can draw it on any panel.....
JPanel p=new JPanel(){
#Override
public void paint(Graphics g){
g.drawImage(drawDie.dieSwitch(game.getDie(0))); //get & draw Image
}
};

Related

drawing with multiple classes in java "applet not initialized"

I am trying to draw to a single panel from multiple sources in java. However when I try this test code I get applet not initialized. Keeping in mind I am fairly new to this. How would I get rid of this error and or draw to a panel from multiple sources.
import java.awt.Graphics;
import javax.swing.JPanel;
class Surface extends JPanel {
public void init() {
}
public void paintComponent(Graphics g){
super.paintComponent(g);
DrawRect d = new DrawRect(this);
d.draw( g );
}
}
class DrawRect {
Surface surface;
Graphics g;
public DrawRect(Surface surface)
{
g = surface.getGraphics();
}
public void draw( Graphics g )
{
g.fillRect(20,20,100,50); // (now this will work).
}
}

Passing functions to classes

I have a java form implemented using swing on which I want to place a number of panels in which I can draw on using the graphics2D package.
To do this, I implement the panels using an extension of JPanel thus:
public class GraphicsPanel extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g2d);
// Need to specify a function from the calling class here
MethodFromCallingClass();
}
}
In the calling class I have
public GraphicsPanel Graphics1= new GraphicsPanel() ;
public void Graphics1_Paint()
{
// Code to draw stuff on the Graphics1 panel
}
So question is how do I pass the function (Graphics1_Paint) from the calling class to the GraphicsPanel class?
I've tried reading about interfaces and lambdas but so far they make no sense.
Can anyone enlighten me please.
I think the easiest way is to pass the calling class (or some other interface) to the constructor of your GraphicsPanel like
public class GraphicsPanel extends JPanel {
private CallingClass cc;
public GraphicsPanel(CallingClass cc) {
this.cc = cc;
}
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g2d);
cc.MethodFromCallingClass(); // <-- invoke a call-back.
}
}
So it sounds like you want to define what is drawn outside of the custom JPanel class, where you can pass what you want drawn to the JPanel instance at anytime. This is possible.
First, define an interface that contains one method that draws with a Graphics2D instance:
public interface DrawGraphics()
{
public void draw(Graphics2D g);
}
Next you'll want to extend your GraphicsPanel a bit to have the ability to change an underlying instance of DrawGraphics.
So your constructor should now be:
public GraphicsPanel(DrawGraphics graphicsToDraw) { ...
You should also have a set method for the DrawGraphics stored so you can change it at anytime:
public void setDrawGraphics(DrawGraphics newDrawGraphics) { ...
Make sure you add some synchronization somewhere here, or create all DrawGraphics on the EDT because the paintComponents method will execute on the EDT.
Next the paintComponents method can simply draw the graphics:
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g2d);
// Need to specify a function from the calling class here
graphicsToDraw(g2d);
}
And when you need to change the DrawGraphics:
// Calling from the EDT
myGraphicsPanel.setDrawGraphics(g -> g.drawString("Hello World!", 50, 50);
I seem to have solved the problem, but I'd like comments on usability and good practice
public class CallingClass
{
public JPanel Graphics1 ;
public CallingClass()
{
Graphics1 = new Graphics1_Paint();
}
public class Graphics1_Paint extends JPanel
{
public Graphics2D g2d;
public void paintComponent (Graphics g)
{
g2d = (Graphics2D) g;
super.paintComponent(g2d);
g2d.drawString("From Calling Class",10,10);
}
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new CallingClass();}
});
}
Seems I don't need my GraphicsPanel class after all.

Adding a threaded Object to a JPanel in Java

I'm trying to use the method addBall to Paint a ball with it's own thread unto the coloredBallPanel
I'm very stuck and would appreiciate any help at all.
Btw I'm trying to make bouncing ball program in which all the balls run on their own separate threads.
public class ColoredBallPanel extends JPanel
{
Ball ball;
public ColoredBallPanel()
{
ball= new Ball();
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.fillRect(0, 0, 499, 300);
// ball.paint(g2);
}
}
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BallsFrame extends JFrame
{
private ColoredBallPanel coloredBallPanel;
private final int FRAME_WIDTH = 500;
private final int FRAME_HEIGHT = 500;
private int BallDimensionsx =30,BallDimensionsy=30;
private Ball ball = new Ball();
public BallsFrame()
{
//sets the size of the JFrame
setSize(FRAME_WIDTH, FRAME_HEIGHT);
coloredBallPanel = new ColoredBallPanel();//Initialize a Ball panel
add(coloredBallPanel, BorderLayout.CENTER);//white square in the centre
addBall(); // add two balls to panel(Doesn't work yet)
addBall();
}
public void addBall()
{
Ball ball = new Ball();
coloredBallPanel.add(ball);
Runnable r = new ColoredBallRunnable(ball, coloredBallPanel);
Thread t = new Thread(r);
t.start();
}
}
import java.awt.*;
import javax.swing.*;
public class Ball extends JComponent
{
private int x=(int) (Math.random()*(500 -1)),
y =(int) (Math.random()*(300-1)),
xVelocity=-10,
yVelocity=10;
private int width=30,height=30,size =30;
/**
* #param args
*/
public void update()
{
x+=xVelocity;
y+=yVelocity;
if(x<=0)
{
xVelocity =10;
}
else if(x+size>=500)
{
xVelocity = -10;
}
if(y<=0)
{
yVelocity =10;
}
else if (y+size>=300)
{
yVelocity=-10;
}
}
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.GREEN);
g2.fillOval(x, y, width, height);
}
}
import javax.swing.JComponent;
import javax.swing.JPanel;
public class ColoredBallRunnable implements Runnable
{
private Ball ball ;
public ColoredBallRunnable(Ball ball, ColoredBallPanel coloredBallPanel)
{
// TODO Auto-generated constructor stub
ball = new Ball();
coloredBallPanel = new ColoredBallPanel();
}
public void run()
{
Ball ball = new Ball();
while(true)
{
ball.update();
ball.repaint();
try{
Thread.sleep(10);
}catch(InterruptedException e){
return;
}
}
}
}
There are so many problems...
Ball is component been added to a Container which is under the control of a LayoutManager, this means that, even if you got Ball to move, you would fighting the layout manager all the time
Ball has no "size" (or position for that matter), so when it is added to the Container, it's sized to it's default size of 0x0, making it, virtually, invisible
Ball is never painted. This is actually for two reasons, but we'll start with the obvious, it doesn't override any valid paint methods that would allow Swing to paint it...if it was larger than 0x0.
Solutions...?
Make Ball just a POJO which knows it's size and location (and can update itself if that's what you need)
Create a "model" of some kind that can be shared between the view ColoredBallPanel and the controller (the thread). This model should maintain a List of Balls currently available...
Allow the ColoredBallPanel to loop through this list of balls and paint them via it's paintComponent method
Allow the controller to loop through this list of balls and update them.
Synchronise access to the balls list, so neither the view or controller can mess with the list while the other is using it. You might consider making a read-only version of the list for the view, but that might be beyond the scope right now...
Call super.paintComponent before you do any custom painting
Perhaps, something more like Java Bouncing Ball

What code should I add so that the rectangles previously printed persist,instead of getting erased when the new rectangles are painted?

What code should I add so that the rectangles painted before continue to exist on the screen when new ones are printed.Here is the code
import javax.sound.midi.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MiniMusicPlayer3
{
private boolean fire = false;
private JFrame frame;
public static void main(String args[])
{
MiniMusicPlayer3 mini = new MiniMusicPlayer3();
mini.go();
}
public void go()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600,600);
frame.setVisible(true);
MyDrawPanel boxes = new MyDrawPanel();
frame.getContentPane().add(boxes);
try
{
Sequencer player =MidiSystem.getSequencer();
player.open();
Sequence seq = new Sequence(Sequence.PPQ,4);
Track track = seq.createTrack();
int arr[] ={127};
player.addControllerEventListener(new MyDrawPanel(),arr);
//add notes to the track
for(int i = 5;i<61;i+=4)
{
track.add(makeEvent(144,1,i,100,i));
track.add(makeEvent(176,1,127,0,i));
track.add(makeEvent(128,1,i,100,(i+2)));
}
player.setSequence(seq);
player.setTempoInBPM(220);
player.start();
}
catch(Exception ex)
{
}
}
public MidiEvent makeEvent(int onOff,int one,int note,int vel,int tick)
{
MidiEvent event = null;
try
{
ShortMessage a = new ShortMessage();
a.setMessage(onOff,one,note,vel);
event = new MidiEvent(a,tick);
}
catch(Exception e)
{
}
finally
{
return event;
}
}
class MyDrawPanel extends JPanel implements ControllerEventListener
{
public void controlChange(ShortMessage message)
{
System.out.println("control change happens");
fire = true;
frame.repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(fire)
{
Graphics2D g2d = (Graphics2D)g;
int red = (int)(Math.random()*255);
int blue = (int)(Math.random()*255);
int green = (int)(Math.random()*255);
Color color = new Color(red,blue,green);
g2d.setColor(color);
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);
g2d.fillRect(x, y, width, height);
fire = false;
}
}
}
}
Also why does the code above not let the rectangles persist as opposed to the code below that allows the circles to persist
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Animate
{
private JFrame frame;
private int x=10,y=10;
public static void main(String args[])
{
Animate ballRoll = new Animate();
ballRoll.go();
}
public void go()
{
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyRoll ball = new MyRoll();
frame.getContentPane().add(ball);
for(x = 5;x<=350;x++)
{
y=x;
try
{
Thread.sleep(50);
}
catch(Exception e)
{
System.out.println("dsfsd");
}
ball.repaint();
}
}
class MyRoll extends JPanel
{
public void paintComponent(Graphics g)
{
g.setColor(Color.ORANGE);
g.fillOval(x, y, 100, 100);
}
}
}
Replace frame.repaint() with this.repaint() and remove super.paintComponent(g) if you want to persist the previous painting as well but I never suggest you to use this approach. You have to redraw all the objects again in paintComponent().
Please have a look at below sections for detail information about Paint processing in Swing application.
Painting in AWT and Swing
The Paint processing
A Closer Look at the Paint Mechanism
"What code should I add so that the rectangles previously printed persist,instead of getting erased when the new rectangles are painted?"
Create a list of Rectangle2D object (as a class member).
Loop through the list in the paintComponent and paint each rectangle.
When you want to add a new rectangle, well, add a new rectangle to the list and repaint.
If you want different colors (or and other state) for each rectangle, create a wrapper like (See some of the examples below).
"Also why does the code above not let the rectangles persist as opposed to the code below that allows the circles to persist"
No rectangle are not "persisting" per se. What you are seeing are paint artifacts from not calling super.paintComponent which clears the previous paint. You should always call super.paintComponent though, like in your first example. So your best options is to go with the first part of my answer.
See a bunch of examples here and here and here and here and here and here.
The basic premise of all those examples is storing a list of similar object in a list and iterating through the list to paint all the objects. Each object can have its own specific state.
You could also extend your MyDrawPanel class so you keep track of which rectangles to paint plus their colors. So you could add each new rectangle to a list of rectangles and keep track of the rectangle colors using a map. Then you just need to add new rectangles to the list and the new color to the map. Finally, you'll need to loop through the list of rectangles and paint these one by one.
Here is how it could be done:
class MyDrawPanel extends JPanel implements ControllerEventListener
{
// List of all rectangles that needs to be painted
java.util.List<Rectangle> rectangles = new ArrayList<Rectangle>();
// Map over all colors for each rectangle that must be painted
Map<Rectangle, Color> rectangleColors = new HashMap<Rectangle, Color>();
public void controlChange(ShortMessage message)
{
System.out.println("control change happens");
fire = true;
frame.repaint();
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(fire)
{
Graphics2D g2d = (Graphics2D)g;
int red = (int)(Math.random()*255);
int blue = (int)(Math.random()*255);
int green = (int)(Math.random()*255);
Color color = new Color(red,blue,green);
g2d.setColor(color);
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);
// Create a new rectangle to paint
Rectangle newRect = new Rectangle(x, y, width, height);
// Store the rectangle in the list over rectangles to paint
rectangles.add(newRect);
// Add the color of the rectangle in the map over rectangle colors
rectangleColors.put(newRect, color);
// Paint all the rectangles using their colors one by one
for (Rectangle rect : rectangles) {
// Get the color of the rectangle
Color rectColor = rectangleColors.get(rect);
// Set the rectangle color
g2d.setColor(rectColor);
// Fill the rectangle with the rectangle color
g2d.fill(rect);
}
fire = false;
}
}
}
There are two common approaches:
as has already been mentioned a couple of times, you keep a List of objects to paint and then iterate through the List in the paintComponent(...) method.
draw to a BufferedImage.
Take a look at Custom Painting Approaches which exams both of these approaches and contains working examples of both.

Painting in "layers" with paintComponent() and inheritance

I am attempting to create two components that each have a paintComponent() method -- The goal being that the parent object will paint default art, and the child object can then paint over it. An example could be creating playing cards: you have a parent Card class which paints a default card, then suite-specific classes which extend Card and paint their details on top of it.
Here is a diagram of what I am trying to accomplish:
Here is the code I am working with. My specific problem is: when the component is drawn -- if the child class does not have a paintComponent method -- the parents art is drawn. However, if I implement a paintComponent method on the child, the parents is not drawn, only the childs.
Here is my code:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
abstract public class Tile extends JPanel {
public static final int DEFAULT_WIDTH = 100;
public static final int DEFAULT_HEIGHT = 100;
#Override public void paintComponent(Graphics g){
//Set Size
setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
Graphics2D g2 = (Graphics2D)g;
//Draw default art
}
#Override public Dimension getMinimumSize()
{ return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT); }
#Override public Dimension getPreferredSize()
{ return getMinimumSize(); }
//.....
}
import javax.swing.*;
import java.awt.*;
public class CharacterTile extends Tile {
protected char symbol;
public CharacterTile(char symbol){
this.symbol = symbol;
}
#Override public void paintComponent(Graphics g){
super.paintComponents(g);
Graphics2D g2 = (Graphics2D)g;
//Draw details
}
//.....
}
It's not a good use of inheritance. You should have something like:
Card
{
Art front;
Art back;
paintComponent()
{
back.paintComponent();
front.paintComponent();
}
}
You could even have a List layers; and paint all the layers you need.
Sub classing is for splitting your code into smaller problems. For reusing code you should use polymorphism + object composition.

Categories