Painted content invisible while resizing in Java - java

Please note I haven't tested this on a Windows-machine only on a Mac-machine. I'm not so sure whether this also occurs on a Windows-machine...
When I resize my Java-application the content is invisible. I already found a way to fix it after resizing it, but not while the user is resizing the window.
I'm not using Swing or something because it makes my binary so slow (in my opinion).
The structure is like this:
Frame My main-window
Container Content view of main-window
Container-based subviews that including the paint(Graphics g)-method
I've added all listeners to My main-window and now I'm able to redraw the Content-view after resizing the window.
public void componentResized(ComponentEvent e) {
this.contentView.paint(this.contentView.getGraphics());
}
I am beware of the fact using the paint(getGraphics())-method isn't a really good way to do this, but since the repaint()-method doesn't do anything at all, it's the only working possibility.
While resizing, all painted content becomes invisible. However, when I add a Button-instance to my Content-view and resize my Main-window, the button doesn't get invisible.
I am able to trace the 'live'-resize event:
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
}
When I start resizing this method is not being called.
While resizing it generates "Live-resize" in my log every single pixel I resize the window.
When I stop resizing this method is not being called, the componentResized-method does.
When I add my repaint-method (or the official repaint-method) to the 'live'-resize event like this, I still get the output, however, it's not repainting or something
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
this.contentView.paint(this.contentView.getGraphics());
}
Or
public void componentMoved(ComponentEvent e) {
System.out.println("Live-resize");
this.contentView.repaint();
}
When I minimize my application to the dock and maximize the application again, the same thing happens, I guess that the same code is needed to fix this.
I'm not using Graphics2D or something, just Graphics.
Could you please explain me how I can repaint the views?
Thanks in advance,
Tim

For reference, here is the same program using Swing. Because JPanel is double buffered, it doesn't flicker as the mouse is released after resizing.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;
public class SwingPaint {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new CirclePanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static class CirclePanel extends JPanel {
private static final Random r = new Random();
public CirclePanel() {
this.setPreferredSize(new Dimension(320, 240));
this.setForeground(new Color(r.nextInt()));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
CirclePanel.this.update();
}
});
}
public void update() {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = this.getSize();
int d = Math.min(size.width, size.height) - 10;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
}

I'm more familiar with Swing, but the article Painting in AWT and Swing distinguishes between system- and application-triggered painting. The example below shows how the system invokes paint() as the window is resized, while the application invokes repaint(), which calls update(), in response to a mouse event. The behavior is cross-platform.
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class AWTPaint {
public static void main(String[] args) {
Frame frame = new Frame();
frame.add(new CirclePanel());
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
private static class CirclePanel extends Panel {
private static final Random r = new Random();
public CirclePanel() {
this.setPreferredSize(new Dimension(320, 240));
this.setForeground(new Color(r.nextInt()));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
CirclePanel.this.repaint();
}
});
}
#Override
public void update(Graphics g) {
this.setForeground(new Color(r.nextInt()));
}
#Override
public void paint(Graphics g) {
Dimension size = this.getSize();
int d = Math.min(size.width, size.height) - 10;
int x = (size.width - d) / 2;
int y = (size.height - d) / 2;
g.fillOval(x, y, d, d);
g.setColor(Color.blue);
g.drawOval(x, y, d, d);
}
}
}

Okay, I finally fixed it.
Instead of redrawing it every time in the paint(Graphics g)-method, you need to buffer the output and only redraw that image (I kinda hoped Java would be already doing that, just like Obj-C).
public BufferedImage buffer;
public void redraw() {
buffer = new BufferedImage(
200, // height
300, // width
BufferedImage.TYPE_4BYTE_ABGR); // ABGR = RGBA, 4-byte (r, g, b, a) per pixel
Graphics g = buffer.getGraphics();
// do your drawing here
if (this.getGraphics()) {
// 'this' is already shown, so it needs a redraw
this.paint(this.getGraphics()); // little hack
}
}
public void update(Graphics g) {
this.paint(g);
}
public void paint(Graphics g) {
g.drawImage(buffer, 0, 0, this);
}
Now, when you minimize the window and maximize it again, the paintings remain. Only, the window's flickering now for .1-second or so, but I don't really care about that.

