Painting in "layers" with paintComponent() and inheritance - java

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.

Related

Relation and implementation of Graphics and paint? [duplicate]

I tried few source codes of drawing in java and they were working fine, but when i tried to make one of my own I could not get the paint(Grahpics g) method to work! I looked again at the codes I have and checked some of the tutorials in Oracle's pages but i don't seem to be able to know why it would not work.
can someone please check it and tell me what is wrong here??
main method:
public class main
{
public static void main(String[] args)
{
new board();
}
}
board:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class board implements ActionListener
{
private JFrame f = new JFrame("Speedy");
private JPanel gamePanel = new JPanel();
private Image bg = new ImageIcon(this.getClass().getResource("road.png")).getImage();
private Timer t;
private car myCar = new car();
public board()
{
t = new Timer(50,this);
t.start();
gamePanel.setSize(600,400);
gamePanel.setDoubleBuffered(true);
gamePanel.setFocusable(true);
gamePanel.addKeyListener(new TAdapter());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(gamePanel,BorderLayout.CENTER);
//f.addKeyListener(new TAdapter());
f.setBounds(200,100,600,400);
f.setVisible(true);
f.revalidate();
f.repaint();
}
public void paint(Graphics g) {
gamePanel.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(bg,0,0,null);
g2d.drawImage(myCar.getImg(), myCar.xPos, myCar.yPos, null);
System.out.println("Painted");
g.dispose();
}
public void actionPerformed(ActionEvent e)
{
gamePanel.repaint();
//System.out.println("Painting..");
}
private class TAdapter extends KeyAdapter {
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e)
{
myCar.keyPressed(e);
System.out.println("You pressed: "+e);
}
}
}
car.java:
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import javax.swing.ImageIcon
;
public class car
{
private Image image;
public int xPos,yPos;
public car()
{
image = new ImageIcon(this.getClass().getResource("car.png")).getImage();
xPos = 300;
yPos = 200;
System.out.println(image.getWidth(null));
}
public Image getImg() {return image;}
public void move() {}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) xPos -= 1;
if (key == KeyEvent.VK_RIGHT)xPos += 1;
if (key == KeyEvent.VK_UP) yPos -= 1;
if (key == KeyEvent.VK_DOWN) yPos += 1;
}
}
There are no errors, it shows me the width of the image which is right, also the timer triggers the ActionListener, also KeyListener is working, but the images would not draw! the paint(Graphics g) method just does not want to get triggered!
Googling it did not help.. I thought this would be a common problem but nobody has the problem I have, all solutions failed me.
help please?
If someone can explain it would be most appreciated!
Your class Board does not extend the JPanel class. So the paint() method is never called by the Swing.
Also, the statement gamePanel.repaint() will only execute the default JPanel paint() method of gamePanel. Instead you want the overridden paint method to be executed, so might want to do this:
public class Board extends JPanel implements ActionListener {
....
public void paint(Graphics g) {
this.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(bg,0,0,null);
g2d.drawImage(myCar.getImg(), myCar.xPos, myCar.yPos, null);
System.out.println("Painted");
g2d.dispose();
}
....
}
Replace your action functionality with this:
public void actionPerformed(ActionEvent e) {
this.repaint();
}
Alternative solution:
If you do not want your Board class to extend JPanel, you can also override the paint() method of the gamePanel as you initialize it.
gamePanel = new JPanel() {
#Override
public void paint(Graphics g) {
this.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(bg,0,0,null);
g2d.drawImage(myCar.getImg(), myCar.xPos, myCar.yPos, null);
g2d.dispose();
}
};
However, I would recommend the first solution rather than this one with anonymous classes.
When you call repaint on some container, then what happens is that Swing looks at all the components in that container and calls their paint method.
However, your board class (you should be calling it Board, by the way. Class names should always start with a capital) is not a component of your JFrame. When you call repaint, Swing will attempt to call the paint method of the JPanel that is a component of that JFrame. But you didn't override that method. You just added a paint method to your board, and board is not a component of the JFrame.
For this reason, usually you are supposed to create a class that extnds JPanel or some other component, and then add the current object of that class as a component to the JFrame. This way, your paint method will be called when the JFrame is repainted.
Your "main" class (board) should extend JPanel to work as expected.
With your way, paint would never be called. it acts like any normal self-written function.
If you want to keep things as they are, you can do something ike that:
gamePanel = new JPanel()
{
#Override
public void paint(Graphics g)
{
//your code here
}
};
Please keep in mind that a Class name should start with a capital letter. It won't make any errors but it is a naming convention as you can see here:
http://www.oracle.com/technetwork/java/codeconventions-135099.html

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 a drawn graphic

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
}
};

