drawing graphics and event handling - java

I am trying to write a program that draws a circle on the screen then gives you 3 buttons (red, yellow, and Green) and clicking the button changes the fill color of the circle accordingly.
I think I'm close, I just don't know how actually to create a method that will draw the circle and change the color. I can write a method to draw and fill a circle I'm just having problems merging it with jbutton
This is what i have so far:
(ignore the unused imports)
took a different approach, i don't know if it's any better. My buttons display and everything just having problems changing the color. Actually right now i cant even display a circle. i know i need to call repaint(); in my eventhandler im just not sure how to do it. This is due Sunday ive spent so many hours watching videos and reading example i just cant get mine to work. I'm sure its stupid simple but it frustrating that heck out of me!
public class test3 extends JPanel {
JRadioButton RED, YELLOW, GREEN;
Color currentColor;
public void paintComponent(Graphics g){
currentColor= Color.RED;
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(currentColor);
g.fillOval(50, 50, 100, 100);
}
public static void main(String[] args) {
test3 frame = new test3();
frame.setSize(500,500);
frame.setVisible(true);
}
public test3 (){
JPanel jpRadioButtons=new JPanel();
jpRadioButtons.setLayout(new GridLayout(1,1));
jpRadioButtons.add(RED=new JRadioButton("RED"));
jpRadioButtons.add(GREEN=new JRadioButton("GREEN"));
jpRadioButtons.add(YELLOW=new JRadioButton("YELLOW"));
add(jpRadioButtons, BorderLayout.SOUTH);
ButtonGroup group=new ButtonGroup();
group.add(RED);
group.add(YELLOW);
group.add(GREEN);
GREEN.addActionListener(new ActionListener()
{
public void actionPerormed(ActionEvent e)
{
currentColor = Color.GREEN;
repaint();
}
});
}
}

Introduce a class variable/property/... with the current color of the circle.
Set this variable in your eventhandler
Also call "repaint();" in your eventhandler
Override the paintComponent() method and make it draw a circle in the color, which you can read from the class variable.
Your paintComponent(Graphics g) might look something like this:
#Override
void paintComponent(Graphics g)
{
g.setColor(currentColor);
g.drawOval(50,50,100,100);
}

Related

Java: How to draw in a canvas?

