I'm trying to write a function that will draw the pictured smiley face
I need to pass in parameters for location and size of the smiley face. For example, if the location passed in was (0,0) and size 100, then the yellow circle would be a width and height of 100, the eyes are black circles located at (30,30) and (60,30) with a width and height of 5, and the mouth is a black semi-circle inset by 10 pixels. Lastly I need to call the smiley function from paintComponent then use it as a stamp to draw at least 5 different smileys in different locations and sizes.
I know I need to create a equation that will do all of this but am unaware as to how to accomplish this because when I change the x and y coordinates the eyes of the smiley face are not in the proper position as they were before the change. anything helps, thanks.
public class GuiApp extends JFrame
{
private DrawingPanel panel;
public class DrawingPanel extends JPanel
{
public DrawingPanel()
{
this.setBackground(Color.RED);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// drawFlower(g, 20, 10, 10);
drawSmiley(g, 25, 25, 100);
}
}
public GuiApp()
{
setBounds(100, 100, 450, 300);//x,y,w,h of window
panel = new DrawingPanel();
this.setContentPane(panel);
}
public static void main(String [] args)
{
GuiApp f = new GuiApp();
f.setTitle("Smiley");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
public void drawFlower(Graphics g,int x,int y,int s)
{
g.drawOval(60, 60, 200, 200);
g.fillOval(90, 120, 50, 20);
g.fillOval(190, 120, 50, 20);
g.drawArc(110, 130, 95, 95, 0, -180);
}
public void drawSmiley(Graphics g, int x, int y, int s)
{
g.setColor((Color.YELLOW));
g.fillOval(x-s/2, y-s/2, s, s);
g.setColor((Color.blue));
g.fillOval((int)(1+(x-s/2)+(x-s/2)*.3), (int)(1+(y-s/2)+(y-s/2)*.3),(int)(s*.10),(int)(s*.10));
g.fillOval((int)(1+(x-s/2)+(x-s/2)*.9), (int)(1+(y-s/2)+(y-s/2)*.3),(int)(s*.10),(int)(s*.10));
g.drawArc((int)(1+(x-s/2)+(x-s/2)*.1), (int)(1+(y-s/2)-(y-s/2)*.15), (int)(s*.9), (int)(s), 0, -180);
}
}
Remember, most graphical operations occur from the top/left corner, so when drawing something like an oval, the x/y is the top/left corner of the oval and it will expand right/down.
So, this means that when drawing the right eye, for example, you will not only need to calculate the x/y position as a factor of the diameter of the circle, but you may need to subtract the width of the eye itself from the horizontal to make it "look" right
Now, you can make your life easier by using Graphics#translate to move the origin/starting point of all graphical operations to that new location, this will reduce the amount of calculations you need to make. This also means that you could, technically, write two methods, one which did the actual painting of the smiley face starting from position 0x0 and one which translated the position (based on the parameters) and then called the first, but that's just an example idea ;)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Smile {
public static void main(String[] args) {
new Smile();
}
public Smile() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawSmiley(g, 25, 25, 100);
}
public void drawSmiley(Graphics g, int x, int y, int s) {
Graphics copy = g.create();
copy.translate(x, y);
copy.setColor((Color.YELLOW));
copy.fillOval(0, 0, s, s);
copy.setColor((Color.blue));
copy.fillOval((int) (1 + s * .3), (int) (1 + s * .3), (int) (s * .10), (int) (s * .10));
copy.fillOval((int) ((1 + s * .7) - (s * .10)), (int) (1 + s * .3), (int) (s * .10), (int) (s * .10));
double width = s * 0.8;
double height = s * 0.8;
copy.drawArc((int)((s - width) / 2d), (int)((s - height) / 2d), (int)width, (int)height, 0, -180);
copy.dispose();
}
}
}
package guimodule;
import processing.core.PApplet;
public class MyDisplay extends PApplet {
public void setup() {
size(400, 400);
background(200, 200, 200);
}
public void draw() {
fill(255, 255, 0);
ellipse(200, 200, 390, 390);
fill(0, 0, 0);
ellipse(120, 130, 50, 70);
ellipse(280, 130, 50, 70);
noFill();
fill(0, 0, 0);
arc(200, 280, 145, 120, 0, PI);
}
}
Related
I'm trying to make my own Java GUI project. I want to make the line's colors change when mouse pressed, but this doesn't work. I used 'for'loop and array for Colors but this doesn't run. So I'd like to ask you help me to solve it! Also, I wonder why loop needs for drawing lines on panel.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GraphicEx extends JFrame {
private MyPanel panel = new MyPanel();
public GraphicEx(){
setTitle("Java Mondrian");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(panel);
setSize(400,220);
setVisible(true);
}
class MyPanel extends JPanel{
private Vector <Point> vStart = new Vector <Point>();
private Vector <Point> vEnd = new Vector <Point>();
Color [] c = {Color.BLUE, Color.RED, Color.YELLOW, Color.BLACK};
private int a;
MyPanel(){
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
Point startP = e.getPoint();
vStart.add(startP);
for(int i=0; i<c.length; i++) {
if (i== (c.length-1)){
i=0;
}
a = i;
}
}
I made this for Color Change, But this not work.
#Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
Point endP = e.getPoint();
vEnd.add(endP);
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//component Color & Size
g.setColor(Color.BLACK);
g.drawRect(10,10,50,50);
g.setColor(Color.BLUE);
g.fillRect(60, 60, 100, 100);
g.setColor(Color.RED);
g.fillRect(50,50,20,20);
g.setColor(Color.YELLOW);
g.fillRect(130,50,50,50);
g.setColor(Color.RED);
g.drawRect(170,10,50,50);
g.setColor(Color.BLACK);
g.fillRect(210,50,80,50);
g.setColor(Color.YELLOW);
g.drawRect(260,30,40,170);
g.setColor(Color.RED);
g.fillRect(240,130,170,40);
g.setColor(new Color(0,0,0));
g.setFont(new Font("Arial",Font.ITALIC, 30));
g.drawString("Mondrian.2020", 100, 174);
g.setColor(new Color(0,210,200));
g.setFont(new Font("Arial",Font.BOLD,20));
g.drawString("Draw your own Picture", 70, 100);
g.setColor(new Color(0,0,0));
g.drawLine(20,20,350,20);
g.drawLine(35,0,35,180);
g.drawLine(20,160,350,160);
g.drawLine(330, 0, 330, 190);
int [] x = {80, 40, 80, 120};
int [] y = {40, 120, 200, 120};
g.drawPolygon(x,y,4);
g.setColor(Color.BLUE);
g.fillArc(290, 10, 50, 50, 90, 360);
for(int i=0; i<vStart.size();i++) {
Point s = vStart.elementAt(i);
Point e = vEnd.elementAt(i);
g.setColor(c[a]);
g.drawLine((int)s.getX(), (int)s.getY(), (int)e.getX(), (int)e.getY());
}
}
}
I cannot understand this part too! why I should use loop to draw lines?
public static void main(String[] args) {
new GraphicEx();
}
}
As there's only a limited supply of colors, and each line should have it's own, the modulo operator seems fitting:
//Make sure both vectors have that index!
for(int i=0; i< Math.min(vStart.size(), vEnd.size()); i++) {
Point s = vStart.elementAt(i);
Point e = vEnd.elementAt(i);
g.setColor(c[i % c.length]); //Use a calculated color using modulo length
g.drawLine((int)s.getX(), (int)s.getY(), (int)e.getX(), (int)e.getY());
}
Also it's not good to access elements that are not there (yet). Painting could happen at any time - so there might be start items with no end items.
If this works, you could get rid of the whole calculation of a as well.
I am working on a class that can rotate a wheel around the center. The wheel is created using graphics2d, but I can not figure out exactly how to get the wheel to rotate around the center.
Currently, the wheel rotates, but not exactly about the origin.
My ultimate goal here is to create the wheel so that it is multicolored as well as a program around it, but my main concern here is getting the rotating wheel to work. If you could point me in the right direction I would be forever grateful!
Here is my current code:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class RotateApp {
private static final int N = 3;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(N, N, N, N));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotatePanel());
frame.pack();
frame.setVisible(true);
System.out.println();
}
});
}
}
class RotatePanel extends JPanel implements ActionListener {
private static final int SIZE = 256;
private static double DELTA_THETA = Math.PI / 90;
private final Timer timer = new Timer(25, this);
private Image image = RotatableImage.getImage(SIZE);
private double dt = DELTA_THETA;
private double theta;
public RotatePanel() {
this.setBackground(Color.lightGray);
this.setPreferredSize(new Dimension(
image.getWidth(null), image.getHeight(null)));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
image = RotatableImage.getImage(SIZE);
dt = -dt;
}
});
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.translate(this.getWidth() / 2, this.getHeight() / 2);
g2d.rotate(theta);
g2d.translate(-image.getWidth(this) / 2, -image.getHeight(this) / 2);
g2d.drawImage(image, 0, 0, null);
}
public void actionPerformed(ActionEvent e) {
theta += dt;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
class RotatableImage {
private static final Random r = new Random();
static public Image getImage(int size) {
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(0, 100, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 200, 100));
g2d.draw(new Line2D.Double(100, 0, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 100, 200));
g2d.draw(new Line2D.Double(25, 25, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 175, 175));
g2d.draw(new Line2D.Double(175, 25, 100, 100));
g2d.draw(new Line2D.Double(100, 100, 25, 175));
g2d.draw(new Ellipse2D.Double(0, 0, 200, 200));
g2d.dispose();
return bi;
}
}
You can use the Rotated Icon class to do the rotation for you so you don't have to worry about all the rotation logic and the rotation logic is in a reusable class.
An example of using this class would be:
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.*;
public class Rotation4 extends JPanel
{
private JLabel label;
private RotatedIcon rotated;
private int degrees;
public Rotation4(Image image)
{
setLayout( new GridBagLayout() );
Icon icon = new ImageIcon( image );
rotated = new RotatedIcon(icon, 0);
rotated.setCircularIcon(true);
label = new JLabel(rotated);
label.setOpaque(true);
label.setBackground(Color.RED);
add(label, new GridBagConstraints());
setDegrees( 0 );
}
public void setDegrees(int degrees)
{
this.degrees = degrees;
rotated.setDegrees( degrees );
label.revalidate();
label.repaint();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
Image bi = RotatableImage.getImage(210);
final Rotation4 r = new Rotation4(bi);
final JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 360, 0);
slider.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
int value = slider.getValue();
r.setDegrees( value );
}
});
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(r));
f.add(slider, BorderLayout.SOUTH);
f.setSize(400, 400);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
static class RotatableImage
{
private static final Random r = new Random();
static public Image getImage(int size)
{
BufferedImage bi = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setPaint(Color.getHSBColor(r.nextFloat(), 1, 1));
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(5, 105, 205, 105));
g2d.draw(new Line2D.Double(105, 5, 105, 205));
g2d.draw(new Line2D.Double(35, 35, 175, 175));
g2d.draw(new Line2D.Double(175, 35, 35, 175));
g2d.draw(new Ellipse2D.Double(5, 5, 199, 199));
g2d.setColor(Color.BLACK);
g2d.fillOval(100, 100, 10, 10);
g2d.dispose();
return bi;
}
}
}
Note I also had to make changes with your image and your painting. These changes will need to be made whether you use the RotatedIcon or do the rotation code yourself:
The image size was changed to 210. This is because your stroke size is 10, so you need to account for the extra pixels in the circle outline.
You need to change the original of the circle by half the stroke size. So in this case the origin becomes (5, 5).
The size of the oval needs to be changed to 199. This is because of the way the outline of the oval is painted. 1 extra pixel is needed for the outline. If you leave the size at 200 then 1 pixel of the outline will be lost. This is not very noticeable when using a stroke size of 10, but if you use a size of 1, then the outline will be missing at the right and bottom edges.
The locations of your lines needs to be changes. you don't want the line right to the edge of the circle because then you will get a flat line at the edge instead of the rounded line. So I started the line 5 pixels from the start and ended it 5 pixels from the end.
Ok, with little modification and fewer "spokes", I got your wheel rotate centric (1.) and multicolor (2.):
UPDATE on 1.) To make centric rotation in your (original) code just chage SIZEto 200!
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;
public class RotateApp {
private static final int N = 3;
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(N, N, N, N));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RotatePanel());
frame.pack();
frame.setVisible(true);
System.out.println();
});
}
}
class RotatePanel extends JPanel implements ActionListener {
private static final int SIZE = 256;
private static final double DELTA_THETA = Math.PI / 90;
private final Timer timer = new Timer(25, this);
private Image image = RotatableImage.getImage(SIZE);
private double dt = DELTA_THETA;
private double theta;
public RotatePanel() {
this.setBackground(Color.lightGray);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
dt = -dt;
image = RotatableImage.getImage(SIZE);
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(theta,128,128);
g2d.drawImage(image, 0, 0, null);
}
#Override
public void actionPerformed(ActionEvent e) {
theta += dt;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
}
class RotatableImage {
private static final Random r = new Random();
static public Image getImage(int size) {
BufferedImage bi = new BufferedImage(
size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
final Color c1 = Color.getHSBColor(r.nextFloat(), 1, 1);
final Color c2 = Color.getHSBColor(r.nextFloat(), 1, 1);
g2d.setPaint(c1);
g2d.setStroke(new BasicStroke(10.0f));
g2d.draw(new Line2D.Double(0, size/2, size, size/2));
g2d.setPaint(c2);
g2d.draw(new Line2D.Double(size/2, 0, size/2, size));
g2d.setPaint(c1);
g2d.draw(new Ellipse2D.Double(0, 0, size, size));
g2d.dispose();
return bi;
}
}
Explanation:
So the "wobble" in your solution came from the fact, that you sized the image and the container/panel 256x256, but "based" your wheel layout on 200x200 only. I fixed all dimensions, and drew a correct cross, the g2d.rotate(theta,128,128); (!) relates then to the center.
Multi-color(easy): You can invoke setPaint() between each shape! ;)
I'm currently working with JFrame and I'm trying to draw a rectangle but I don't know how to execute the code paint(Graphics g), how do I get the Graphics object?
package com.raggaer.frame;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
public class Frame {
private JFrame frame;
public Frame() {
this.frame = new JFrame("Java Snake");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.setSize(new Dimension(500, 500));
// DRAW??
this.frame.setVisible(true);
}
public void paint(Graphics g) {
g.drawRect(10, 10, 200, 200);
}
}
Just call frame.repaint() (which should be called once automatically) to make it repaint the graphics. No need to provide your own Graphics object.
Side note, you should be using a JPanel with paintComponent(Graphics) instead. This will make handling of events a lot easier, especially for a game like snake.
Here is a small code example on Stack Overflow: Java drawing on JPanel which on a JFrame
And one I made myself with usage of Java 8:
import javax.swing.*;
import java.awt.*;
/**
* #author Obicere
*/
public class PaintExample {
public PaintExample() {
final JFrame frame = new JFrame("Paint Example");
final MyPanel panel = new MyPanel();
frame.add(panel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(PaintExample::new);
}
public class MyPanel extends JPanel {
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.fillOval(0, 0, 50, 50);
g.setColor(Color.BLACK);
g.drawOval(0, 0, 50, 50);
g.drawLine(20, 10, 20, 20);
g.drawLine(30, 10, 30, 20);
g.drawArc(15, 15, 20, 20, 180, 180);
g.drawString("Drawing with swing!", 10, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
As request of your comment, I also modified the program to display objects upon request:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
/**
* #author Obicere
*/
public class PaintExample {
public PaintExample() {
final JFrame frame = new JFrame("Paint Example");
final MyPanel panel = new MyPanel();
frame.add(panel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(PaintExample::new);
}
public class MyPanel extends JPanel {
private final LinkedList<SmileyFace> faces;
public MyPanel() {
faces = new LinkedList<>();
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
faces.add(new SmileyFace(e.getX(), e.getY()));
MyPanel.this.repaint(); // Refresh the display on the screen
}
});
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
faces.stream().forEach((e) -> e.render(g));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public class SmileyFace {
private final int x;
private final int y;
public SmileyFace(final int x, final int y) {
this.x = x;
this.y = y;
}
public void render(final Graphics g) {
g.setColor(Color.YELLOW);
g.fillOval(x, y, 50, 50);
g.setColor(Color.BLACK);
g.drawOval(x, y, 50, 50);
g.drawLine(x + 20, y + 10, x + 20, y + 20);
g.drawLine(x + 30, y + 10, x + 30, y + 20);
g.drawArc(x + 15, y + 15, 20, 20, 180, 180);
}
}
}
I've come across an issue when messing around with shapes.
I created a simple program with a slider with which you manipulate the size of an arc. The problem is that when I run it, the arc isn't painted to the screen. However, when I change the value of the slider, everything starts working perfectly. Any thoughts on what this might be caused by? Here's the code:
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.awt.geom.*;
#SuppressWarnings("serial")
public class GraphicsTest2 extends JFrame implements ChangeListener { // START OF GraphicsTest2
private JPanel sliderPanel, thePanel;
private JSlider slider;
private DrawStuff draw;
public static void main(String[] args) { // START OF main
new GraphicsTest2();
} // END OF main
public GraphicsTest2() { // START OF CONSTRUCTOR
super("Graphics test 2");
this.setSize(500, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
thePanel = new JPanel();
thePanel.setLayout(new BorderLayout(10, 10));
slider = new JSlider(JSlider.HORIZONTAL, 1, 360, 120);
slider.addChangeListener(this);
sliderPanel = new JPanel();
sliderPanel.add(slider, BorderLayout.CENTER);
draw = new DrawStuff();
thePanel.add(sliderPanel, BorderLayout.SOUTH);
thePanel.add(draw, BorderLayout.CENTER);
this.add(thePanel, BorderLayout.CENTER);
this.pack();
this.validate();
this.setVisible(true);
} // END OF CONSTRUCTOR
public void stateChanged(ChangeEvent e) { // START OF stateChanged
if(e.getSource() == slider) {
int size = slider.getValue();
draw.arc = new Arc2D.Double(draw.getWidth() / 2 - 50, draw.getHeight() / 2 - 50, 100, 100, 0, size, Arc2D.PIE);
this.repaint();
}
} // END OF stateChanged
private class DrawStuff extends JComponent { // START OF DrawStuff
Shape arc;
public void paintComponent(Graphics g) { // START OF paintComponent
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.GREEN);
g2.fill(arc);
} // END OF paintComponent
public DrawStuff() {
this.setPreferredSize(new Dimension(100, 140));
arc = new Arc2D.Double(this.getWidth() / 2 - 50, this.getHeight() / 2 - 50, 100, 100, 0, 120, Arc2D.PIE);
}
} // END OF DrawStuff
} // END OF GraphicsTest2
When you create your arc in the constructor of DrawStuff here:
arc = new Arc2D.Double(this.getWidth() / 2 - 50, this.getHeight() / 2 - 50, 100, 100, 0, 120, Arc2D.PIE);
this.getWidth() and this.getHeight() will give a result of 0 because the components are not sized yet. A simple fix would be to use the fixed size like this:
arc = new Arc2D.Double(210 / 2 - 50, 140 / 2 - 50, 100, 100, 0, 120, Arc2D.PIE);
I am writing an application which has a JLayeredPane (call it layers) containing two JPanels in different layers. I override the paintComponent method of the JPanel at the bottom (call it grid_panel) so it paints a grid, and the the paintComponent method of the one at the top (call it circuit_panel) so it paints a circuit.
Here's a summary of the structure:
layers -
|-circuit_panel (on top)
|-grid_panel (at bottom)
I want the grid_panel to stay static, ie, not to do any repaint (except the initial one) since it does not change.
The trouble is, whenever I call circuit_panel.repaint(), grid_panel gets repainted as well! This is a definitely not efficient.
I think this is due to the eager painting behavior of JLayeredPane. Is there a way to disable this feature in JLayeredPane?
In case you're interested to see the above effect, I've written a small demo program:
public class Test2 extends JFrame {
public Test2() {
JLayeredPane layers = new JLayeredPane();
layers.setPreferredSize(new Dimension(600, 400));
MyPanel1 myPanel1 = new MyPanel1();
MyPanel2 myPanel2 = new MyPanel2();
myPanel1.setSize(600, 400);
myPanel2.setSize(600, 400);
myPanel1.setOpaque(false);
myPanel2.setOpaque(false);
myPanel2.addMouseListener(new MyMouseListener(myPanel2));
layers.add(myPanel1, new Integer(100)); // At bottom
layers.add(myPanel2, new Integer(101)); // On top
this.getContentPane().add(layers, BorderLayout.CENTER);
this.setSize(600, 400);
}
class MyPanel1 extends JPanel {
Color getRandomColor() {
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
return new Color(r, g, b);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getRandomColor());
g2d.fillRoundRect(30, 30, 60, 60, 5, 5);
}
}
class MyPanel2 extends JPanel {
Color getRandomColor() {
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
return new Color(r, g, b);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getRandomColor());
g2d.fillRoundRect(45, 45, 75, 75, 5, 5);
}
}
class MyMouseListener extends MouseAdapter {
JPanel panel;
MyMouseListener(JPanel panel) {
this.panel = panel;
}
#Override
public void mouseClicked(MouseEvent e) {
panel.repaint();
}
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
(new Test2()).setVisible(true);
}
});
}
}
As you found, a BufferedImage is an effective way to cache complex content for efficient rendering; CellTest is an example. A flyweight renderer, shown here, is another approach. Finally, I've re-factored your instructive example in a way that may make experimentation easier.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** #see https://stackoverflow.com/q/9625495/230513 */
public class LayerDemo extends JFrame {
private static final Dimension d = new Dimension(320, 240);
public LayerDemo() {
JLayeredPane layers = new JLayeredPane();
layers.setPreferredSize(d);
layers.add(new LayerPanel(1 * d.height / 8), 100);
layers.add(new LayerPanel(2 * d.height / 8), 101);
layers.add(new LayerPanel(3 * d.height / 8), 102);
this.add(layers, BorderLayout.CENTER);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setLocationByPlatform(true);
}
private static class LayerPanel extends JPanel {
private static final Random r = new Random();
private int n;
private Color color = new Color(r.nextInt());
public LayerPanel(int n) {
this.n = n;
this.setOpaque(false);
this.setBounds(n, n, d.width / 2, d.height / 2);
this.addMouseListener(new MouseHandler(this));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(color);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16);
g2d.setColor(Color.black);
g2d.drawString(String.valueOf(n), 5, getHeight() - 5);
}
private void update() {
color = new Color(r.nextInt());
repaint();
}
}
private static class MouseHandler extends MouseAdapter {
LayerPanel panel;
MouseHandler(LayerPanel panel) {
this.panel = panel;
}
#Override
public void mouseClicked(MouseEvent e) {
panel.update();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
(new LayerDemo()).setVisible(true);
}
});
}
}