Hi everyone. After christmas I have to hand in a Java assignment that consists of a GUI that can show either a square or a circle in a white viewing area, and alows the user to alter various attributes of the shape using various widgets at the side of the viewing area. I've finished the assignment, but because it's such a basic task a lot of marks will be handed out for style and sensible implementation. If any of you have some time could you please give my code a quick check through and let me know if my style is ok. I think i've followed all the guidelines to the letter, but i'm quite new to java.
Also, is my implementation of the square and circle class sensible? I've implemented them both as extensions of JPanel and given them their own paintComponent() methods to draw the corresponding shapes. I've then put them in a cardLayout stored in the viewing area JPanel.
Any pointers on my code would be much appreciated, thanks. If you run the code nothing will happen until you press the reset button (part of the specification).
My only comments would be that the application class name (mjbarr_shapes) should be capitalized, you don't need to redeclare the default constructor and call super() if you're not going to do anything extra there, and I'd probably make the duplicated calculations in the shape drawing [(int) ((getWidth() - shapeWidth) / 2), etc.] into private methods just for tidiness and elegance (always eliminate duplicate code where possible).
Out of clutter, find simplicity. From discord, find harmony. In the middle of difficulty, lies opportunity...
A. Einstein
Please use [CODE]...your code here...[/CODE] tags when posting code. If you get an error, please post the full error message and stack trace, if present.
I don't like the application class name at all, but we were told to name it that (mjbarr is my username at university) so he can identify what it is and who wrote it.
I'll write those private methods, silly me for not thinking of that. Thanks for the help.
in the interests of future expansion, you should consider adding attributes for the Height (though for circles and squares they are the same as the width). you can then say the program may be easily modified to draw another shape by writing a class for it
additionally, i think you reference the square and the circle directly and draw them using individual lines of code:
Code:
//set the width to the shape
square.setShapeWidth(newWidth);
circle.setShapeWidth(newWidth);
if your project is as a teaching point of inheritance, code re-use and abstraction, you will have only one shape; declared to be of type Shapes, but instantiated to be either a square or a circle:
Code:
private Shapes theShape;
theShape = new Square();
in either case you can set the width, height, and tell it to draw; both shapes have a draw method, and if theShape is of type Square, a square will be drawn, if it is of type Circle, a circle will be drawn. This makes adding new shapes a doddle; you extend the abstract class, and add them to the radiobutton group. clicking a radio button makes a new instance of the specific shape, but all code calls theShape generic class. THis is also partly why i recommended having a height attribute in the shape, even if it is set = to width for circle/square (or ignored, and use width only in fillRect/Oval)..
I'm not sure what a javadoc comment is, how is it different to the comments i have in my assignment? I don't remember my lecturer mentioning them, but it may be that i'm just being forgetful.
I wrote the assignment in textpad (the text editor the university uses and recommends), and it automatically indents code if it is in a .java file. I tried to clean it up when i posted it as a .text file but i guess it didn't work.
About the rest of your posts, I think I understand what you mean but I don't have my assignment here with me so I can't work on it. As soon as i get home i'll try adn do what you said. Again, thanks for your help
Ok, i think i've made the changes you suggested about having just one Shapes object but i'm having trouble getting it to display now the shapeArea.add(...) statement isn't in the constructor. I've altered the resetButton listener to this:-
Code:
public void actionPerformed(ActionEvent e)
{
if (e.getSource() instanceof JButton)
{
if (!resetPressed)
{
resetPressed = true;
}
theShape = new Square();
shapeArea.add(theShape, BorderLayout.CENTER);
theShape.repaint();
.....plus various lines of code to set the widget settings as before
}
}
the lines that set the widget settings work fine as before, but nothing the shape is no longer shown. Does the shapeArea.add() statement only work in the constructor, and if so how do I go about adding theShape to shapeArea and getting it to display? I tried the above method with and without the call to repaint() and neither worked. Can anyone tell me where i'm going wrong?
(The code compiles fine, and it runs without runtime errors. No errors are produced on the console when the reset button is pressed either. It simply fails to display the shape. The widget settings are changed to the correct values, so the handler is being called fine).
Javadoc.exe is in your SDK/bin folder. It is used to turn your source files into html class documentation like you see in the SDK documentation. I know the Netbeans IDE and the Sun Studio both support auto commenting GUI/tags into your source code to handle the Javadoc parser. It's intuitive and easy on the eyes.
I think this assignment to draw shapes using inheritance is in every first java course. You might consider that a shape is not a Jpanel but is its own base type. All it has to do is draw itself which if you look at your existing code boils down to the drawing function having access to a Graphics object. This allows you to paint a square and a circle on the same pane if you want, overlapping them. This removes extra object overhead of jPanel and allows you to paint a shape on anything that can supply a graphics panel to the draw function.
i get your point about it not needing to be a JPanel, but i'm not sure how to get it to draw on another JPanel. It wouldn't have the paintComponent method if it didn't extend something like JPanel would it?
This is a really lame example and I'm sure there are a dozen things wrong with it, but it does show how one might paint an object that is not a Component.
Code:
import javax.swing.*;
import java.awt.*;
class Tester{
public static void main(String[] args){
Sample sample = new Sample();
JFrame myJFrame = new JFrame("Test");
myJFrame.setSize(300, 300);
myJFrame.setContentPane(sample);
myJFrame.show();
}
}
class Sample extends JPanel{
Square mySquare;
Sample(){
mySquare = new Square();
}
public void paint(Graphics page){
page.setColor(Color.BLACK);
mySquare.doThing(page, 100);
}
}
class Square{
public void doThing(Graphics page, int size){
page.fillRect(0,0,size,size);
}
}
Note: I am a novice, take anything I say as coming from such.
To display shapes you need to pass the Graphics object of your container. Every AWT or SWING component has a member function called getGraphics() which returns a Graphics object. Say you have JPanel you want to put the shapes on.
Code:
public class myshapesapp extends JFrame
{
private JPanel displaypanel = new Jpanel();
myshapesapp() {
Shape shapes[] = new Shape[10];
shapes[0] = new Circle();
shapes[1] = new Square();
shapes[2] = new Triangle();
add(displaypanel);
/// now you can call drawmyshapes()
drawmyshapes();
}
drawmyshapes(){
Graphics g = displaypanel.getGraphics(g);
for(int i=0;i<10;i++)
shapes[i].draw(g);
}
}
I'm a little rusty on the java code but this is the basic idea. Since all shapes must be able to draw themselves you could be "more object oriented" by declaring an ABSTRACT draw(Graphics g) function. Derived classes (circle,square,etc) will override this draw function. If you don't understand this then leave it out as your instructor may ask you why you would do something like this.
The only thing about this code is that drawmyshapes() only draws the shapes once per call. Thats to say if another window covers it then uncovers the java app you will need to make another call to drawmyshapes(). Since this is school work I leave to you to work out the specifics of making your displaypanel draw the shapes on demand. There are a couple approaches, one you have already done.
Originally posted by cjard
if your project is as a teaching point of inheritance, code re-use and abstraction, you will have only one shape; declared to be of type Shapes, but instantiated to be either a square or a circle:
code:--------------------------------------------------------------------------------
private Shapes theShape;
theShape = new Square();
--------------------------------------------------------------------------------
I understand why I should do it in this way, but how do I change the object from square to circle and vice versa when the radio button is pressed? We were specifically told not to rediscover them from the values of the widgets (ie they should be stored in the shape object, and only update specific values when they change). I originally thought i'd be able to cast between square and circle because the object is of type Shapes but when i tried that i got a scroll of runtime messages on the dos prompt. Is there an easy way to do it?
how do I change the object from square to circle and vice versa when the radio button is pressed?
I haven't been following very closely, but I would suggest simply creating an instance of each shape (Square or Circle) at the start, then when the radiobutton switches between them, simply hide the current shape and show the hidden shape. That way, you can keep your shape hierarchy tidy and you separate display considerations from shape creation:
Code:
Circle circle = new Circle(...);
Square square = new Square(...);
...
Shape currentShape = circle; // default
...
// toggle shape:
currentShape = (currentShape instanceof Square) ? circle : square;
...
displayShape(currentShape);
Or whatever...
Everything should be made as simple as possible, but not simpler...
A. Einstein
Please use [CODE]...your code here...[/CODE] tags when posting code. If you get an error, please post the full error message and stack trace, if present.
I originally thought i'd be able to cast between square and circle because the object is of type Shapes but when i tried that i got a scroll of runtime messages on the dos prompt. Is there an easy way to do it?
casting for these purposes works in only one direction: downwards from the generic to the specific.
You declare an object of type Shape. and it is very general. it has height, width and filled properties. ALL shapes have these
Then when you make a specific example of a circle, or square, it has specific properties of that particular shape, but you can still refer to it as a Shape. There is a separation here though.. and you cannot say:
Circle is-a Shape. Square is-a Shape, therefore a Circle is-a Square. In taking your generic shape object, you can cast it into what it is suppsoed to be, but you cannot convert it from one thing to another
A real world example. Imagine there is a blue bus, a cyan car and a violet van in the university car park. You point to them and tell a friend
"there are 3 vehicles out there" - no problem.
"Look at that blue bus." - no problem.
"look at that cyan vehicle" - no problem.
"look at that violet car" - your mate looks at you like you had too many joints last night
java works in the same way; you can have 3 shapes, all of which are of type Shape, all of which have changeable generic properties, and all of which can be told to draw() themselves.. but theres where the implementation changes. a circle specifically takes the width and draw()s a circle, a square specifically draw()s a square, but using the same set of attributes that a circle has (width, height and fill)
hence, you have in your project:
a area reserved in your GUI, for a JPanel
the area with the JPanel will actually be given a Shape, because a Shape is-a JPanel
when the the JPanel is add()ed to the gui where it should be, java will tell it to draw()
at this moment, if the JPanel is of type Shape is of type Circle, a circle will be drawn
and if its a JPanel of type Shape of type Square,a square will be drawn. can you see how this works?
remember: to your gui, youre supposed to add the generic supertype "Shape"
you can then, so long as you keep a reference to that, set it to be either a circle or a square, simply by re-pointing to the relevant shape. Joe Nellis is actually pretty close with his code, some scope problems, and probably mine isnt perfect, but it outlines the idea:
Code:
public class myshapesapp extends JFrame
{
private JPanel toBeDrawn = new Jpanel();
//this will make it easier for our program to support
//up to 10 different shapes
Shape[] allShapes = new Shape[10];
//these will be array indexes
final int CIRCLE_TYPE = 0;
final int SQUARE_TYPE = 1;
myshapesapp() {
//add the drawing area
this.add(toBeDrawn);
//add the radio buttons, sliders etc
this.add(...)
}
actionlistener(source){
if(source == circle_radiobutton){
//check to see if we made a circle already
if(allShapes[CIRCLE_TYPE] == null)
allShapes[CIRCLE_TYPE] = new Circle(...);
//now we cause java to draw the Circle
toBeDrawn = allShapes[CIRCLE_TYPE];
//remember, toBeDrawn is-a JPanel
//and Circle is-a Shape, which is-a JPanel
//so java knows it can call paint() / repaint() on the JPanel
//Because Shape is-a JPanel, it is also guaranteed to
// have a paint() and repaint() method
//Now the trick of it is, that Circle has a paint() that should
// paint a circle.
//Java doesnt care what paint() does.. it just calls it
toBeDrawn.repaint();
}
else if(source == square_radiobutton){
//check if we made a square already?
if(allShapes[SQUARE_TYPE] == null)
allShapes[SQUARE_TYPE] = new Square(...);
//its guaranteed not to be null now, so we draw it
toBeDrawn = allShapes[SQUARE_TYPE];
toBeDrawn.repaint();
}
}
}
do you see, how easy it would be to add a triangle?
You just make another FINAL INT for the triangle, numbered 2 (or anything up to 9)
add a radiobutton for the triangle
make that clicking on the radiobutton first checks if a triangle exists already, if not, make it
then draw it
the idea of making the triangle in the radiobutton check code, is that we dont have to make it eslewhere; sure you can make it in the constructor for jFrame, just like all the other shapes can be made there too, but then youre ending up where youre editing line 6 of the code, then line 15, then line 27, then line 42....
its easier to maintain your code if you say "to add a new shape, make a final int for it, then do the rest of the code in the radiobutton handler" - it keeps it all together
the idea of keeping your "hidden" Shapes in an array, is that you can use a loop to iterate over the array when you change a slider/checkbox- move the slider and all the shape sizes change.. remmeber, that a Circle/Square/Triangle/Rectangle is guaranteed to have a setWidth and setHeight, because they are all of type Shape, and Shape has those methods. Those methods always do identical things. It is the draw method that uses them in disfferent ways. The draw method is always called by java. and java doesnt care what is drawn
do you see how the inheritance works?
Last edited by cjard; January 15th, 2004 at 07:08 AM.
* The Best Reasons to Target Windows 8
Learn some of the best reasons why you should seriously consider bringing your Android mobile development expertise to bear on the Windows 8 platform.