I am trying to paint one Oval over another oval.
Here i use a select statement to draw in the paint component method.
import java.awt.*;
public class test extends JPanel{
public static final int OVAL = 1;
public static final int SOLID = 2;
private int type = LINE;
public test(int type){
this.type = type;
}
public void piantComponent(Graphics g){
super.paintComponent(g);
switch(type)
{
case OVAL:
g.setColor(Color.YELLOW);
g.drawOval(0,0,150,150);
break;
case SOLID:
g.setColor(Color.BLUE);
g.fillOval(0,0,50,50);
break;
}
}
}
Now in my main method i want to display a solid blue oval (SOLID) inside of a yellow oval (OVAL).
import...;
public class Main{
public static void main (String [] args){
JFrame window = new JFrame();
window.add(new test(test.OVAL));
window.add(new test(test.SOLID));
window.setSize(300,300);
window.setVisible(true);
}
}
This does not do what I want it to do at all. This only displays a oval and not a oval and a solid. I think that i am overloading the window to only display the oval. I have tried to display with a layout manager(gridlayout) but that does not display the two paintings over each other it displays the two paintings next to each other.
How do i fix this without loosing the switch statement.
window.add(new test(test.OVAL));
window.add(new test(test.SOLID));
Your problem has to do with ZOrder. To simplify, Swing paints components in the reverse order they are found on the panel. So in your case the SOLID gets painted BEFORE the OVAL. Since the OVAL is larger in size it covers the SOLID.
You can force Swing to paint the components in the more intuitive order by doing:
window.add(0, new test(test.OVAL));
window.add(0, new test(test.SOLID));
Now the last component added will also be painted last.
However a better design is to create separate Oval and Solid classes so you can specify size, location and color independently. Then you can add as many components to your panel as you want.
Painting in Swing must be done for concrete JComponent
public class test{ (use proper naming convention and to change test to Text) doesn't contains any JComponents
use JPanel or plain JComponent
painting in AWT, Swing by default never returns PreferredSize, then container has zero Dimension, required to override getPreferredSize
modification in the test class. read comments for explaination of the modifications
import java.awt.*;
public class test extends JPanel {
public static final int OVAL = 1;
public static final int SOLID = 2;
**public static final int BOTH = 3;** //instance variable to represent the
//painting of both images
private static int type;
public test(int type){
this.type = type;
}
public void paintComponent(Graphics g){
switch(type)
{
case OVAL:
g.setColor(Color.BLACK);
g.drawOval(0,0,150,150);
break;
case SOLID:
g.setColor(Color.BLUE);
g.fillOval(0,0,50,50);
break;
case BOTH: //statements to draw both figures
g.setColor(Color.BLACK);
g.drawOval(0,0,150,150);
g.setColor(Color.BLUE);
g.fillOval(0,0,50,50);
break;
}
}//paint component
}
in the main method use
window.add( new test(test.BOTH);
Why don't you just paint one right after the other? Like that:
g.setColor(Color.YELLOW);
g.drawOval(0,0,150,150);
g.setColor(Color.BLUE);
g.fillOval(0,0,50,50);
Try something like this (Just using snippets):
private ArrayList<int> ovals = new ArrayList<int>();
public test(int type) {
ovals.add(type);
}
public void piantComponent(Graphics g)
{
super.paintComponent(g);
for(int oval : ovals)
{
switch(type)
{
case OVAL:
g.setColor(Color.YELLOW);
g.drawOval(0,0,150,150);
break;
case SOLID:
g.setColor(Color.BLUE);
g.fillOval(0,0,50,50);
break;
}
}
}
This will allow you to add as many ovals as you want (although with their static positioning it will just write over them n the canvas). You could try using a boolean array and iterating through that to see if you need to paint a specific type if you only want 1 of each.
Related
Hello fellow programmers,
I've ran into a little issue in my code that I can't seem to crack. It has to do with the Jframe; Graphics area of Java. The code that I'll post below, is over a drawing method. Which purpose is to draw the "rooms" that are in a ArrayList roomList which is located in another class hence lvl. before. This off-course doesn't happen, hence the post on here.
public class LevelGUI implements Observer {
private Level lv;
private Display d;
public LevelGUI(Level level, String name) {
this.lv = level;
JFrame frame = new JFrame(name);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
d = new Display(lv, 500, 500);
frame.getContentPane().add(d);
frame.pack();
frame.setLocation(0, 0);
frame.setVisible(true);
}
private class Display extends JPanel {
public Display(Level fp, int x, int y) {
addKeyListener(new Listener());
setBackground(Color.GRAY);
setPreferredSize(new Dimension(x + 20, y + 20));
setFocusable(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
private void draw(Graphics g) {
Level lvl = new Level();
for(int i = 0; i < lvl.roomList.size(); i++) {
Room room = lvl.roomList.get(i);
g.setColor(room.floorColor);
g.drawRect(room.posX, room.posY, room.roomWidth, room.roomHeight);
}
}
}
}
To get some background info on the program. roomList is the ArrayList, and it is filled with various different sized and colored rooms. The rooms themselves are objects.
Here comes first Level class:
public class Level extends Observable {
private boolean Switch = true;
public ArrayList<Room> roomList = new ArrayList<Room>();
(...)
}
Here is the Class Room() that is used to create the rooms.
public class Room {
Color floorColor;
int roomWidth;
int roomHeight;
int posX;
int posY;
public Room(int dx, int dy, Color color) {
this.floorColor = color;
this.roomHeight = dy;
this.roomWidth = dx;
this.posY = 0;
this.posX = 0;
}
(...)
}
I've managed to locate where the problem is thought to occur, and it's the code in the for-loop. I tried switching the roomList.size() for an integer to test if it was the loop., But it wasn't. It is possible to draw a figure outside of the for-loop.
and again, the problem isn't an error message, the program simply doesn't draw the rooms that I've instructed it to draw in the method draw().
The display output looks like this:
Thanks beforehand!
Be aware that the paintComponent() method is invoked by Swing whenever the framework thinks the component needs to be rendered on screen. This usually is when the window is getting visible - initially or because some other window no longer hides the component. Such events are out of your control.
So your application should create a state and be ready to draw it anytime. Therefore you do not create state (like a level) inside the paint() or paintComponent() method. Put that elsewhere - if need be into the constructor.
Looking at you code:
As you are creating a new level inside paintComponent()/draw(), is it correct to assume that this level has no rooms associated? In that case the method is right to return without having painted anything.
If your application thinks the screen should be updated call repaint(), knowing that the paint() method will be called by the framework soon.
Hello fellow programmers!
So to be honest here, i'm not sure if the title question is correct, and you will see why.
Before i explain what i do, and why, here is the code snippet:
JPanel playerPanel = new JPanel() {
public void paint(Graphics g) {
X = 1;
Y = 1;
g.drawImage(player.getScaledInstance(player.getHeight()/2, player.getWidth()/2, Image.SCALE_DEFAULT), X, Y, null);
}
};
So this a snippet from a custom class i made, and my question would be that, you see there is an X and Y variable, i can change their values , but that changes nothing on the impact of the actual program, my first question would be that can i change the X, and Y of this JPanel's image, and if so , how can i "refresh" the actual JPanel/Image so that it looks like it moved?
Some notes:
-the X, Y are global variables
-playerPanel is inside a procedure, and a global variable
-i can access X, Y since they are global variables from outside the class
I'm having a hard time actually writing down my problem... Hopefully you understand what i would like to accomplish.
You're main problem: Don't use an anonymous inner class if you want to give the class new mutable fields. Instead, create a separate class, it can be an inner class, but it can't be anonymous, give it fields that are needed with getters and setters. Also all that Luxx recommends is correct -- override paintCompoent, call the super method, don't declare the fields within a method...
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
public class PlayerDrawingPanel extends JPanel {
private int playerX;
private int playerY;
private Player player;
public PlayerDrawingPanel(int playerX, int playerY, Player player) {
this.playerX = playerX;
this.playerY = playerY;
this.player = player;
}
public void setPlayerX(int playerX) {
this.playerX = playerX;
repaint();
}
public void setPlayerY(int playerY) {
this.playerY = playerY;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(player.getImage(), playerX, playerY, this);
}
}
There is no need to create global variables.
You can use setBounds(x, y, w, h) from Swing's JComponent to move and resize the JPanel.
Though, you have to keep in mind that a Component cannot draw outside its borders. Meaning that the Graphics object that is passed into paint(Graphics g) comes clipped and translated to fit the Component from it's parent.
So, to solve your case, you can either make your JPanel take over the whole area in which you want to draw by using setBounds() or you can you the LayeredLayout from your root panel to draw anywhere.
Let me exemplify the last solution. Consider frame to be your JFrame and playerPanel the JPanel that you overwrote the paint() method:
frame.setGlassPane(playerPanel);
frame.getGlassPane().setVisible(true);
Now your playerPanel is at the topmost layer of your application, covering the whole area. This means you can draw anywhere over anything.
I'm working on a program that add pins to a map, the pins are are subclasses to a class that draws a triangle to the map and are clickable, and if you click it shall unfold and show different things like name, text or a picture.
I've one working subclass that creates a rectangle out of the triangle and shows what the name of the place is. For this I used the drawString. But now, to my second subclass, it shall show an description over the place, and the description could be quite long and for this I can't use the drawString, because it only shows on one row, and it will clip my text..
I tried to add the description to a JTextArea, and add that one to a JScrollPane and then I tried to add the scrollpane to the rect area, but that didn't seem to work, because "The method add(JScrollPane) is undefined for the type Graphics"
Here is my super class:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
abstract public class Place extends JComponent {
private String name;
private int x,y;
boolean highlighted = false;
boolean hidden = false;
boolean showed = false;
public Place(int x, int y, String name){
setBounds(x,y,30,30);
this.name=name;
this.x=x-15;
this.y=y-30;
Dimension d = new Dimension(30,30);
setPreferredSize(d);
setMaximumSize(d);
setMinimumSize(d);
addMouseListener(new MouseLis());
}
abstract protected void show(Graphics g);
protected void paintComponent(Graphics g){
super.paintComponent(g);
// g.setColor(Color.BLACK);
if(!showed){
setBounds(x,y,30,30);
int[] xes = {0,15,30};
int[] yes = {0,30,0};
g.fillPolygon(xes, yes, 3);
} else {
show(g);
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public String getName() {
return name;
}
class MouseLis extends MouseAdapter{
#Override
public void mouseClicked(MouseEvent mev){
showed = ! showed;
repaint();
}
}
}
and here is my subclass that doesn't work..
class DescPlace extends Place{
private String Description;
private JTextArea desc = new JTextArea(Description);
public DescPlace(int x, int y, String name, String descr){
super(x,y,name);
this.Description = descr;
}
protected void show(Graphics g){
setBounds(getX(), getY(),150,200);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 150, 200);
//g.add(new JScrollPane(desc));
}
}
You can use the same JTextArea and just paint it using Graphics instance
desc.setSize(width, height); //define size
desc.paintAll(g); //paint
You can use a JLabel to do this, using it to display HTML-formatted content.
From Oracle's docs:
If you want to mix fonts or colors within the text, or if you want formatting such as multiple lines, you can use HTML. HTML formatting can be used in all Swing buttons, menu items, labels, tool tips, and tabbed panes, as well as in components such as trees and tables that use labels to render text.
Source: https://docs.oracle.com/javase/tutorial/uiswing/components/html.html
EDIT
No time to write a thousand words, so here's an example:
new JLabel("<html><p>This will</p><br /><p>appear over multiple</p><br /><p>lines</p></html>")
The same applies to JToolTip if you go down that route.
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.
I have found this class that draws circles with different colors. The color of each circle is determined according to a specific order of colors which iterates as it comes to the end (having used all colors by one time). I want to modify this on a way that grants me the potential to determine individually the color (on g.setColor) for each circle. In other words, I want to be able to deploy the color as a parameter and to invoke the method from another method in another class.
public class DrawingPane extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle rect;
for (int i = 0; i < circles.size(); i++) {
rect = circles.elementAt(i);
g.setColor(colors[(i % color_n)]);
g.fillOval(rect.x, rect.y, rect.width, rect.height);
}
}
}
If you find my question stupid I would like to let you know that what worries me is the fact that the method is inherited from JPanel and I am not sure how to override it effectively.
Your DrawingPane seems to have a list of Rectangle named circles (sic). I don't know if Rectangle is one of your classes or the standard java.awt.Rectangle.
If it's one of your class, then simply add a color attribute to this class, and get this attribute from it during your iteration.
If it's the standard java.awt.Rectangle, then introduce a Circle class, containing a Rectangle and a color, and use a list of Circle rather than a list of Rectangle.
I want to be able to deploy the color as a parameter and to invoke the method from another method in another class
Then you need to store the Color and shape as properties of a custom class.
Custom Painting Approaches shows an example of how to do this. I would use the DrawOnComponent example as a starting point. Your code will be much simpler since you don't need to handle dragging. All you need to do is create an addCircle(...) method which will take the size/location/color of the circle as parameters.
Are you looking for this ?
You are free to declare the classes MyCircle and DrawingPane in separate .Java files.
I am sure that this will give answer to " I want to be able to deploy the color as a parameter and to invoke the method from another method in another class."
public class TestingX12 {
public static void main(String args[]) {
new TestingX12();
}
public TestingX12() {
//create list of circles
List<MyCircle> circList = new ArrayList<MyCircle>();
circList.add(new MyCircle(new Rectangle(100, 20, 120, 30), Color.red));
circList.add(new MyCircle(new Rectangle(150, 50, 80, 50), Color.yellow));
circList.add(new MyCircle(new Rectangle(30, 90, 30, 110), Color.blue));
DrawingPane dp = new DrawingPane(circList);
JFrame frame = new JFrame("JToolTip Sample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(dp);
frame.setSize(400, 450);
frame.setVisible(true);
}
class MyCircle {
Rectangle rectangle;
Color color;
public MyCircle(Rectangle r, Color c) {
this.rectangle = r;
this.color = c;
}
}
public class DrawingPane extends JPanel {
List<MyCircle> circles;
public DrawingPane(List<MyCircle> circles) {
this.circles = circles;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle rect;
for (int i = 0; i < circles.size(); i++) {
rect = circles.get(i).rectangle;
g.setColor(circles.get(i).color);
g.fillOval(rect.x, rect.y, rect.width, rect.height);
System.out.println("Drawing...");
}
}
}
}
I'm not sure what you mean, but if you're trying to set the circle colors from an outside class, then make the array a property of the class with a setter and (if needed) a getter:
public class DrawingPane extends JPanel {
private Color[] colors;
public void setCircles(Color[] colors) {
this.colors = colors;
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle rect;
if (colors != null) {
for (int i = 0; i < colors.size(); i++) {
rect = circles.elementAt(i);
g.setColor(colors[(i % color_n)]);
g.fillOval(rect.x, rect.y, rect.width, rect.height);
}
}
}
}