Troubles with a piechart applet

Alright the deal is I am trying to use the drawPie method to create my pie chart in an applet. After attempting google searches I find multiple tutorials that explain part of the process but not all of it. While trying to knit together partial information I am not getting the results I want.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JApplet;
import javax.swing.JComponent;
public class JLW_PieApplet extends JApplet {
class PieData extends JComponent {
PieValue[] slices = new PieValue[4];
PieData() {
slices[0] = new PieValue(35, Color.red);
slices[1] = new PieValue(33, Color.green);
slices[2] = new PieValue(20, Color.pink);
slices[3] = new PieValue(12, Color.blue);
}
public void paint(Graphics g) {
drawPie((Graphics2D)g, getBounds(), slices);
}
}
}
Ther isn't such method in Swing called drawPie. Without the contents of this method, we have no idea of how to help you
Try having a read through 2D Graphics and have a look at Ellipse2D in particular
The other problem I can see is you don't call super.paint(g) in your paint method. This is VERY, VERY, VERY important
You have a PieData component within your applet but you never add it, so you need to add it in init and bring in drawPie from your link above:
public class JLW_PieApplet extends JApplet {
public void init() {
add(new PieData());
}
class PieData extends JComponent {
PieValue[] slices = new PieValue[4];
PieData() {
slices[0] = ...
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawPie((Graphics2D) g, getBounds(), slices);
}
public void drawPie(Graphics2D g, Rectangle area, PieValue[] slices) {
...

overriding paint method in JApplet

I am working on a project to make a JApplet's contents automatically scale to the size specified in the html. I realize this is the kind of thing that layout managers were made for, however as I am not permitted to rewrite the entire applets structure, i decided i would try to override paint and simply set the AffineTransform of the Graphics object to an appropriately scaled version, then capture mouse Events in the top container and scale them back with an appropriate scaling transform. Im getting stuck on the drawing part at the moment. When viewed in a web browser, it renders the scaled version correctly once, then the image is shrunk back to its original size. In addition, it seems that the paint method in JApplet is only ever called once. Here is a cropped version of my code which focuses on the problem. Any help would be appreciated. Thanks in advance.
import javax.swing.JApplet;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
public class Test extends JApplet
{
public static final int ORIGINAL_APPLET_WIDTH = 1024;
public static final int ORIGINAL_APPLET_HEIGHT = 800;
private AffineTransform scalingTransform;
private AffineTransform inverseScalingTransform;
#Override
public void init()
{
double xFactor = ((double)(this.getWidth()))/((double)(Test.ORIGINAL_APPLET_WIDTH));
double yFactor = ((double)(this.getHeight()))/((double)(Test.ORIGINAL_APPLET_HEIGHT));
this.scalingTransform = new AffineTransform();
this.inverseScalingTransform = new AffineTransform();
this.scalingTransform.scale(xFactor,yFactor);
this.inverseScalingTransform.scale(1D/xFactor,1D/yFactor);
}
#Override
public void paint(Graphics g)
{
((Graphics2D)g).setTransform(Test.this.scalingTransform);
super.paint(g);
}
}
I figured out, after much research, that the problem was that JApplet's paint method doesn't get called very often. The instead the content pane has its own drawing surface, so i simply had to replace the content pane to get it to upload. heres the way i did it:
#Override
public void init()
{
double xFactor = ((double)(this.getWidth()))/((double)(qt.ORIGINAL_APPLET_WIDTH));
double yFactor = ((double)(this.getHeight()))/((double)(qt.ORIGINAL_APPLET_HEIGHT));
this.scalingTransform = new AffineTransform();
this.inverseScalingTransform = new AffineTransform();
this.scalingTransform.scale(xFactor,yFactor);
this.inverseScalingTransform.scale(1D/xFactor,1D/yFactor);
JPanel drawScale = new JPanel()
{
#Override
public void paint(Graphics g)
{
((Graphics2D)g).setTransform(Test.this.scalingTransform);
super.paint(g);
}
#Override
public void paintAll(Graphics g)
{
((Graphics2D)g).setTransform(Test.this.scalingTransform);
super.paintAll(g);
}
#Override
public void paintComponents(Graphics g)
{
((Graphics2D)g).setTransform(Test.this.scalingTransform);
super.paintComponents(g);
}
#Override
public void paintComponent(Graphics g)
{
((Graphics2D)g).setTransform(Test.this.scalingTransform);
super.paintComponents(g);
}
};
Container oldPane = this.getContentPane();
drawScale.setLayout(oldPane.getLayout());
this.setContentPane(drawScale);
}
these paint methods were of course in addition to the ones in the applet.

Categories