I'm new to Java and I want to make a simple game where there are squares in a simple grid (something between a snake and a labyrinth). The number of squares doesn't change, only their color does.
I already programmed the objects I need for the game. Now I want to display the game.
I know how to create a JFrame, a JPanel and a JButton, which I will need, but I don't understand how to draw in a canvas.
I made a test class:
import javax.swing.*;
import java.awt.*;
public class Test extends Canvas {
public static void main (String[] arg) {
JFrame f=new JFrame();
f.setTitle("Title");
f.setSize(400,500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
JPanel pan=new JPanel(new FlowLayout());
pan.add(new JButton("hey"));
Test canvas=new Test();
Graphics g=canvas.getGraphics();
canvas.paint(g);
pan.add(canvas);
f.setContentPane(pan);
}
public Test() {
setBackground (Color.green);
setSize(300,300);
setVisible(true);
}
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.RED);
g.fillRect(10,10,80,80);
g.setColor(Color.BLUE);
g.fillRect(150,50,80,80);
g.setColor(c);
}
}
When I run this, a windows is created, with the correct title , size and button, but there isn't anything else (no green background, no red or blue rectangle), and an error is detected :
Exception in thread "main" java.lang.NullPointerException
at Test.paint(Test.java:25)
at Test.main(Test.java:15)
What am I doing wrong ? Is there a better way to draw these squares ?
Thanks !
Comment these two lines
//Graphics g=canvas.getGraphics();
//canvas.paint(g);
And change the paint method as follows.
#Override
public void paint(Graphics g) {
super.paint(g);
Rest is all fine. Hope it helps you to progress.

java inheritance and polymorphism

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

JFrame's Border Layout Positioning Not as Expected

I'm trying to create a Java Program that does this,
A Frame with
NORTH ;Description:Two Button Listener
CENTRE:Description: Click Button1 to change solidcolor circle and Button2 to change gradient circle
SOUTH: I want 2 buttons ,Button 1 and Button 2
WEST: A Circle with a gradient colour
EAST:A circle with a solid colour
It's fairly Simple , when I click a button the respective circle's colour should change
The code is as follows ..
import javax.swing.*;//for the frame etc
import java.awt.*;//for paintComponent(),Grapics Object etc
import java.awt.event.*;//for listeners
class CircleGradientColor extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2d=(Graphics2D)g;//cast
int r1=(int)(Math.random()*255);
int g1=(int)(Math.random()*255);
int b1=(int)(Math.random()*255);
int r2=(int)(Math.random()*255);
int g2=(int)(Math.random()*255);
int b2=(int)(Math.random()*255);
Color startcolor=new Color(r1,g1,b1);
Color endcolor=new Color(r2,g2,b2);
GradientPaint gradient=new GradientPaint(10,10,startcolor,70,70,endcolor);
g2d.setPaint(gradient);//here it aint set color for a Graphics2D object
g2d.fillOval(10,10,60,60);//fills with the Current PaintBrushColor
}
}
class CircleSolidColor extends JPanel//this class will contain code for the circle
{
public void paintComponent(Graphics g)
{
int r=(int)(Math.random()*255);//generate random float between 0 & 255
int b=(int)(Math.random()*255);//generate random float between 0 & 255
int gr=(int)(Math.random()*255);//generate random float between 0 & 255
Color randcolor=new Color(r,gr,b);//name clashed with Graphics g
g.setColor(randcolor);
g.fillOval(10,10,60,60);
}
}
public class TwoButtonGui
{
JFrame frame;
CircleSolidColor circlesolidcolor;
CircleGradientColor circlegradientcolor;
JButton b1;
JButton b2;
JLabel toplabel;
JLabel centerlabel;
public static void main(String[] args)
{
TwoButtonGui twobuttongui=new TwoButtonGui();
twobuttongui.go();
}
public void go()
{
frame=new JFrame();
toplabel=new JLabel("Example of Multiple Action Listeners");
centerlabel=new JLabel("Click The respective Button to change circle Color");
b1=new JButton("Click to change solid");
b2=new JButton("Click to change Gradient");
b1.addActionListener(new CircleSolidColorListener());
b2.addActionListener(new CircleGradientColorListener());
circlesolidcolor=new CircleSolidColor();
circlegradientcolor=new CircleGradientColor();
frame.setSize(1000,1000);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.NORTH,toplabel);
frame.getContentPane().add(BorderLayout.SOUTH,b1);
frame.getContentPane().add(BorderLayout.SOUTH,b2);
//if i add 2 SOUTH position i though first will go left and second right
//IS it so
frame.getContentPane().add(BorderLayout.CENTER,centerlabel);
frame.getContentPane().add(BorderLayout.WEST,circlesolidcolor);
frame.getContentPane().add(BorderLayout.EAST,circlegradientcolor);
frame.setVisible(true);
}
//inner class for solidlistener
class CircleSolidColorListener implements ActionListener
{
public void actionPerformed(ActionEvent e1)
{
circlesolidcolor.repaint();
}
}
class CircleGradientColorListener implements ActionListener
{
public void actionPerformed(ActionEvent e2)
{
circlegradientcolor.repaint();
}
}
}//main class ends
But the output I get is rather absurd
Where am I going Wrong , I know it can be corrected using multiple Panels and LayoutMangaers etc but is there a way to get the results I described without all that?
I wrote the program as a variant of an example in Headfirst Java which seems to work just fine with (2 buttons , a label and a Circle ), so why doesn't this work , can I add 2 components in a single position like say BorderLayout.SOUTH as described in my comments,
Thanks!
North, is correct.
South, adding b2 will overwrite b1, what you want to do is create a new container like JPanel and add b1 to b2 to that container. Then add this container to south. This new container can use GridLayout(1,2)
Center, is correct.
East and west, the components are 'blank' so the default size giving by layout manager is none ( painting in the container background != isNotBlank() ). You can however use circles*.setPrefferredSize(dimension) to force a size on them.

Why does this this Swing bug occur when using repaint() and not with getParent().repaint()?

