Print Multiple Objects - java

Hi I am trying to get my program to move the shapes I create across the screen and for some reason its not working Im not sure whats happening? It has to be something small can anyone point me in the right direction.
public class MultipleObs extends JFrame {
private JPanel paintPanel;
public MultipleObs() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setMinimumSize(new Dimension(300, 300));
paintPanel = new PaintPanel();
getContentPane().add(paintPanel, BorderLayout.CENTER);
pack();
}
class PaintPanel extends JPanel implements ActionListener {
private java.util.List<Shape> shapes;
private Shape mouseOverShape=null;
int x=0, velX=2;
javax.swing.Timer tm = new javax.swing.Timer(5,this);
public PaintPanel(){
super();
shapes = new ArrayList<Shape>();
shapes.add(new Rectangle2D.Float(x,25,25,25));
shapes.add(new Ellipse2D.Float(x, 15, 60, 30));
shapes.add(new Ellipse2D.Float(x, 35, 60, 30));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (Shape s : shapes){
g2.draw(s);
}
tm.start();
}
#Override
public void actionPerformed(ActionEvent e) {
x = x+ velX;
repaint();
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MultipleObs().setVisible(true);
}
});
}
}

Two issues:
Do not ever start a Swing Timer inside of paintComponent. That method should be for painting and painting only. Instead start the Timer in the constructor.
When you create an Ellipse2D object, its position is fixed. Changing one of the variables used to create it will have no effect on the already created object.
A possible solution is to not use Ellipse2D's but instead draw ovals using Graphics#drawOval(...) inside of paintComponent and use the changing x field in that method call. If you must use Ellipse2D's, then you will need to translate them some way, perhaps by using an AffineTransform, but this way is a bit more complicated since I think that you'd have to wrap your Ellipse2D into a Path2D for this to work.
Another option: create a BufferedImage sprite, draw your complex shapes into the BufferedImage using a Graphics2D object obtained from the BufferedImage, and then draw that within paintComponent via drawImage(myImage, imageX, imageY, null), and change the imageX in your Timer.

Related

What did super.paintComponent(g) do?

So in the past few days I've tried to implement an easier version of a graph plotter.
One big problem I was confronted with was a bug that occured on repainting.
Basically I've designed my program in one class which is responsible for drawing the whole coordinate system and the given function after clicking a JButton in an other class. The other class contains the JButton which is pressed. After pressing the JButton it calls a function in the coordinate system class which repaints the picture. Both of those classes are extending JPanel.
The bug was that when I was doing the repainting on pressing the button, the button was drawn on the coordinate System and not in its original place, so in other words on the other JPanel even though I didn't change a thing about placements and stuff. Both classes were added to a JFrame which use a GridLayout.
Can anyone tell me why super.paintComponent(g); solved that bug?
Edit: Added Code
Window class
public class main {
public static void main(String[] args) throws SemanticFailureException {
int x = 800;
int y = 600;
JFrame frame = new JFrame();
frame.setLayout(new GridLayout());
frame.setTitle("Function plotter");
frame.setSize(2*x, 2*y);
Surface test = new Surface(x, y, 10);
CommandDraw test1 = new CommandDraw(x/2,y,test);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(test);
frame.add(test1);
frame.setVisible(true);
}
}
Coordinate System class: (changed drawing the coordinate system to a rectangle for simplicity, the bug still occures with only drawing a rectangle)
public class Surface extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
boolean drawFunct;
public Surface(int x1, int y1, int coordLength) {
setSize(x1,y1);
drawFunct = false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // without this the jbutton occures on the left
// create Graphics object to get more functions
Graphics2D g2 = (Graphics2D) g;
// draw Plotter
drawFunction(g2);
if (drawFunct)
g2.drawLine(0, 0, 80, 80);
}
public void drawFunction(Graphics2D g) {
g.drawRect(40, 40, 30, 30);
}
public void redraw() {
drawFunct = true;
repaint();
}
}
The Class with the JButton:
public class CommandDraw extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
JButton makeDraw;
JTextField inputPoly;
Surface surf;
public CommandDraw(int x, int y, Surface surf) {
this.surf = surf;
setSize(x,y);
setLayout(new FlowLayout());
makeDraw = new JButton("draw Function");
makeDraw.setBackground(Color.LIGHT_GRAY);
makeDraw.setFocusable(false);
makeDraw.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
surf.redraw();
}
});
add(makeDraw);
inputPoly = new JTextField("Input polynomial");
inputPoly.setHorizontalAlignment(JTextField.CENTER);
add(inputPoly);
}
}
Can anyone tell me why super.paintComponent(g); solved that bug?
Because the call to paintComponent(g) of the superclass (JPanel) will guarantee that panel will be rendered as expected before you do your paint operations. You need to make sure that your JPanel will behave, in the Graphics perspective, like any other JPanel.
A template for a customized JPanel to draw should be something like:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class MyDrawPanel extends JPanel {
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g );
// create a new graphics context based on the original one
Graphics2D g2d = (Graphics2D) g.create();
// draw whatever you want...
g2d.dispose();
}
}
EDIT
You need to call super.paintComponent(g) to tell that the JPanel paintComponent(Graphics) version should be execute before you start doind your customized things. It's something like to ask the JPanel to prepare its paint capabilities to be used. A good starting point to these kind of doubts is the documentation: https://docs.oracle.com/javase/10/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics)
The dispose() method is being called in the copy of the original Graphics. If you dispose the Graphics that is passed to the method, you may have some issues too. You could use the original Graphics to perform you painting operations, but you shouldn't, so a better practice is to make a copy of the original Graphics and dispose it after using.
Take a look here too: How does paintComponent work?