Related

Java Swing Graphics and Events

I am trying to make a simple game which displays circles on a frame and when clicked the circle should disappear. I am learning how Java Swing works and managed to draw a circle (Wow such an achievement) and figured out how events work. I added an mouseListener to the circle and when clicked for now I want a to get a console log that it has been clicked but the end result is not as expected. No matter where I click I always get the "click" console log. When I try to add a listener to a JButton for example I get the end result. Are events different for graphics?
import javax.swing.*;
import javax.swing.event.MouseInputListener;
import java.awt.*;
import java.awt.event.*;
import java.sql.SQLOutput;
public class CirclePop {
JFrame frame;
Circle circle;
public static void main(String[] args) {
CirclePop circlePop = new CirclePop();
circlePop.drawFrame();
}
public void drawFrame() {
frame = new JFrame();
circle = new Circle();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circle);
circle.addMouseListener(new Click());
frame.setSize(300, 300);
frame.setVisible(true);
}
class Click implements MouseListener {
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
}
import java.awt.*;
import javax.swing.*;
class Circle extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillOval(150, 140, 30, 30);
}
}
First of all, you may want to extend MouseAdapter instead of implementing MouseListener. This way you don't have "implement" all these empty methods.
Then, in your mousePressed method you just have to calculate if the click happened inside the circle. This is basically just Pythagoras:
static class ClickListener extends MouseAdapter {
private final Circle circle;
public ClickListener(Circle circle) {
this.circle = circle;
}
#Override
public void mousePressed(MouseEvent e) {
int centerX = circle.getCenterX();
int centerY = circle.getCenterY();
int radius = circle.getRadius();
int clickX = e.getX();
int clickY = e.getY();
// inside circle: (clickX - centerX)^2 + (clickY - centerY)^2 < radius^2
double xSquare = Math.pow(clickX - centerX, 2);
double ySquare = Math.pow(clickY - centerY, 2);
if (xSquare + ySquare < radius * radius) {
System.out.println("pressed");
}
}
}
I've added some fields to Circle class to get access to the properties you need for the calculation:
class Circle extends JPanel {
private final int radius = 30;
private final int centerX = 150;
private final int centerY = 140;
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillOval(centerX, centerY, radius, radius);
}
// getter, etc.
}
You have to implement the MouseListener interface indeed, and after a mouse click, you have to check whether the mouse position is contained in the region of your circle. You could do this manually, by comparing coordinates, but this could be a bit too much work. I think it's easier to rather create a Shape object(Infact this is a good time to learn about it since you're just starting out) that you fill with the respective color, and then just check whether the circle contains the mouse position.
Also, check out the Shape class docs when you've got some spare time.
I've gone ahead and made changes to your code, it now uses an instance of Shape class to create a circle.
Also, instead of implementing the MouseListener interface, I recommend extending MouseAdapter since you're not actually providing any meaningful implementation to any method of the interface except the mousePressed() method.
Lastly, notice the shape.contains(event.getPoint()) in the mousePressed() method, that is what does the trick for checking the coordinates.
The rest of the code should be familiar.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
public class CirclePop {
JFrame frame;
Circle circle;
public static void main(String[] args) {
CirclePop circlePop = new CirclePop();
circlePop.drawFrame();
}
public void drawFrame() {
frame = new JFrame();
circle = new Circle();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circle);
circle.addMouseListener(new Click());
frame.setSize(300, 300);
frame.setVisible(true);
}
class Click extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (circle.shape.contains(e.getPoint())) {
System.out.println("Pressed");
}
}
}
}
class Circle extends JPanel {
Shape shape;
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
shape = new Ellipse2D.Double(150, 140, 30, 30);
g2.setColor(Color.red);
g2.fill(shape);
}
}
Okay, so, this isn't going to be short
Let's start with ....
frame = new JFrame();
circle = new Circle();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circle);
circle.addMouseListener(new Click());
frame.setSize(300, 300);
frame.setVisible(true);
Okay, seems simple enough, but, one thing you've missed is the fact that JFrame, by default, uses a BorderLayout - this means, it will make the child component (and the centre/default position) fill all the available space of the frames viewable space
You can see this if you do something like...
frame = new JFrame();
circle = new Circle();
circle.setBackground(Color.RED);
You will now see that the Circle component occupies the entire frame, so when you click on it, you're clicking the Circle component itself.
This isn't bad, but, you might want to change tact a little. Instead of adding the MouseListener independently of the Circle, have the Circle component make use of its own MouseListener, for example...
class Circle extends JPanel {
public Circle() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
// More to come...
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(150, 140, 30, 30);
}
}
This means you get to control much of the logic internally to the class, makes it easier to access some of the more critical information without needing to make a bunch of, potentially, dangerous casts.
So, now we just need to add the logic in to determine if the mouse was clicked within the desirable location or not...
public void mouseClicked(MouseEvent e) {
Point point = e.getPoint();
if (point.x >= 150 && point.x <= 150 + 30 && point.y >= 140 && point.y <= 140 + 30) {
System.out.println("You clicked me :(");
}
}
Okay, that's ... basic
We can simplify it a little and make use of the available functionality within the wider API by making use of the "shapes" API, for example...
class Circle extends JPanel {
private Ellipse2D dot = new Ellipse2D.Double(150, 140, 30, 30);
public Circle() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Point point = e.getPoint();
if (dot.contains(point)) {
System.out.println("You clicked me :(");
}
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.red);
g2d.fill(dot);
g2d.dispose();
}
}
The benefit of this, apart from contains, is we can change the position of the shape relatively easily and our if statement contains to work 🎉
I do, highly, recommend also having a look at
Performing Custom Painting
Painting in AWT and Swing
2D Graphics Trail
Working with Geometry