This question is based on a problem I had a while back with a simple Swing dice program. The original question I posted is here and has an accepted answer, but I'd like to know exactly what is happening, why the problem occurs, and why the solution works.
I managed to whittle down the original code to get to the core of the problem and it now looks very different:
I have two ColorPanels that each draw a coloured square
when you click on a panel the box should change colour in this order: start at black, then >red>green>blue>red>green>blue> etc
once a box has changed colour it should never be black again
However when I just call repaint() in the MouseListener, the program behaves very strangely:
I click on one panel and the square's colour changes
I then click on the other and it's square changes colour, but the first square also changes, back to black
you can see this behaviour in the gif below:
If you use getParent().repaint() instead this behaviour goes away and the program behaves as expected:
The problem only seems to occur if the panels/squares start off 'overlapping'.
If you use a layout that stops this or don't set the size small then the problem does not seem to occur.
the problem doesn't happen every time which initially made me think that concurrency problems might be involved.
The code that I had problems with in my original question did not seem to cause problems for everybody and so my IDE, jdk etc might be relevant as well: Windows 7, Eclipse Kepler, jdk1.7.0_03
The code minus imports etc is as follows:
public class ColorPanelsWindow extends JFrame{
static class ColorPanel extends JPanel {
//color starts off black
//once it is changed should never be
//black again
private Color color = Color.BLACK;
ColorPanel(){
//add listener
addMouseListener(new MouseAdapter(){
#Override
public void mousePressed(MouseEvent arg0) {
color = rotateColor();
repaint();
//using getParent().repaint() instead of repaint() solves the problem
//getParent().repaint();
}
});
}
//rotates the color black/blue > red > green > blue
private Color rotateColor(){
if (color==Color.BLACK || color == Color.BLUE)
return Color.RED;
if (color==Color.RED)
return Color.GREEN;
else return Color.BLUE;
}
#Override
public void paintComponent(Graphics g){
g.setColor(color);
g.fillRect(0, 0, 100, 100);
}
}
ColorPanelsWindow(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1,0));
add(new ColorPanel());
add(new ColorPanel());
//the size must be set so that the window is too small
// and the two ColorPanels are overlapping
setSize(40, 40);
// setSize(300, 200);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new ColorPanelsWindow();
}
});
}
}
So my question is, what on earth is going on here?
but I'd like to know exactly what is happening,
I see the same problems using JDK7u60 on Windows 7. Definitely seems like a bug to me.
My best guess is that it is a problem with the double buffering.
I added so debug code to the paintComponent() method.
1) When you click on the right component only its paintComponent() method is called and the panel is painted the proper color.
2) When you click on the left component only its paintComponent() method is called and the panel is painted the proper color, however the panel on the right reverts back to the black color, without invoking the paintComonent() method on the right panel. This leads me to believe that somehow an old buffer is being used (this would be the bug and I have no idea how to fix it).
The reason that getParent().repaint() works is because this forces both components to be repainted no matter which panel you click on.
I'm not sure the cause of your problem, since I cannot reproduce the misbehavior with your code, but your paintComponent override neglects to call the super's paintComponent method. Put that in and see what happens.
#Override // method should be protected, not public
protected void paintComponent(Graphics g) {
// ******* add
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, 100, 100);
}
Note that if this were my program, and this were the only painting behavior I desired, I'd simplify my program my not overriding paintComponent but rather simply calling setBackground(color); I'd also create a Color array or List and iterate through it:
public static final Color[] COLORS = {Color.RED, Color.Blue, Color.Green};
private int colorIndex = 0;
// .....
#Override
public void mousePressed(MouseEvent mEvt) {
colorIndex++;
colorIndex %= COLORS.length;
color = COLORS[colorIndex];
setBackground(color);
}
I would also override the getPreferredSize method.
e.g.,
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyColorsPanelDemo extends JPanel {
private static final int GAP = 20;
public MyColorsPanelDemo() {
setLayout(new GridLayout(1, 0, GAP, GAP));
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
add(new ColorPanel());
add(new ColorPanel());
}
private static class ColorPanel extends JPanel {
public static final Color[] COLORS = {Color.red, Color.green, Color.blue};
private static final int PREF_W = 100;
private static final int PREF_H = PREF_W;
private static final Color INIT_BACKGROUND = Color.black;
private int colorIndex = 0;
public ColorPanel() {
setBackground(INIT_BACKGROUND);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent mEvt) {
setBackground(COLORS[colorIndex]);
colorIndex++;
colorIndex %= COLORS.length;
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
private static void createAndShowGui() {
MyColorsPanelDemo mainPanel = new MyColorsPanelDemo();
JFrame frame = new JFrame("MyColorsPanelDemo");
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();
}
});
}
}

GUI programming, FlowLayout blocking other things on JFrame(?)

The idea of the program is that I have some buttons and an icon SOMEWHERE on the frame. I want the buttons to change the color. I'm only worried about making all the elements show up right now. If I comment out lines 11-13, I see "hello," printed out, with a red circle on top of it. Otherwise, I just have the button "red" without "hello" or my red circle. So here's my code:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class ButtonTester
{
public static void main (String[] args)
{
JFrame frame = new ButtonFrame();
frame.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton redButton = new JButton("Red");
frame.add(redButton);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class ButtonFrame extends JFrame
{
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
public ButtonFrame()
{
setTitle("Hello");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
ButtonPanel panel = new ButtonPanel();
add(panel);
}
}
class ButtonPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hello !", 100, 100);
Icon ico = new ColorIcon(32);
ico.paintIcon(null, g, 75, 75);
}
}
I'm 90% sure the problem is lines 11-13, but I'm not sure what to change to make everything visible.
Your problem is that your ButtonPanel's size is 0. Have it override getPreferredSize() and you will see what I mean:
class ButtonPanel extends JPanel {
private static final int PREF_W = 150;
private static final int PREF_H = PREF_W;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hello !", 100, 100);
// !! Icon ico = new ColorIcon(32);
// Icon ico = new ImageIcon();
// ico.paintIcon(null, g, 75, 75);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Also as an unrelated aside, why are you creating an Icon inside of the paintComponent method? This doesn't make sense to me and would only serve to needlessly slow your graphics down.
Edit
You state:
Ok, I see the difference after overriding getPreferredSize() But what would be the "better" or "correct" way to create the icon? I'm just trying to follow the directions for an exercise out of a Java textbook: Exercise 4.14. Write a program that shows a frame with three buttons labeled "Red", "Green", and "Blue", and a label containing an icon showing a circle that is initially red. As the user clicks the buttons, the fill color of the circle should change. When you change the color, you need to invoke the repaint method on the label. The call to repaint ensures that the paintIcon method is called so that the icon can be repainted with the new color.
You need to think on this a different way. Myself I'd create three ImageIcons one for a blue circle, one for red, and one for green. I'd then display the ImageIcon in a JLabel on my JFrame. I'd change the color by simply swapping the label's icons via its setIcon(...) method. I wouldn't worry about futzing with paintComponent(...) but rather would try to solve this in as simple a fashion as possible.

Categories

Resources