java inheritance and polymorphism

this is my first question here so please apologize if I do mistakes.
Trying to better understand oop (inheritance, polymorphism, abstractization) I have imagine the following exercise : in a JPanel with BorderLayout I add a JPanel which contains two JRadioButton and an abstract panel that extends JPanel. The abstract panel can point to 2 different concrete children ... but it doesn't work as I expected. . When I select the rectangle button on the console is printed "rectangle fires event" but the paint Component of the rectangle object is not executed and when I select the oval button on the console is printed "oval fires event" and "paint oval draw oval" which means the paint Component of the oval button is executed. Please help me to understand what do I do wrong? Why the paintComponent of the rectangle is not executed?
I repeat, my aim is to practice and understand oop, not to draw ovals and rectangles.
Here is my code
public class MyGeometry extends JPanel{
private MyShape shape;
private JPanel shapeControl = new JPanel();
private JRadioButton rbtOval = new JRadioButton("Oval");
private JRadioButton rbtRectangle = new JRadioButton("Rectangle");
MyGeometry(){
setLayout(new BorderLayout());
// setting radio buttons
ButtonGroup shapeButton = new ButtonGroup();
shapeButton.add(rbtOval);
shapeButton.add(rbtRectangle);
rbtOval.setSelected(true);
shapeControl.add(rbtOval);
shapeControl.add(rbtRectangle);
//Initialize shape to avoid null pointer exception
final MyOval oval = new MyOval();
final MyRectangle rectangle = new MyRectangle();
shape = oval;
//add components to the panel
add(shapeControl, BorderLayout.NORTH);
add(shape);
//add event handling
rbtOval.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
shape = oval;
shape.repaint();
System.out.println("Oval fires event");
}
});
rbtRectangle.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
shape = rectangle;
shape.repaint();
System.out.println("Rectangle fires event");
}
});
}
public Dimension getPreferredSize(){
return new Dimension(500, 500);
}
public abstract class MyShape extends JPanel{
public abstract String dummyString();
public void paintComponent(Graphics g){
super.paintComponent(g);
}
}
public class MyOval extends MyShape{
public String dummyString(){
return "Draw oval";
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawOval(50, 50, 100, 80);
String textOval = "paint oval " + dummyString();
System.out.println(textOval);
}
}
public class MyRectangle extends MyShape{
public String dummyString(){
return "Draw rectangle";
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(50, 50, 100, 80);
String textRectangle = "paint rectangle " + dummyString();
System.out.println(textRectangle);
}
}
}
the paint Component of the rectangle object is not executed
All you are doing is setting a field on the JPanel, you are not changing the JPanel itself. I suspect what you intended was to remove the oval and add the rectangle.
Your OOP is sound; your Swing is not.
You started off with shape set to your oval. This shape was added to your JPanel. The rectangle was never added to the JPanel. Even when you set shape = rectangle, and then tell it to draw, Swing knows that your rectangle has no parent component, and thus will never be drawn. So telling it to repaint() will not call paintComponent().

Display Two Classes Java