java decrease time between mouseMotionListener intervals

I want to create a simple drawing programm in java which currently only draws a line using Graphics.fillOval() and a mouseMotionListener(). The problem is, that if you move the mouse quickly the line gets less precise and the ovals (circles in this case) spread apart.
Here is the code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Drawing
{
private JFrame window;
private Graphics g;
public Drawing()
{
window=new JFrame();
window.setTitle("Paint_window");
window.setSize(1000,700);
window.setVisible(true);
window.setDefaultCloseOperation(window.EXIT_ON_CLOSE);
g=window.getGraphics();
window.addMouseMotionListener(new MouseMotionAdapter()
{
public void mouseDragged(MouseEvent e)
{
if(SwingUtilities.isLeftMouseButton(e)
{
g.fillOval((int)e.getX(),(int)e.getY(),10,10);
}
}
});
}
}
Is there a way of improving this or a better way to this?
g=window.getGraphics();
First of all you should not be using getGraphics() of a component. Any painting you do will only be temporary and will be erased the first time Swing determines the component needs to be repainted. In you above example just try resizing the frame to see this.
The proper way to do custom painting is to override the paintComponent(...) method of a JPanel and add the panel to the frame. See Custom Painting for more information.
The problem is, that if you move the mouse quickly the line gets less precise and the ovals (circles in this case) spread apart
You will not be able to have an event generated for every pixel the mouse moves.
Instead you need to be able to "draw a line" between consecutive points generated as you drag the mouse.
So you need to store each point in an ArrayList and in the custom painting code iterate through all the points and draw a line.
A basic example to get you started:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DrawingPanel extends JPanel
{
private ArrayList<ArrayList<Point>> previous = new ArrayList<ArrayList<Point>>();
private ArrayList<Point> current = new ArrayList<Point>();
private BasicStroke basicStroke;
public DrawingPanel(int strokeSize)
{
basicStroke = new BasicStroke(strokeSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
MouseAdapter ma = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
}
#Override
public void mouseDragged(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
if (current.size() > 1)
{
previous.add( current );
}
current = new ArrayList<Point>();
}
};
addMouseMotionListener( ma );
addMouseListener( ma );
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke( basicStroke );
// Paint lines from previous drags
for (int i = 0; i < previous.size(); i++)
{
drawLines(g, previous.get(i));
}
// Paint line from current drag
drawLines(g, current);
}
private void drawLines(Graphics g, ArrayList<Point> points)
{
for (int i = 0; i < points.size() - 1; i++)
{
int x = (int) points.get(i).getX();
int y = (int) points.get(i).getY();
int x2 = (int) points.get(i + 1).getX();
int y2 = (int) points.get(i + 1).getY();
g.drawLine(x, y, x2, y2);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(15));
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
Using the above approach you will redraw the lines every time the component is repainted.
Another approach is to draw to a BufferedImage and then paint the BufferedImage on the panel. You can check out Custom Painting Approaches for an example of this approach.

Object class isn't painting to screen

I am currently working on a small coding project and I've run into an issue. I've looked over my past work and I can't seem to figure out why this program wont call the paint method. Currently I'm just trying to draw a circle to the frame.
The following creates the window and the object class for the simple circle I'm trying to draw.
public class Main {
public static void main(String[] args) {
final int WIDTH = 700, HEIGHT = 900;
JFrame frame = new JFrame("Physics Demo");
JPanel content = new JPanel();
content.setLayout(new GridLayout(1, 0, 0, 0));
Character ball = new Character(WIDTH, HEIGHT);
Timer changeFrame = new Timer (100, ball);
frameSetup(frame, content, WIDTH, HEIGHT, ball, changeFrame);
}
public static void frameSetup(JFrame frame, JPanel content, int WIDTH, int HEIGHT, Character ball, Timer changeFrame){
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(content);
content.add(ball);
frame.addKeyListener(ball);
frame.setPreferredSize(new Dimension(WIDTH, HEIGHT));
frame.setResizable(false);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
changeFrame.start();
}
}
The class below is the object class, when I run the program I get a response from the console. Character triggers once (as it should) and the actionPreformed method runs on loop with the timer. For some reason it doesn't run the paint class.
public class Character extends JPanel implements ActionListener, KeyListener{
/* Identify the Objects values and physics,
* Characters weight, size and properties are below.
*
*/
private static final long serialVersionUID = 1L;
final int characterRadius = 30;
final double characterWeight = 0.5;
int characterY, characterX;
boolean bouncy;
public Character(int WIDTH, int HEIGHT){
System.out.println("Character called upon... " + WIDTH);
}
public void characterObject(Graphics g, int WIDTH, int HEIGHT){
super.paint(g);
System.out.println("characterObject graphics called upon... " + WIDTH);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
/*
* Ball does not have any player interactions
*/
#Override
public void keyPressed(KeyEvent buttonPressed) {
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
//******************************************
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("actionPreformed called upon...");
repaint();
}
}
I've been doing trial and error for a while now and I can't seem to figure it out so I'm using this as a last resort.
I can supply more information if needed.
Why are you calling super.paint from characterObject? This is not how custom painting works. You don't control the painting process, the API does
You need to override one of the methods called when the API want's the component to be repainted. As a general recommendation, this would the paintComponent method, for example
public class Character extends JPanel implements ActionListener, KeyListener {
/* Identify the Objects values and physics,
* Characters weight, size and properties are below.
*
*/
private static final long serialVersionUID = 1L;
final int characterRadius = 30;
final double characterWeight = 0.5;
int characterY, characterX;
boolean bouncy;
public Character(int WIDTH, int HEIGHT) {
System.out.println("Character called upon... " + WIDTH);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
System.out.println("characterObject graphics called upon... " + WIDTH);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}
/*
* Ball does not have any player interactions
*/
#Override
public void keyPressed(KeyEvent buttonPressed) {
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
}
//******************************************
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("actionPreformed called upon...");
repaint();
}
}
I would recommend having a read of Performing Custom Painting and Painting in Swing for more details about how painting actually works in Swing.
I'd also recommend having a look at How to use Key Bindings as a replacement for KeyListener, which will address you next obvious issue
You may also want to have a read of Java Coding Conventions, it will make it easier for other people to read your code and easier for you to read others.
You're passing the width and height to the Character constructor, but are ignoring them, I'd suggest you're going to need to assign those values to instance fields and use them within the paintComponent method
You should not call paint directly. It is called from the framework whenever repaint is needed. To force repaint just call 'repaint()'.
In case you call it from a timer you might need to put the call into the EDT that means the EventDispatchThread:
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
ball.repaint();
}
});
Oh and you really should override the paint method:
#Override
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.BLUE);
g.fillOval(350, 450, characterRadius, characterRadius);
}

