Here's my code:
class Ramka extends JFrame
{
public static final int SZEROKOSC = 800;
public static final int WYSOKOSC = 600;
Container powZawartosci = getContentPane();
public Ramka()
{
setSize(SZEROKOSC, WYSOKOSC);
setTitle("Siatka bryły by Paweł Mysior");
}
public void addRectangle(int startX, int startY, int sizeX)
{
drawRectangle rect = new drawRectangle(startX, startY, sizeX);
powZawartosci.add(rect);
}
class drawRectangle extends JPanel
{
private int a, startX, startY;
public drawRectangle(int startX, int startY, int a) // square
{
this.a = a;
this.startX = startX;
this.startY = startY;
}
public void paintComponent(Graphics g)
{
Rectangle2D rect = new Rectangle2D.Double(startX, startY, a, a);
Graphics2D g1 = (Graphics2D) g;
g1.draw(rect);
}
}
public class Main
{
public static void main(String[] args)
{
Ramka ramka = new Ramka();
ramka.addRectangle(200, 200, 50);
ramka.addRectangle(100, 100, 100);
ramka.addRectangle(300, 300, 150);
ramka.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ramka.setVisible(true);
}
}
What I want it to do is draw three rectangles (set aside the functionality and sense of doing so, I'm still just learning).
But it draws only the last one, starting at 300 and 300. I don't really understand the paintComponent thing...
Thanks in advance for any help,
Paul
I beleive that you are adding three JPanels on top of each other. This seems like an odd way to draw rectangles, but with this design, you need to use a LayoutManager.
Check out this link, and try to learn. The code below should do the trick though.
...
Container powZawartosci = getContentPane();
public Ramka()
{
setSize(SZEROKOSC, WYSOKOSC);
setTitle("Siatka bryły by Paweł Mysior");
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));//Only this line is inserted.
}
public void addRectangle(int startX, int startY, int sizeX)
{
drawRectangle rect = new drawRectangle(startX, startY, sizeX);
powZawartosci.add(rect);
}
...
In your JPanel derivative, you can keep track of the Rectangles that you need to draw. I am writing the code below spontanously, so check for errors first.
class RectangleDrawer extends JPanel{
ArrayList<Rectangle> rList = new ArrayList()<Rectangle>;
public void addRectangle(Rectangle rect){
rList.add(rect);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
for(int i=0; i<rList.size(); r++){
g.drawRectangle(rList.get(i));
}
}
}
The problem basically is that you're using two different levels of abstraction here.
In the first, you are adding a component to your JFrame, which is fine at some point.
You're adding your "DrawRectangle" instance, just the same way you would add a new button, a label or another panel. The problem comes when you add components in the same position. JFrame's main panel ( the content pane ) uses a "Border" layout manager that places the component in the middle if you don't add any constraint.
As a convenience, BorderLayout interprets the absence of a string specification the same as the constant CENTER
So, this line:
powZawartosci.add(rect);
Always adds your component in the "center", overriding the previous one. That's why you only saw one rectangle.
The second level of abstraction used here is painting the component yourself. This is low level and you have to tell the component who to draw each line and where.
That's fine, but if you want to draw several rectangles in the same component, you have to hold the references for each one ( using a collection like a list ) and then iterate that collection and draw them all.
Like this:
many http://img40.imageshack.us/img40/8125/capturadepantalla201001nd.png
I took your code, and changed it, to reflect what I'm saying. The final result, uses the same component, but this component in turn draws all the rectangles.
Notice also the naming/brace style, while is not mandatory it is common while programming in Java
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
class Ramka extends JFrame {
public static final int SZEROKOSC = 800;
public static final int WYSOKOSC = 600;
Container powZawartosci = getContentPane();
DrawRectangle rectangle = new DrawRectangle();
public Ramka() {
setSize(SZEROKOSC, WYSOKOSC);
setTitle("Siatka bryły by Paweł Mysior");
powZawartosci.add( new JLabel("Several rectangles are being displayed"), BorderLayout.NORTH );
powZawartosci.add(rectangle);
}
public void addRectangle(int startX, int startY, int sizeX) {
this.rectangle.addRectangle( startY, startY, sizeX );
}
}
class DrawRectangle extends JPanel {
private java.util.List<Rectangle2D> squares;
//private int a, startX, startY;
public DrawRectangle(){
squares = new ArrayList<Rectangle2D>();
}
public void addRectangle(int startX, int startY, int a) { // square
squares.add( new Rectangle2D.Double(startX, startY, a, a) ) ;
//this.a = a;
//this.startX = startX;
//this.startY = startY;
}
public void paintComponent(Graphics g) {
Graphics2D g1 = (Graphics2D) g;
for( Rectangle2D rect : squares ) {
g1.draw(rect);
}
}
}
public class Main {
public static void main(String[] args) {
Ramka ramka = new Ramka();
//ramka.addRectangle(200, 200, 50);
//ramka.addRectangle(100, 100, 100);
//ramka.addRectangle(300, 300, 150);
for( int i = 0 ; i < 20 ; i++ ){
ramka.addRectangle( i * 10 , i * 10 , i * 20 );
}
ramka.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ramka.setVisible(true);
}
}
Related
Hello i'm trying to learn Graphics in java and at the same time Classes and Objects. My goal now is to make a programm that contains different classes with Rectangles or Circles and then I want to use those Rectangles and Circles in other classes and change their parameters like size, color and position to draw some kind of Pattern.
My problem right now is that I can make a rectangle and I think I can even make a second one, but i can't change parameters of it (Color, size and position) I tried adding variables to this part of code Rect rect = new Rect(int variables); but it didn't work.
Normally I can solve easy problems like this but i really don't understand how classes and objects works in java if someone can give me some help would be great.
Here is my code
public class Main{
public static void main(String[] args ) {
Pattern.drawPattern();
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Rect extends JPanel{
public static Color myColor = Color.RED;
public static int myX = 10;
public static int myY = 10;
public static int myWidth = 200;
public static int myHeight = 200;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(myColor);
g.fillRect(myX, myY, myWidth, myHeight);
}
}
import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
public class Pattern {
public static void drawPattern() {
JFrame window = new JFrame("test");
window.setSize(1000, 800);
window.setVisible(true);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Rect rect = new Rect();
Rect rect1 = new Rect();
window.add(rect);
window.add(rect1);
Rect.myColor = Color.lightGray;
}
}
So many problems here, but main ones I can see are:
Over-use of static modifier in the Rect class. By using static fields, the Rect instances will not have their own unique state, their own color and position. Make all those fields private non-static (instance). If this causes compilation problems, fix it by not making the fields static but rather by not trying to access them from the class.
Also give those fields setter methods if you're going to want to change them outside the class. And getter methods if you want to query them
You're ignoring the fact that a JFrame's contentPane uses BorderLayout by default. This layout will cover over previously added components by anything added next. If you need multiple components within this container, use a different layout
But your main problem is that Rect should not extend JPanel, it should not be a component class but rather it should be a logical class.
Instead create one class that extends JPanel and does all the drawing, and then give it multiple Rect instances to draw within its single paintComponent method. You could use an ArrayList<Rect> for this.
Add this single drawing JPanel to the JFrame. Then there would be no need to change the JFrame's layout manager if you do this since BorderLayout would work nicely, allowing the drawing JPanel to fill the center of the JFreme.
Minor quibbles:
Avoid giving your classes names that clash with common Java core classes, such as Pattern which is frequently used in Java regular expressions analysis
Not sure why you even need the Pattern class since it doesn't do anything useful that couldn't be done in the main method.
For instance, Rect (or here named Rect2 to show it's different from your class) could look something like:
// imports here
public class Rect2 {
private Color myColor = Color.RED;
private int x = 10;
private int y = x;
private int width = 200;
private int height = width;
public Rect2() {
// default constructor
}
public Rect2(Color myColor, int x, int y, int width, int height) {
this.myColor = myColor;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
// method used to allow the rectangle to be drawn
public void draw(Graphics g) {
g.setColor(myColor);
g.fillRect(x, y, width, height);
}
public void setMyColor(Color myColor) {
this.myColor = myColor;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
// more setters and some getters if need be
}
and the drawing JPanel something like:
// imports here
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private List<Rect2> rectList = new ArrayList<>();
// ..... more code
public void addRect2(Rect2 rect) {
rectList.add(rect);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// iterate through the rectList and draw all the Rectangles
for (Rect2 rect : rectList) {
rect.draw(g);
}
}
// ...... more code
}
and it could be put into a JFrame like so....
Rect2 rectA = new Rect2();
Rect2 rectB = new Rect2();
rectB.setMyColor(Color.BLUE);
rectB.setX(300);
rectB.setY(300);
// assuming that the class's constructor allows sizing parameters
DrawingPanel drawingPanel = new DrawingPanel(1000, 800);
drawingPanel.addRect2(rectA);
drawingPanel.addRect2(rectB);
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(drawingPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
So I've been stuck on this problem for a while now and I'm desperate for help. Please help me. I've got 3 classes:
Circle is just suppose to draw a circle in the frame created by Frame with random starting position (and defind the radius).
Frame is the mainclass with methods such as addCircle(), bounce(), start(), stop(), run() (moves the circles) and quit(). This class also creates the frame in which the circles are added to.
Interfa is just for now a inteface frame where I define the radius, number of circles and Frame size.
No matter what I try I cannot add more than two circle (one is colored and one is not):
The "recursive way":
private static void addCircle(int n){
Circle[] circles = new Circle[n+10];
if (n > 0){
circles[circleAdd] = new Circle();
frame.add(circles[circleAdd]);
circleAdd = circleAdd + 1;
addCircle(n-1);
}
}
Normal itterative way
private static void addCircles(int n){
ArrayList<Circle> circles = new ArrayList<Circle>();
for(int i = 0; i<=n;i++){
circles.add(new Circle());
frame.add(circles.get(i));
}
}
This is how I create my Frame:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public Class Frame{
private static JFrame frame;
private static int circleAdd = 0;
private static JPanel fra;
public static void mainFrame(){
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
fra = new JPanel();
frame.add(fra);
...
//addCircle and addCircles
...
public static void main..
}
}
This is my circle:
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class Circle extends JPanel{
private Random random = new Random();
public void paint(Graphics g){
int randX = random.nextInt(250)+50;
int randY = random.nextInt(250)+50;
g.drawOval(randX,randY,50,50);
g.setColor(Color.ORANGE);
g.fillOval(100,100,50,50);
}
}
I would suggest that your general approach is wrong. Instead of using a JPanel as the element, you should have a JPanel capable of painting any number of "circles". The Graphics2D API is capable of drawing complex shapes (including ovals).
The main issues I can see are:
JFrame by default is using a BorderLayout, this only allows a single component to be placed in each of the five available positions
Layout managers rely on the preferred/minimum/maximumSize hints to make determinations about the size of the components. They are also responsible for deciding on where the component should be placed. In your current implementation, this would mean that it's possible for you to paint beyond the visible range of the component
Overriding paint is not recommend, and failing to call super.paint could cause a number of unexpected and difficult to diagnose issues
Painting can occur at any time, so using random values in the paint method will cause the UI to constantly change
Instead, you could define your own Circle class which takes the location and size you want and simply acts as a container
public class Circle {
private int x;
private int y;
private int radius;
private Ellipse2D shape;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.shape = new Ellipse2D.Double(x, y, radius * 2, radius * 2);
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getRadius() {
return radius;
}
public Rectangle getBounds() {
return shape.getBounds();
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.ORANGE);
g2d.fill(shape);
}
}
This is simply a container class, it represents the information need to generate the desired outcome. It has a convince method which is capable of then painting the shape itself.
You would then need to create a List of these shapes and paint them to your component
public class TestPane extends JPanel {
private List<Circle> circles = new ArrayList<>(10);
private Dimension size;
public TestPane() {
Random random = new Random();
int maxX = 0;
int maxY = 0;
for (int index = 0; index < 10; index++) {
int randX = random.nextInt(250) + 50;
int randY = random.nextInt(250) + 50;
circles.add(new Circle(randX, randY, 25));
maxX = Math.max(maxX, randX + 50);
maxY = Math.max(maxY, randY + 50);
}
size = new Dimension(maxX, maxY);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Circle circle : circles) {
Graphics2D g2d = (Graphics2D) g.create();
circle.paint(g2d);
g2d.dispose();
}
}
}
One of the things you seem to lack understanding in is how painting actually works in Swing.
Start by having a look at Performing Custom Painting and Painting in AWT and Swing for more details.
A deeper understanding of how layout managers and the component hierarchy work also wouldn't hurt
I've written an app that custom draws everything inside paint() based on fixed pixel positions. Then I disabled resize of the frame so its always visible.
However, now I would like to be able to resize it but I dont want to change my drawling code. I was hoping I could grab the 300x300 square of the Graphics g object and resize it to the JFrame current size after all of my drawling code, but I have no idea what I'm doing.
Here sample code. In this I want the 100x100 square to remain in the middle, proportionate to the resized JFrame:
package DrawAndScale;
import java.awt.Color;
import java.awt.Graphics;
public class DASFrame extends javax.swing.JFrame {
public DASFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
this.setSize(300, 300);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new DASFrame().setVisible(true);
}
});
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLACK);
g.fill3DRect(100, 100, 100, 100, true);
}
}
Thanks.
Assuming you rename your method that paints for 300x300 as paint300, define a buffered image:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300); // empty image
paint300(bufferImage.getGraphics()); // fill the image
g.drawImage(bufferImage, 0, 0, null); // send the image to graphics device
}
Above is when you want to draw at full size (300x300).
If your window is resized:
#Override public void paint(Graphics g) {
Image bufferImage = createImage(300, 300);
paint300(bufferImage.getGraphics());
int width = getWidth();
int height = getHeight();
CropImageFilter crop =
new CropImageFilter((300 - width)/2, (300 - height)/2 , width, height);
FilteredImageSource fis = new FilteredImageSource(bufferImage, crop);
Image croppedImage = createImage(fis);
g.drawImage(croppedImage, 0, 0, null);
}
The new classes are from from java.awt.image.*.
I didn't test this code. It's just to send you in the right direction.
if you want to painting Custom paint then look for JLabel or JPanel and including Icon/ImageIcon inside, simple example about that
import java.awt.*;
import javax.swing.*;
public class MainComponentPaint extends JFrame {
private static final long serialVersionUID = 1L;
public MainComponentPaint() {
setTitle("Customize Preffered Size Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponent());
pack();
setMinimumSize(getSize());
setPreferredSize(getSize());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setVisible(true);
}
});
}
public static void main(String[] args) {
MainComponentPaint main = new MainComponentPaint();
main.display();
}
}
class CustomComponent extends JComponent {
private static final long serialVersionUID = 1L;
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
for (int i = 0; i < Math.max(w, h); i += 20) {
g.drawLine(i, 0, i, h);
g.drawLine(0, i, w, i);
}
}
}
Not an expert, but you could just scale the Graphics2D object (the passed Graphics is in fact a Graphics2D instance), where the x and y ratios are the ratios of the fixed size you chose to draw and the actual size of the frame.
See http://download.oracle.com/javase/6/docs/api/java/awt/Graphics2D.html#scale%28double,%20double%29
You could do this with some math.
public void paint(Graphics g){
int height = 100;
int width = 100;
int x = (this.getWidth() / 2) - (width / 2);
int y = (this.getHeight() / 2) - (height / 2);
g.setColor(Color.BLACK);
g.fill3DRect(x, y, width, height, true);
}
Or if you wanted to keep the width and height of the box with the same proportion, use int width = this.getWidth() / 3; and int height = this.getHeight() / 3.
The other option is to use Graphics2D.scale(), as JB pointed out, the passed Graphics object is actually a Graphics2D object.
I have a problem to create circle whenever I like to call the paint function it will draw me another circle. Here is my code:
import java.awt.*;
import javax.swing.*;
public class MyOnto extends JFrame
{
int weight = 960;
int heigh = 960;
int x = 200;
int y = 100;
//Graphics p;
private static MyOnto my = new MyOnto();
public MyOnto()
{
setTitle("My Ontology");
setSize(weight, heigh);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
g.setColor(Color.black);
drawing(g,x,y,100,50); //g, x ,y, w, h of circle
}
public void drawing(Graphics g, int x, int y, int w, int h)
{
g.drawOval(x,y,w,h);
g.drawString("Helo", x+25,y+20);
x = x + 100;
y = y + 100;
}
public static void main(String[] args)
{
//my = new MyOnto();
my.paint(null);
my.paint(null); //try to print one more circle
}
}
The output is always just one circle. How can I make it like a function call whenever I want to draw extra one circle it will just a simple call a function?
Don't override paint() on a JFrame.
Custom painting is done by override paintComponent(...) on a JPanel and then you add the panel to the frame.
it will draw me another circle.
There are two common approaches:
Keep a List of circles to draw
Draw the circles on a BufferedImage
Check out Custom Painting Approaches for working examples of both approaches.
My application performs data visualization using gray-scale "heat map". Above it I need to paint time axis in yellow color. It looks good on black background, but becomes invisible on white background (see attached image). How to make it visible regardless of background?
Here is how I paint the timestamps:
g.setColor(Color.yellow);
g.drawString("12:43:15", x, y);
where g is java.awt.Graphics object
What about XOR'ing your Color. For e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
#SuppressWarnings("serial")
public class XorEg extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W / 4;
private static final float SIZE = 24f;
private String text = "Hello world, how's it going? ";
public XorEg() {
setFont(getFont().deriveFont(SIZE));
for (int i = 0; i < 2; i++) {
text += text;
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
int x = 0;
int y = 0;
int width = getWidth() / 2;
int height = getHeight();
g.fillRect(x, y, width, height);
g.setColor(Color.white);
x = width;
g.fillRect(x, y, width, height);
g.setXORMode(Color.blue);
g.drawString(text, 10, PREF_H / 2);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
XorEg mainPanel = new XorEg();
JFrame frame = new JFrame("XorEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
which shows:
You could "outline" the text (in black for example)
For example...
As demonstrated in Assigning a image to a String
Put a rectangle behind the text, and paint it a dark translucent color.
E.G. as seen in this answer (OK that is 'dark on light' as opposed to 'light on dark' but ..Batteries Not Included).
Here is another example that uses the same 'outline' approach as mentioned by #MadProgrammer.
Use another color that works good with black and white or detect lower color and change text color based on that, which is a better approach