so I am new to designing graphics in Java I was wondering if anyone could help me here. I have two classes and I want to display both of them at the same time in a JFrame. But only one or the other get displayed.
public class Tutorial extends JPanel implements ActionListener {
Background bc = new Background();
Timer tm = new Timer(5,this);
int x =0, velX = 2;
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
tm.start();
}
#Override
public void actionPerformed(ActionEvent e) {
if(x<0 || x>550){
velX = -velX;
}
x = x+ velX;
repaint();
}
public static void main(String [] args){
Background bc = new Background();
Tutorial t = new Tutorial();
JFrame jf = new JFrame();
jf.setTitle("Tutorial");
jf.setSize(600,400);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(t);
jf.add(bc);
}
My second class
public class Background extends Canvas {
public void paint(Graphics g){
g.setColor(Color.GREEN);
g.fillRect(0,0,600,125);
g.fillRect(0,250,600,125);
g.setColor(Color.black);
g.fillRect(0,125,600,125);
}
For some reason I can only get either Background to be displayed or Tutorial?
Can anyone point me in the right direction or tell me where I am going wrong. I want to be able to display multiple things like these classes in the one window
The default layout manager of a JFrame is a BorderLayout.
By using the single-argument JFrame.add() function, you're adding both of the components to the BorderLayout.CENTER portion of your JFrame. This means that you'll only see one of the components.
The solution is to either use a different layout manager, or to add the components to different sections of your BorderLayout.
More info here: http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html

what is Alternative of Paint and repaint function?

Is there any function which can be replaced with paint() and repaint()in java.
I have a scenario.
There is a triangle (Triangle 1). when user will click in triangle another triangle (Triangle 2) will appear and 1st (Triangle 1) will be removed from screen. (coded using JFrame, paint() and repaint())
I have achieved it so far. but problem is when I minimize or change size of window with mouse the output window, it just paint again Triangle 1 rather than Triangle 2. OR Clear the whole screen if i call g2d.clearRect(0, 0, 1000, 1000);
triangle.reset();
Note: These both functions are to remove previous triangle (Triangle 1).
Is there any function that state should not be changed on minimize or when window size changed ?
or can we override repaint() or anything helping according to scenario.
Here is the Working code. Execute it, Click in triangle then minimize and see again. You will get the problem more clearly.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Triangle_shape extends JFrame implements ActionListener {
public static JButton btnSubmit = new JButton("Submit");
public Triangle_shape() {
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setLayout(new BorderLayout());
frame.add(new TrianglePanel(), BorderLayout.CENTER);
frame.add(btnSubmit, BorderLayout.PAGE_END);
frame.pack();
frame.repaint();
frame.setTitle("A Test Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
public static class TrianglePanel extends JPanel implements MouseListener {
private Polygon triangle, triangle2;
public TrianglePanel() {
//Create triangle
triangle = new Polygon();
triangle.addPoint(400, 550); //left
triangle.addPoint(600, 550); //right
triangle.addPoint(500, 350); //top
//Add mouse Listener
addMouseListener(this);
//Set size to make sure that the whole triangle is shown
setPreferredSize(new Dimension(300, 300));
}
/**
* Draws the triangle as this frame's painting
*/
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.draw(triangle);
}
//Required methods for MouseListener, though the only one you care about is click
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
/**
* Called whenever the mouse clicks. Could be replaced with setting the
* value of a JLabel, etc.
*/
public void mouseClicked(MouseEvent e) {
Graphics2D g2d = (Graphics2D) this.getGraphics();
Point p = e.getPoint();
if (triangle.contains(p)) {
System.out.println("1");
g2d.clearRect(0, 0, 1000, 1000);
triangle.reset();
g2d.setColor(Color.MAGENTA);
triangle2 = new Polygon();
triangle2.addPoint(600, 550); // left
triangle2.addPoint(700, 350); //top
triangle2.addPoint(800, 550); //right
g2d.draw(triangle2);
} else {
System.out.println("Triangle dont have point");
}
}
}
}
paint() and repaint() work fine, but you are not using them as they are designed to be used. The window system doesn't keep a persistent image of what a component looks like. It expects your component to be able to repaint its entire appearance on demand (such as when the window is resized or un-minimized).
If you grab a Graphics object with getGraphics() and draw something on the component, the stuff you draw will indeed be lost if/when the entire component needs to be repainted. So you should not do that, but instead make sure your paintComponent method has all the information it needs to completely repaint the component.
If you only want one triangle on the screen at once, do not create a separate variable triangle2. Just replace the one triangle you have by altering your mouse click handler as follows:
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
if (triangle.contains(p)) {
triangle = new Polygon();
triangle.addPoint(600, 550); // left
triangle.addPoint(700, 350); //top
triangle.addPoint(800, 550); //right
repaint();
} else {
System.out.println("Point not in triangle");
}
}
Your paintComponent method should call super.paintComponent to ensure the background is painted, but otherwise you do not need to change it:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.draw(triangle);
}
Alternatively if you are trying to keep multiple triangles on the screen, for example, to add a new one at every click, you should add them into a list of shapes which will be drawn whenever the component needs to be repainted:
private final List<Shape> shapes = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for (Shape s : shapes)
g2d.draw(s);
}
Then to control the set of shapes that is on screen, manipulate the contents of the list, and call repaint();.
For instance, to add a new shape to the ones on screen:
Polygon triangle = new Polygon();
triangle.addPoint(200, 300);
triangle.addPoint(200, 200);
triangle.addPoint(300, 200);
shapes.add(triangle);
repaint();
To remove all shapes from the screen:
shapes.clear();
repaint();
You should also make sure that you switch to the Swing thread at the start of the program, because it is not safe to interact with Swing components from the main thread. In main:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
.
.
.
frame.setVisible(true);
}
});
}
Your mouseClicked method should not create a new Triangle object. After resetting triangle just add new points to it. Also don't draw in this method but call repaint:
public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
if (triangle.contains(p)) {
System.out.println("1");
triangle.reset(); // change the current triangle
triangle.addPoint(600, 550); // new left
triangle.addPoint(700, 350); // new top
triangle.addPoint(800, 550); // new right
repaint(); // force repainting
} else {
System.out.println("Triangle dont have point");
}
}
Now if you want many triangles you should have a collection of Polygons. Like this :
public static class TrianglePanel extends JPanel implements MouseListener {
private Vector<Polygon> triangles;
public TrianglePanel() {
n = 0;
// Create an empty collection
triangles = new Vector<Polygon>();
//Create first triangle
Polygon triangle = new Polygon();
triangle.addPoint(400, 550); //left
triangle.addPoint(600, 550); //right
triangle.addPoint(500, 350); //top
// Add the triangle to the collection
triangles.add(triangle);
//Add mouse Listener
addMouseListener(this);
//Set size to make sure that the whole triangle is shown
setPreferredSize(new Dimension(300, 300));
}
/**
* Draws the triangles as this frame's painting
*/
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
for (Polygon p : triangles) // Draw all triangles in the collection
g2d.draw(p);
}
//Required methods for MouseListener, though the only one you care about is click
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
/**
* Called whenever the mouse clicks. Could be replaced with setting the
* value of a JLabel, etc.
*/
public void mouseClicked(MouseEvent e) {
Graphics2D g2d = (Graphics2D) this.getGraphics();
Point p = e.getPoint();
// Do what you want with p and the collection
// For example : remove all triangles that contain the point p
ListIterator<Polygon> li = triangles.listIterator();
while (li.hasNext()) {
if (li.next().containsâ„—) li.remove();
}
// Do what you want to update the list
// For example: Add a new triangle...
Polygon triangle = new Polygon();
triangle.addPoint(600+n, 550); // left
triangle.addPoint(700+n, 350); //top
triangle.addPoint(800+n, 550); //right
triangles.add(triangle); // add the new triangle to the list
n += 10; // next new triangle will be "moved" right
repaint();
}
private int n;
}

Timer move an object of Graphics2D over an image in JFrame? (Sunrise program example)

I'm trying to write a program to design a sunrise animation by using Timer and JFrame and JComponent. The Object of Graphics2D is the sun which is going to move over JFrame. My problem is that I'm not sure where to place the Timer and move the Graphicc2D! Here is what I have done so far to place an image in JFrame and then place the sun on that image. Please tell me where I can manage to move the sun. where should I define Timer class? In JFrame or JComponent or main class?
public class Main(){
public static void main(String[] args){
myFrame frame = new myFrame();
}
}
class myFrame extends JFrame
{
public myFrame()
{
Draw component = new Draw();
add(component);
}
}
class Draw extends JComponent
{
public Draw()
{
//Read the image here
//set the newImage
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(newImage, 0, 0, null);
g2.fill(new Ellipse2D.Double(x,y,20,20));
//For the sunrise I need to change x,y during the Timer class!!
}
}
This should do:
int x, y;
Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent evt){
// update x and y
repaint();
}
});
Don't forget to start the timer, (it's preferred to do so, in the constructor).

Categories

Resources