I've written a small Swing program that draws a head and when a JCheckBox instance is selected/unselected by the user a hat is drawn or removed from on top of the head. I'm having some trouble taking the next step with this program -- I'd like to add a boolean field to the Head class that causes this component to listen to mouse events with a MouseListener. From there, I'd like to use two methods to set this field to true/false and render the remaining three methods lame ducks. Also, how would I change the paintComponent method so that if the boolean value is true the object is drawn with open eyes, and if it's false, the head is drawn with the eyes closed? Please provide any advice you have. Many thanks!
import javax.swing.*;
import java.awt.geom.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
public class Head extends JPanel {
Rectangle2D.Double chapeau, chapeau2;
Ellipse2D.Double bouche, visage, oeil1, oeil2;
JCheckBox box;
public Head(){
this.setBackground(Color.WHITE);
visage = new Ellipse2D.Double (150,130,100,100);
bouche = new Ellipse2D.Double (170,180,60,27);
chapeau = new Rectangle2D.Double (170,80,60,40);
chapeau2 = new Rectangle2D.Double (125,120,150,10);
oeil1 = new Ellipse2D.Double (170,150,20,20);
oeil2 = new Ellipse2D.Double (210,150,20,20);
box = new JCheckBox("Hat");
this.add(box);
box.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie){
repaint();
}
});
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(3.0f));
g2.setPaint(Color.BLUE);
g2.draw(visage);
g2.draw(oeil1);
g2.draw(oeil2);
g2.draw(bouche);
if(box.isSelected()){
g2.draw(chapeau);
g2.draw(chapeau2);
}
}
public static void main(String[] args){
JFrame f = new JFrame("Face Display Window");
f.setSize(425,285);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);
f.add(new Head());
}
}
----------------------------------- Second Try
import javax.swing.*;
import java.awt.geom.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Head extends JPanel implements MouseListener {
Rectangle2D.Double chapeau, chapeau2;
Ellipse2D.Double bouche, visage, oeil1, oeil2, oeil3, oeil4;
JCheckBox box;
boolean isClosed = false;
public Head(){
this.setBackground(Color.WHITE);
visage = new Ellipse2D.Double (150,130,100,100);
bouche = new Ellipse2D.Double (170,180,60,27);
chapeau = new Rectangle2D.Double (170,80,60,40);
chapeau2 = new Rectangle2D.Double (125,120,150,10);
oeil1 = new Ellipse2D.Double (170,150,20,20);
oeil2 = new Ellipse2D.Double (210,150,20,20);
oeil3 = new Ellipse2D.Double (175,155,25,25);
oeil4 = new Ellipse2D.Double (215,155,25,25);
box = new JCheckBox("Hat");
this.addMouseListener(this);
this.add(box);
box.addItemListener(new ItemListener(){
public void itemStateChanged(ItemEvent ie){
repaint();
}
});
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(3.0f));
g2.setPaint(Color.BLUE);
g2.draw(visage);
g2.draw(oeil1);
g2.draw(oeil2);
g2.draw(bouche);
if(box.isSelected()){
g2.draw(chapeau);
g2.draw(chapeau2);
if(isClosed) {
g2.draw(oeil3);
g2.draw(oeil4);
}
else {
g2.draw(oeil1);
g2.draw(oeil2);
}
}
}
#Override
public void mouseClicked(MouseEvent e) {
isClosed = !isClosed;
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
public static void main(String[] args){
JFrame f = new JFrame("Face Display Window");
f.setSize(425,285);
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setVisible(true);
f.add(new Head());
}
}
I'm intentionally being a little vague here because I'm not sure if this is homework or not since you already have a fair amount of code that does a lot of stuff that is very similar to what you want it to and modifying it shouldn't be very difficult. However, if you're actually stuck, please clarify and I'll add more details if required.
I'd like to add a boolean field to the Head class that causes this component to listen to mouse events with a MouseListener.
This isn't too hard, let's walk through it. It's trivial to add a boolean field to your Head class - you simply declare boolean isClosed = false; - indicating that you begin the execution with the field set to false which your code will later interpret as the instruction to draw eyes open.
Your core requirement is the MouseListener. If you haven't already, check out the Java Trail for events; it explains how to implement a simple MouseListener. At this point, note that MouseListener is an interface and thus, you'd necessarily need to provide an implementation for all it's methods, even if they're empty-bodied methods. You may want to check out the MouseAdapter abstract class. It provides empty implementations of all these methods (and more) so that you only need to override the ones you need - this makes your code cleaner since you don't have a bunch of empty methods just to satisfy the compiler. This would solve the problem I believe you're referring to when you say 'and render the remaining three methods lame ducks' Of course, since you're already extending JPanel, you can't extend MouseAdapter as well so this doesn't apply here. But this (with other Adapters) is a useful class to keep in mind for later.
From there, I'd like to use two methods to set this field to true/false
If I understand you correctly, what you want is to toggle the value of isClosed on mouse clicks. So right now, you have a MouseListener/ MouseAdapter that doesn't really do anything. What you need to do next is provide an implementation for, lets say, the MouseClicked() method where you toggle the value of your boolean field. This is really easy as well - you simply invert the current value using the ! (NOT) operator and assign it back the variable - isClosed = !isClosed;. You may wish to read up on operators in Java in more detail.
Also, how would I change the paintComponent method so that if the boolean value is true the object is drawn with open eyes, and if it's false, the head is drawn with the eyes closed?
One way of doing this is to create two more shapes for the two closed eyes, similar to the ones you have for open eyes. Once you've done that, it's a simple matter of deciding which ones to draw (i.e. the closed eyes or the open ones) on the basis of the value of isClosed. So you'd use an if clause to check the value of isClosed and draw the open eyes when it's false and the closed eyes when true. Note that since your value of isClosed is being modified in your click handler, you need to make sure that you call repaint() when you change the value otherwise Swing may not update the panel immediately to show the change so then you won't see anything happen.
To sum it up, one way to do what you want is to make the following modifications to Head:
public class Head
extends JPanel
implements MouseListener {
//...all your other declarations still go here
boolean isClosed = false;
//also declare new 'eyes' which are closed
public Head() {
//..all your existing code is still here
//add code to instantiate closed eyes
//need to register a new MouseListener
//since head itself is a MouseListener, we can pass this as the argument
this.addMouseListener(this);
}
//...all your existing code still goes here
public void paintComponent(Graphics g) {
//...all your existing code still goes here
//decide which eyes to draw depending on isClosed
if(isClosed) {
//draw closed eyes
}
else {
//draw open eyes
}
//draw everything else as before
}
//implementation for MouseListener
//don't forget the rest of the MouseListener events
//mousePressed, mouseReleased, mouseEntered, mouseExited
public void mouseClicked(MouseEvent e) {
//toggle the value of isClosed
isClosed = !isClosed;
//must repaint
repaint();
}
Related
first questing for me here. Been searching forever but cant seem to find the answer.
Im working on a school assignment. Got given an ui and are supposed to make the different panels in it do different things in separate threads. Anyway, Im trying to make a triangle rotate inside one of the JPanels. I have managed to draw it and somewhat rotate it, but when I try to make a loop to update it it just blinks and then disappears again. Heres the code Ive written.
StartAssignment, starts the application
public class StartAssignment1 {
public static void main(String[] args) {
Controller controller = new Controller();
}
Controller, recieves calls from the ui and executes various functions in other classes
public class Controller {
private GUIAssignment1 gui = new GUIAssignment1(this);
private RotateShape rotateShape;
private Thread t1;
public Controller() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui.Start();
}
});
}
public void startT1(JPanel panel) {
rotateShape = new RotateShape(panel);
t1 = new Thread(rotateShape);
t1.start();
}
public void t1Shutdown() {
rotateShape.shutdown();
}
RotateShape, where Im trying to rotate the damned thing
public class RotateShape implements Runnable {
JPanel panel;
private volatile boolean t1Running = true;
public RotateShape(JPanel panel) {
this.panel = panel;
}
private void draw() {
Graphics2D g2 = (Graphics2D) panel.getGraphics();
g2.rotate(Math.toRadians(10));
g2.drawPolygon(new int[] {50, 100, 150}, new int[] {150, 50, 150}, 3);
}
public void shutdown() {
t1Running = false;
System.out.println("Shutdown");
}
#Override
public void run() {
while (t1Running) {
try {
draw();
Thread.sleep(500);
System.out.println("loop working");
panel.repaint();
panel.revalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
I'm not certain because I don't have access to your GUI code and therefore cannot test it myself, but here's an idea:
It looks like you're rotating your Graphics2D object by a fixed amount on every re-render (i.e. every invocation of draw()). It's possible that the internal JPanel code initiates the Graphics2D at a default rotation before every re-render, which may be why your rotation only causes the shape to "somewhat rotate."
Maybe try storing a variable for the radian offset (e.g. double offset;) as a member field of the RotateShapeclass, incrementing it on every re-render, then calling g2.rotate(Math.toRadians(offset));?
Addendum 1:
The reason that it may draw on top of the previous render is because the Graphics2D object is not clearing the canvas between re-renders. One way to fix this is to "clear" the canvas at the beginning of the draw() method by filling a rectangle that covers the whole canvas.
Addendum 2:
When you call panel.repaint(), it triggers the paintComponent() method of the JPanel object. So, by calling repaint() and your own draw() method, you are doing two separate renders, which may cause some graphical errors. If you were able to extends the JPanel class and use that, you should define an override to replace draw():
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// TODO: add your rendering operations here
}
This way, calls to repaint() should trigger this method.
I'm a beginner at java and I'm trying to make a square that moves in response to arrow keys. I've tried a lot of things but it just won't work. There are probably obvious errors in my code/ in my understanding of the code so if anyone could point them out that would be really helpful.
Main class:
import java.awt.;
import javax.swing.;
public class mainClass2 {
public static void main(String[] args) {
JFrame window = new JFrame();
window.setSize(600,400);
window.setVisible(true);
window.getContentPane().setBackground(Color.WHITE);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
boolean constant = true;
graphics2 DC = new graphics2();
window.add(DC);}
}
Graphics class:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class graphics2 extends JComponent implements KeyListener{
public static int x1;
public static int y1;
public static int xvelocity = 0;
public static int yvelocity = 0;
public void paintComponent(Graphics g){
graphics2 thing = new graphics2();
this.addKeyListener(thing);
System.out.println(x1);
System.out.println(y1);
Graphics2D g2 =(Graphics2D)g;
Rectangle rect = new Rectangle(x1,y1,200,200);
g2.setColor(Color.red);
rect.setLocation(x1,y1);
g2.fill(rect);
repaint();
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
x1+=10;
// TODO Auto-generated method stub
if(e.getKeyCode()== KeyEvent.VK_LEFT) {
x1-=10;
}
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
Let's start with...
public void paintComponent(Graphics g){
graphics2 thing = new graphics2();
this.addKeyListener(thing);
System.out.println(x1);
System.out.println(y1);
Graphics2D g2 =(Graphics2D)g;
Rectangle rect = new Rectangle(x1,y1,200,200);
g2.setColor(Color.red);
rect.setLocation(x1,y1);
g2.fill(rect);
repaint();
}
You're creating a new instance of graphics2 and adding a KeyListener to that instance, but since that instance is never added to anything which could possibly display it, it will never be eligible for receiving key events.
Painting should paint the current state and work as quickly as possible. Creating short lived objects in paintComponent (including the creation of your rect) is generally a bad idea, as it could put unnecessary strain on your system and degrade performance.
Also, calling repaint(); inside paintComponent is a very, very bad idea. paintComponent called be called for any number of reasons, doing this is going to setup a cycle of updates which will consume the CPU and bring your system to its knees (yes, I've done it before).
To schedule regular updates, a better choice would be to use a Swing Timer, see How to use Swing Timers
Another issue is, you component isn't focusable, so it will never be able to receive keyboard focus and won't receive key events.
KeyListener is a poor choice, which has well documented limitations (just search "KeyListener doesn't work"). Instead, you should be using the Key Bindings API which will solve the limitations of the KeyListener
You may also want to have a look at How to use Key Bindings instead of Key Listeners and Key bindings vs. key listeners in Java
Add your graphics2 as a key listener and a component to your JFrame (Which I assume you are using) instead:
graphics2 graphics = new graphics2();
JFrame frame = new JFrame("Your Frame");
frame.add(graphics);
frame.addKeyListener(graphics);
Remove this line of code from graphics2 above:
this.addKeyListener(this);
I have a problem when trying to draw some elements using paint method in Swing.
As title says, my whole frame collapses and does some weird repeating.
I made a separate JPanel so I can manipulate drawn shapes:
public class PanelPovrsina extends JPanel{
private ArrayList<Oblik> listaOblika;
public PanelPovrsina() {
// svi oblici
this.listaOblika = new ArrayList<Oblik>();
this.listaOblika.add(new Kvadrat(new Tacka(50, 50), 50, "zuta", "crvena"));
this.setBackground(Color.WHITE);
this.setVisible(true);
}
public void paint(Graphics g) {
if(this.listaOblika.isEmpty()) return;
Iterator<Oblik> it = this.listaOblika.iterator();
while(it.hasNext()) {
it.next().crtajUBoji(g);
}
repaint(); // this causes problems!
}
public ArrayList<Oblik> getListaOblika() {
return this.listaOblika;
}
}
Here is the frame with this code:
And here it is without repaint method:
No, I know repaint method is essential in order to dynamically add shapes and actually draw, but I can't make this work correctly.
Also, as you can see from the code above, background of panel is set to white, but my frame would'n render it.
Hope there is enough information to solve my problem, if not, I will add code of my JFrame!
Thank you!
You should never override the paint method, as it handles a number of other things behind the scenes. You should override paintComponent instead.
As #Joe C answered, I should have been using paintComponent method, not paint! Working code:
public class PanelPovrsina extends JPanel{
private ArrayList<Oblik> listaOblika;
public PanelPovrsina() {
// svi oblici
this.listaOblika = new ArrayList<Oblik>();
this.listaOblika.add(new Kvadrat(new Tacka(50, 50), 50, "zuta", "crvena"));
this.setBackground(Color.PINK);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Oblik obl : this.listaOblika) {
obl.crtajUBoji(g);
}
repaint();
}
public ArrayList<Oblik> getListaOblika() {
return this.listaOblika;
}
}
I have created a program that draws a thick line.
import javax.swing.*;
import java.awt.*;
public class Movement {
int xGrid = 50;
public static void main(String[] args) {
Movement m = new Movement();
m.animate();
}
public void animate() {
JFrame frame = new JFrame("Movement");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ScreenDisplay display = new ScreenDisplay();
frame.getContentPane().add(display);
frame.setSize(400, 400);
frame.setVisible(true);
for (int aL = 0; aL < 200; aL++) {
xGrid++;
display.repaint();
try {
Thread.sleep(50);
} catch (Exception ex) { }
}
}
class ScreenDisplay extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.fillOval(xGrid, 175, 50, 50);
}
}
}
Because of the method "Thread.sleep(50)", the speed of the program slows down a little.
So I got a little curious and removed the "sleep()" method.
What I expected to output was the exact same output, just extremely fast.
However, it just prints out one circle in the frame.
I don't really know why it outputs just one circle, none of the researches I've done back up the answer.
Can anyone please explain why?
From Component.repaint documentation:
Repaints this component.
If this component is a lightweight component, this method
causes a call to this component's paint
method as soon as possible. Otherwise, this method causes
a call to this component's update method as soon
as possible.
By the looks of it, your for loop finishes so quickly that by the time the component calls the repaint method, it has already finished and therefore only paints the final circle stored in the buffer.
I have tried my best to read around on the topic but I cannot find/understand how to apply the answers to my piece code (and those I have applied don't work)
I have used an example from "Ivor Horton's Beginning Java 2 JDK book" and it is the first time I'm using repaint() but it doesn't seem to work unless I resize the window. It tries to repaint but leaves the screen blank.
Here's the code please let me know if there is something I'm doing wrong.
public class CurveDrawings extends JFrame{
public CurveDrawings (String title){
setTitle(title);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pane = new CurvePane(); //Create pane containing curves
Container content = getContentPane(); //Get the content pane
//Add the pane containing the curves to the content pane for the applet
content.add(pane);
MouseHandler handler = new MouseHandler();
pane.addMouseListener(handler);
pane.addMouseMotionListener(handler);
}
//Class defining pane on which to draw
class CurvePane extends JComponent{
public CurvePane(){
quadCurve = new QuadCurve2D.Double(
startQ.x, startQ.y, control.x, control.y, endQ.x, endQ.y
);
cubicCurve = new CubicCurve2D.Double(
startC.x, startC.y, controlStart.x, controlStart.y,
controlEnd.x, controlEnd.y, endC.x, endC.y
);
}
}
class Marker{//not needed for my problem}
class MouseHandler extends MouseInputAdapter{
public void mousePressed(MouseEvent e){
if(ctrlQuad.contains(e.getX(),e.getY())){
selected = ctrlQuad;}
else if(ctrlCubic1.contains(e.getX(),e.getY())){
selected = ctrlCubic1;}
else if(ctrlCubic2.contains(e.getX(),e.getY())){
selected = ctrlCubic2;}
}
public void mouseReleased (MouseEvent e){
selected = null;
}
public void mouseDragged (MouseEvent e){
System.out.println("DRAGGED");
if(selected != null){
//Set the marker to current cursor position
selected.setLocation(e.getX(),e.getY());
pane.validate();
pane.repaint();
}
}
Marker selected = null;
}
public void paint (Graphics g){
Graphics2D g2D = (Graphics2D)g; //Get a 2D device context
//Code to draw each component
}
//Points for quadratic curve
//Points for cubic curve
//More unnecessary code}
Incase it is any help here's the 'Launcher' class for the application
Thanks in advance.
You will need to call
super.paint(g);
in your paint method in your CurveDrawings class to avail of the painting functionality already provided in the JFrame super class.
Note, the standard way of using custom painting in Swing is to use custom components that are based on javax.swing.JComponent that use and override paintComponent. This approach would leverage Swing's optimized painting model.
See: Performing Custom Painting