JPanel won't paint correctly

I am quite new to Java and Swing, and this is also my first post so sorry if it doesn't make too much sense.
What I am trying to do is when I click on a JPanel, I want it to add a circle where I click. At the moment, all that seems to happen is when I click, a small grey square appears inside the JPanel I want to add to, but I can't seem to find any way of making it draw as a circle.
I have a class that extends JPanel called "Ball" which is what is being added when I click.
At the moment, I am not too worried about it being in the correct location, just for it to draw the ball correctly.
Below is the code for my "Ball" class:
package paintsliders;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
class Ball extends JPanel{
private int x,y,w,h;
//I will use this constructor to put the ball in the correct location later.
Ball(){
/*this.w = 100;
this.h = 100;
this.x = 200;
this.y = 200;*/
}
//draw the ball
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(200,200,10,10);
g.setColor(Color.RED);
}
}
I can kind of guess that it is something to do with the paintComponent method, but everywhere I have looked doesn't seem to have a solution for me.
Any help would be great, thanks!
The Graphcis context has already been translated to meet the x/y location that the component should appear within it's parent container, this means that the top, left corner of the Graphics context within the paintComponent method is actually 0x0.
You need to define some size for the ball, you're painting at 10x10, which would suggest that your ball component should return a preferredSize of 10x10
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
You will become responsible for providing appropriate layout details to the ball when it's added to the parent container...
public void mouseClicked(MouseEvent evt) {
Point p = evt.getPoint();
Ball ball = new Ball();
Dimension size = ball.getPreferredSize();
ball.setBounds(new Rectangle(p, size));
add(ball);
}
This, of course, assumes you have a null layout set for the parent container
UPDATED
Something like...
public class PaintBalls {
public static void main(String[] args) {
new PaintBalls();
}
public PaintBalls() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Board());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Board extends JPanel {
public Board() {
setLayout(null);
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
Ball ball = new Ball();
Dimension size = ball.getPreferredSize();
p.x -= size.width / 2;
p.y -= size.height / 2;
ball.setBounds(new Rectangle(p, size));
add(ball);
repaint();
}
});
}
}
public class Ball extends JPanel {
public Ball() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(10, 10);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fillOval(0, 0, 10, 10);
g2d.dispose();
}
}
}
You probably have a main JPanel where you click.
I would rather design the main panel to handle the mouse click and the Ball class to be a simple Object that defines a drawBall(Graphics g, int x, int y) method that knows how to paint a Ball. This would be called by the paintComponent() method in the main panel. In the main panel, you handle the mouse click, create an object of type Ball and call repaint(). Inside the paintComponent() you call ball.drawBall().

