This is more of a conceptual question, so it's hard to post a small workable code sample. But, I have a class that overrides paintComponent here:
public abstract class BasePanel extends JPanel {
...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
this.standardDraw(drawObjects,g2);
}
}
Basically, I'd like this to be the "standard way" this base panel draws if paintComponent is not overridden in a derived class. So, I have a derived class called AspectRatioPanel which I'd like to re-specify how it draws things:
public class AspectRatioPanel extends BasePanel {
...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
// Get ViewPort Bounding Box to clip
BoundingBox viewPortBoundingBox = this.getViewPortBoundingBox();
// Clip to viewport
g2.setClip((int)viewPortBoundingBox.topLeft.getX(),(int)viewPortBoundingBox.topLeft.getY(),(int)viewPortBoundingBox.getWidth(),(int)viewPortBoundingBox.getHeight());
this.standardDraw(drawObjectsBuf,g2);
}
}
The problem I'm having is the call super.paintComponent(g) in the derived class. I intend for it to be calling paintComponent in JComponent, but it is going through BasePanel first. Is there a better way to approach this problem? I could delete the paintComponent method in BasePanel, but having a standard way of drawing things is useful for me. I also can't seem to call JComponent.paintComponent directly since it's protected. Is there a solution for this? Also, am I doing something conceptually wrong?
Probably I misunderstood your problem, but I would separate standard and custom painting
public abstract class BasePanel extends JPanel {
...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
provideCustomPainting(g);
}
protected void provideCustomPainting(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
this.standardDraw(drawObjects,g2);
}
}
public class AspectRatioPanel extends BasePanel {
protected void provideCustomPainting(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
// Get ViewPort Bounding Box to clip
BoundingBox viewPortBoundingBox = this.getViewPortBoundingBox();
// Clip to viewport
g2.setClip((int)viewPortBoundingBox.topLeft.getX(),(int)viewPortBoundingBox.topLeft.getY(),(int)viewPortBoundingBox.getWidth(),(int)viewPortBoundingBox.getHeight());
this.standardDraw(drawObjectsBuf,g2);
}
}
You could simply not call super.paintComponent(Graphics); by overwriting paintComponent() you have 3 options:
a.) Call super at the beginning of the method; you paint code paints on top of what has been painted by the super class.
b.) Don't call super at all - your paintComponent needs to ensure it paints everything that is needed; if the components is opaque that means you need to paint the entire area the component occupies.
c.) Call super at your convinience; whatever is painted in super appears to be "layered" in sequence where you call it. Makes only sense if the super method doesn't paint the entire area.
If you insist to use paintComponent from a specific class of your inheritance hierarchy as super.paintComponent, regardless of inheritance hierarchy in between, thats also possible:
BasePanel extends JPanel {
protected final defaultPaintComponent(Graphics g) {
super.paintComponent(g);
}
}
Child classes can call "defaultPaintComponent" instead of super.paintComponent, thus bypassing any implementation classes in between in the hierarchy defined (I suggest declaring it final to prevent accidental overwrites).
Related
I created a customized Class which extends JButton to create buttons with a Graphic image for a button panel, I override paintComponenet() which also calls a method drawColoredShape().
The Graphic images are mostly GeneralPath Shapes.
When running the application, paintComponent() of each Button is repeatedly called.
Question: Does Overriding paintComponent to add custom GeneralPath graphics on the JButton significantly slow down the application. Is there a way to make it more efficient?
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
drawColoredShape(g2, getText());
g2.setColor(new Color(myColor));
g2.dispose();
}
/**
* Draws Custom Shape for this button.
*/
private void drawColoredShape(Graphics2D g2, String txt) {
Shape sh = null;
case "Cat":
this.setForeground(Color.BLUE);
g2.setPaint(Color.Blue);
g2.draw(SPECIAL_SHAPE); // from General Path defined somewhere
break;
case "Dog":
break;
... etc
}
I know there's no direct replacement for java.awt.Canvas in swing, and I know I'm supposed to use a JPanel and override paintComponent, for example like so:
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.drawOval(0, 0, 100, 100);
}
And this would draw a black circle on the JPanel when it is created. The problem I have is that I want a dynamic canvas: I want to be able to draw things in response to user input, and redraw continuously, not just once when the application starts. An example would be having a moving object on a canvas, that would need to be redrawn at a rate of say 60 frames per second. How could I achieve this without using AWT components?
EDIT: what I mean is, in an actual canvas, I'd be able to arbitrarily call, say, drawOval anywhere in my code, and that would draw an oval on the canvas; is this doable with JPanel?
Store the information to be drawn (e.g. a Shape or a group of them) and call repaint() from a Swing Timer. Each time the paintComponent(..) method is called, first call the super(..) method to erase the previous drawings, then iterate the list of shapes, move them if necessary, and draw each one.
Here's one way to do it:
public class Renderer extends JComponent implements ActionListener {
private int x;
public Renderer() {
Timer timer = new Timer(1000/60, this);
timer.start();
x = 0;
}
#Override
public void paintComponent(Graphics g) {
super.paint(g);
// drawing code
g.setColor(Color.black);
g.drawOval(x, 0, 100, 100);
}
private void update() {
this.x++;
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
}
Now just add this to your component (JPanel or whatever):
comp.add(new Renderer());
I'm trying to figure out if the repaint method does something that we can't do ourselves.
I mean,how are these two versions different?
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
g2.draw(r);
}
}
and
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
repaint();
}
}
The 2nd version can result in a very risky and poor animation since it can result in repaints being called repeatedly, and is something that should never be done. If you need simple animation in a Swing GUI, use a Swing Timer to drive the animation.
i.e.,
public class MyComponent extends JComponent {
private Rectangle r = new Rectangle(0,0,20,10);
public MyComponent() {
int timerDelay = 100;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
r.translate(5, 5);
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(r);
}
}
The use of repaint() is to suggest to the JVM that the component needs to be painted, but it should never be called in a semi-recursive fashion within the paint or paintComponent method. An example of its use can be seen above. Note that you don't want to call the painting methods -- paint or paintComponent directly yourselves except under very unusual circumstances.
Also avoid calling a class Componenet since that name clashes with a key core Java class.
I've a JPanel where I'm drawing with the mouse events and after resizing, or minimizing-restoring it, it doesnt display what has to be drawn, despite a "degub" println shows me the function is being called, but for some reason, it reamains blank
I thought I should have added my drawing function in the paintComponent function but it seems not to be working right, so what I'm doing wrong or where should I place that drawElements() call?
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawElements();
}
void drawElements() {
Graphics2D g = (Graphics2D)this.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Element el : elements) {
el.draw(g);
}
}
Pass the graphics object to the drawElements method.
(Right answer provided by #rafalopez79)
I am trying to draw a fractal tree, and currently my design gleamed from other people's work is using graphics objects outside of the paint() method for recursion. Will this result in anything at all?
You can get a Graphics object (or even the backing array) from a BufferedImage and draw on that, then in paintComponent() you can just draw the image.
You need to pass the Graphics context as a parameter to your drawMethod(Graphics g), then invoke than method withing the paint method. drawMethod(g);, g being the Graphics context of the paint method
public class SomeClass {
public void drawMethod(Graphics g) {
g.drawString("Hello World", 50, 50);
}
}
public class DrawPanel extends JPanel {
SomeClass someClass = new SomeClass();
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
someClass.drawMethod(g);
}
}