Paint a JPanel to a BufferedImage or Print it without rendering it to screen first

Picture this... A program GUI JFrame that is split in 2, EAST and WEST. The first JPanel is just a print preview screen. The EAST side of the JFrame is where the user can create a 1 2 or 3 size image. The user clicks the "Add" button and the defined image on the right goes to the panel on the left. So if the user clicks "Add" 3 times with different size images, then the panel uses FlowLayout to organize the added panel images added on the left.
When you run this code, you can see a sorta idea of what I want. Really what would be nice is to create all this off-screen and call it MainPanel. Then have printPreview extend MainPanel and scale it down for screen view. And have the Printable method paint the MainPanel into the print method which would be a correct size.
So my question...
-Can you copy or paint a JPanel before it is rendered on the screen?
-Is there a better way to do what I want, I FlowLayout solves what I want amazingly, so a JPanel seems to be the answer unless there is something I do not know of.
Ok now that that is pictured. I have built some code that is about as SSCCE as I can get.
Guys I have tried asking this question at New To Java forums and they just do not respond, I am not double posting on purpose, I completely rewrote this from scratch.
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.print.*;
import java.awt.event.*;
public class PrintGrid extends JFrame {
Paper paper = new Paper();
PrintGrid() {
super("Check out this grid panel");
setSize(672, 750);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(paper);
setVisible(true);
} // end PrintGrid constructor
// **************************
// ****** PAPER CLASS *******
// **************************
private class Paper extends JPanel {
final int PAPER_X = 672, PAPER_Y = 975, UNIT = 12, DPI = 72;
X1 x1a = new X1(), x1b = new X1(), x1c = new X1();
X2 x2a = new X2(), x2b = new X2(), x2c = new X2();
X3 x3a = new X3(), x3b = new X3(), x3c = new X3();
Paper() {
setPreferredSize(new Dimension(PAPER_X, PAPER_Y));
setBackground(Color.GRAY);
setLayout(new FlowLayout(FlowLayout.LEADING));
//Users will manually add different sizes to this sheet.
add(x1a);
add(x2a);
add(x3a);
add(x1b);
add(x1c);
add(x2b);
add(x3b);
}
// ******* Parent Class for GridUnits *******
abstract class GridUnit extends JPanel {
MouseListen ml = new MouseListen();
float alpha = 1.0f;
GridUnit() {
this.addMouseListener(ml);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.BLACK);
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(makeComposite(alpha));
g.setColor(Color.WHITE);
g.drawRect(0, 0, this.getWidth()-1, this.getHeight()-1);
g.setColor(Color.darkGray);
g.fillRect(15, 15, this.getWidth()-30, this.getHeight()-30);
} // end paintComponent.
private AlphaComposite makeComposite(float alpha) {
int type = AlphaComposite.SRC_OVER;
return(AlphaComposite.getInstance(type, alpha));
}
void click() {
setVisible(false);
}
void entered() {
alpha = 0.8f;
repaint();
}
void exited() {
alpha = 1.0f;
repaint();
}
class MouseListen extends MouseAdapter {
public void mouseEntered(MouseEvent event) {
entered();
}
public void mouseExited(MouseEvent event) {
exited();
}
public void mousePressed(MouseEvent event) {
click();
}
}
} // end GridUnit class
class X1 extends GridUnit {
X1() {
setPreferredSize(new Dimension(UNIT*13, UNIT*18));
}
} // end X1 Class
class X2 extends GridUnit {
X2() {
setPreferredSize(new Dimension(UNIT*26, UNIT*18));
}
} // end X1 Class
class X3 extends GridUnit {
X3() {
setPreferredSize(new Dimension(UNIT*39, UNIT*18));
}
} // end X1 Class
} // end Paper class.
public static void main(String[] args) {
new PrintGrid();
} // end main method.
} // end PrintGrid class.
It's quite trivial to paint any Java component to an offscreen image, from which you can do as you please, including copying a portion or scaled image to a final target.
Subclass JComponent and override void paintComponent(Graphics g). Paint to a BufferedImage, then copy the image to the target component. Off the top of my head, something like:
void paintComponent(Graphics g) {
BufferedImage img=new BufferedImage(getWidth(),getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics2D gph=(Graphics2D)img.getGraphics();
// paint to gph here
gph.dispose();
g.drawImage(img); // paints the contents of img to the component's graphics context.
}

Categories

Resources