I am taking an introductory java class and I have an inheritance related problem. I have to implement a system that has classes for various figures. the superclass is Figures and subclasses are Circle, Box and Rectangle and I am supposed to output the figures in the JPanel. I have designed all the classes and my code is below but I need to define the center() method on the figure class which when called on an object should draw and also erase the object. so within the main method of the JPanel class, I want to use the center() method to draw the three shapes and use the wait(long timeout) method to make the shapes change to random positions after 15 seconds.
import java.awt.Graphics;
import javax.swing.JPanel;
public abstract class Figure extends JPanel {
protected int xCoord;
protected int yCoord;
public Figure() {
}
public void erase() {
}
public void draw() {
}
public void center() {
}
}
the rectangle sub class is:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Rectangle extends Figure {
int width;
int height;
public Rectangle() {
this.width = 0;
this.height = 0;
}
public Rectangle(int xCoord, int yCoord, int width, int height)
{
this.xCoord = xCoord;
this.yCoord = yCoord;
this.width = width;
this.height = height;
}
public void draw(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(xCoord, yCoord, width, height);
}
public void erase(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 0, 0);
}
}
the circle subclass:
public class Circle extends Figure {
private int radius;
public Circle ()
{
this.radius = 0;
}
public Circle (int xCoord, int yCoord, int radius)
{
this.xCoord = xCoord;
this.yCoord = yCoord;
this.radius = radius;
}
public void draw(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(xCoord, yCoord, radius, radius);
}
public void erase(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(0,0,0,0);
}
}
the box subclass:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Box extends Figure {
private int edgeLength;
public Box() {
this.edgeLength = 0;
}
public Box(int xCoord, int yCoord, int edgeLength)// Creating Rectangle
// Class given width and
// height
{
this.xCoord = xCoord;
this.yCoord = yCoord;
this.edgeLength = edgeLength;
}
public void draw(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(xCoord, yCoord, edgeLength, edgeLength);
}
public void erase(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 0, 0);
}
}
the JPanel class
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SamplePanel extends JPanel {
private static final int PANEL_HEIGHT = 400;
private static final int PANEL_WIDTH = 400;
Rectangle rect = new Rectangle(75, 85, 50,30);
Circle circle = new Circle(275, 75, 50);
Box box = new Box (75, 275, 50);
public SamplePanel() {
setPreferredSize(new Dimension(PANEL_HEIGHT, PANEL_WIDTH));
setBackground(Color.WHITE);
//Rectangle rect = new Rectangle(100, 100, 15,10);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
rect.draw(g);
circle.draw(g);
box.draw(g);
g.drawLine(200, 0, 200, 400);
g.drawLine(0, 200, 400, 200);
}
/**
* #param layout
*/
public SamplePanel(LayoutManager layout) {
super(layout);
}
/**
* #param isDoubleBuffered
*/
public SamplePanel(boolean isDoubleBuffered) {
super(isDoubleBuffered);
}
/**
* #param layout
* #param isDoubleBuffered
*/
public SamplePanel(LayoutManager layout, boolean isDoubleBuffered) {
super(layout, isDoubleBuffered);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Window Title Here");
SamplePanel panel = new SamplePanel();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
Figure box = new Box(75, 275, 50);
}
}
Answer...
How to use Swing Timers
It is safe to use within the context of Swing as it won't block the Event Dispatching Thread while waiting and will generate notifications within the context of the Event Dispatching Thread, making it safe to update the UI from.
Observations...
Your basic design, while correct in concept, is implemented in correctly. I'd would strongly discourage you from using JPanel in this way. Components have a particular life cycle which you're not managing correctly, which could cause no end of issues.
Instead, go right back to basics. What is it the shape should be able to?
public interface Figure {
public void draw(Graphics2D g2d);
public Rectangle getBounds();
public void setBounds(Rectangle bounds);
}
Here, it's capable of been painted and has a concept of size and position (yes, you can use an abstract class, but this removes all requirements on the base implementation, making it far more flexible)
Because I imagine most of the implementations are going to be basically the same, I can use an abstract class to implement the core functionality...
public abstract class AbstractFigure implements Figure {
private Rectangle bounds;
#Override
public Rectangle getBounds() {
return bounds;
}
#Override
public void setBounds(Rectangle bounds) {
this.bounds = bounds;
}
}
Based on your needs, you might be able to create a few abstract classes which implement the functionality different, but the basic idea is to see what functionality is common to all the implementations and reduce the amount of duplicate code you generate.
Then you can start implementing your concrete classes...
public class RectangleFigure extends AbstractFigure {
public RectangleFigure(Rectangle bounds) {
setBounds(bounds);
}
#Override
public void draw(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fill(getBounds());
}
}
But where's the "erase" method you ask? Well, erase is just the absence of painting, so, when you want to "erase" a shape, remove it from the painting process and repaint the container, shape is erased
This is suggests to me that you need to have a look at Painting in AWT and Swing and Performing Custom Painting to better understand how painting works in Swing.
"Where's the center method?" I hear you ask. Okay, you "could" have a center method in the Figure, the problem is, Figure has no concept of the parent container, is you'd have to pass the size of the container to it. Not difficult, I'd argue, however, this really isn't a function of the Figure, but a function of the container, so I'd have the center method implemented there, since you want to change the position of the Figures anyway, it makes sense (to me) to all that wrapped up in the container
Related
I have an array of 100 random ints. I want to create a bar graphic from them. I know how to create a single rectangle in a frame but without passing values.
This is the draw class:
import javax.swing.*;
import java.awt.*;
public class draw extends JPanel
{
public void drawing() {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(100, 150, 12, 3);
}
}
I want to replace the values in g.fillRect(100,150,12,3) with my random int values. But since i call repaint() from main to call paintComponent, i cant pass values to paintComponent. How do i do it? if its impossible, what alternatives do i have?
You can do it by introducing fields and intitialize then using a constructors, setters, or both :
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Draw extends JPanel {
int x, y, width, height;
public Draw(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
void setX(int x) {this.x = x;}
void setY(int y) {this.y = y;}
void setWidth(int width) {this.width = width;}
void setHeight(int height) {this.height = height;}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(x, y, width, height);
}
}
How can I pass values to paintComponent
You can't pass values to paintComponent().
But since i call repaint() from main to call paintComponent, i cant pass values to paintComponent. How do i do it?
For whatever object you want to customize and draw by yourself, you can create a class for that and have a method like draw() to do the painting, for example:
//Just a normal class with a draw() method
class BarGraph{
private int x;
private int y;
private int width;
private int height;
private Color color;
public BarGraph(int x, int y, int width, int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void setColor(Color color){
this.color = color;
}
public void draw(Graphics g){
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
Then in your main panel for displaying your customized image:
class DrawingSpace extends JPanel{
private BarGraph barGraph;
public DrawingSpace(){
barGraph = new BarGraph(50, 50, 400, 100);
barGraph.setColor = (Color.RED);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g){
barGraph.draw(g); //draw your own customized object
}
}
}
So instead of trying to pass values to dictate how paintComponent draws, you can pass/set your values in your own class (in this case, BarGraph class). From the Graphics content, implement how you want it to be painted.
In your paintComponent, simply call the draw method which already has "a set of instructions" for how it should be drawn.
We cannot pass values to paintComponent method as parameters because, we are just overriding an existing method which is called by Swing. But we can have an instance variable (values in my example) in the class which is accessible inside paintComponent method (as suggested in other answers/comments).
I'm putting an example program below since, sometimes examples convey ideas better.
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
public class BarGraph extends JPanel
{
private static final int GRAPH_HEIGHT = 300;
private static final int BAR_WIDTH = 50;
private static final int GAP_BETWEEN_BARS = 20;
private static final int GRAPH_X_OFFSET = 50;
private int[] values;
public BarGraph(int[] values)
{
this.values = values;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.BLUE);
for (int i = 0; i < values.length; i++)
{
g.fillRect(
GRAPH_X_OFFSET + (i * (GAP_BETWEEN_BARS + BAR_WIDTH)),
GRAPH_HEIGHT - values[i],
BAR_WIDTH,
values[i]
);
}
}
public static void main(String[] args)
{
int[] graphValues = {100, 150, 50, 250, 200, 75};
JFrame frame = new JFrame("Graph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new BarGraph(graphValues), BorderLayout.CENTER);
frame.setBounds(300, 200, 600, 400);
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 am currently making a tile based game. Everything so far is working fine. However, I want the player to be able to add objects, like stone or wood to the screen when he/she presses the mouse button. I attempted this myself but it is not working. Here is what I have done, but is not working:
This is my KeyInput class, where all the keyboard and mouse events take place.
public static ArrayList<StoneTile> sTile = new ArrayList<StoneTile>();
public KeyInput(Handler handler) {
this.handler = handler;
}
public void tick(LinkedList<Square> object) {}
public void mousePressed(MouseEvent e){
int mx = e.getX();
int my = e.getY();
System.out.println("Pressed (X,Y): " + mx + " " + my);
sTile.add(new StoneTile(1,mx,my));
if(sTile.add(new StoneTile(1,mx,my))){
System.out.println("ADDED");
}
}
public void mouseReleased(MouseEvent e){
System.out.println("Released");
}
Here is my StoneTile class, this is what I want to add to screen:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.LinkedList;
public class StoneTile extends Tile {
Textures tex;
public StoneTile(int id,int x,int y) {
super(Textures.stoneArray[0], id);
Tile.x = x;
Tile.y = y;
}
public static void main(String[] args) {
}
public Rectangle getBounds(){
return new Rectangle(x,y,Tile.TILEWIDTH,Tile.TILEHEIGHT);
}
}
The Textures.stoneArray[0] is simply the image that I want to add to the screen.
The Tile.(instance variable, like x, y, TILEWIDTH, and TILEHEIGHT) is simply a Tile class that contains all the render methods for the tiles (grass, stone, etc). If anything is unclear I will clarify or if you need any code provided, then I will add it in.
Note - The ArrayList was just an idea that I had in mind, if there are more efficient ways of doing this or any better ideas, I am open to all of them.
Here is where I set the MouseListener. I set it in an init() method and then called in a run() method (last line):
private void init() {
BufferedImageLoader loader = new BufferedImageLoader();
level = loader.loadImage("level.png");
world = new worldLoader("res/worlds/world1.txt");
handler = new Handler();
WIDTH = getWidth();
HEIGHT = getHeight();
cam = new Camera(handler, Game.WIDTH / 2, Game.HEIGHT / 2);
setWIDTH(getWidth());
setHEIGHT(getHeight());
tex = new Textures();
//backGround = loader.loadImage("/background.jpg");
handler.addObject(new Coin(100, 100, handler, ObjectId.Coin));
handler.addObject(new newStoneTile(20,20,ObjectId.newStoneTile));
handler.addObject(new player_Square(100,100, handler, ObjectId.player_Square));
//handler.addObject(new OneUp(300, 150, handler, ObjectId.OneUp));
this.addKeyListener(new KeyInput(handler));
this.addMouseListener(new KeyInput(handler));
}
jcomponent, is this what you meant?
public class Window {
private static final long serialVersionUID = -6482107329548182911L;
static final int DimensionX = 600;
static final int DimensionY = 600;
public Window(int w, int h, String title, Game game) {
game.setPreferredSize(new Dimension(w, h));
game.setMaximumSize(new Dimension(w, h));
game.setMinimumSize(new Dimension(w, h));
JFrame frame = new JFrame();
frame.add(game);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
game.start();
}
}
Perhaps the best way to try and answer this question is to give a small example.
Essentially what needs to happen is the following (assuming my understanding of the problem is correct):
User clicks on the JComponent/JPanel to determine where to place a Tile. This will cause a MouseEvent that needs to be listened for and handled.
The JComponent/JPanel needs a MouseListener implementation which will create a new Tile object and add it to the List of the Tile objects. Once this is complete the JComponent/JPanel needs to know to repaint(). You do not override repaint() but rather paintComponent(Graphics g), which will be called by repaint() (eventually).
The paintComponent(Graphics g) method will iterate over the List of Tile objects, drawing them to the JComponent/JPanel using the Graphics context for the component.
To illustrate this I have simplified your problem. Note this isn't the best way to solve the problem since the Model (game logic) and the GUI should be separated, ideally using Model View Controller / Observer pattern.
First and most importantly is the GamePanel class, which extends JPanel. It's sole role in this example is to display the game graphically and handle mouse clicks. i.e. handling the list of tasks noted above.
GamePanel
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
public class GamePanel extends JPanel {
private List<Tile> tiles; // Stores the Tile objects to be displayed
/**
* Sole constructor for GamePanel.
*
* #param width the width of the game panel
* #param height the height of the game panel
*/
public GamePanel(int width, int height) {
tiles = new ArrayList<>();
setPreferredSize(new Dimension(width, height));
// Implement mouse events for when the JPanel is 'clicked', only using the
// mouse released operation in this case.
addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
// On mouse release, add a StoneTile (in this case) to the tiles List
tiles.add(new StoneTile(e.getX(), e.getY()));
// Repaint the JPanel, calling paint, paintComponent, etc.
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
// Do nothing
}
#Override
public void mousePressed(MouseEvent e) {
// Do nothing
}
#Override
public void mouseEntered(MouseEvent e) {
// Do nothing
}
#Override
public void mouseExited(MouseEvent e) {
// Do nothing
}
});
}
/**
* Draws the Tiles to the Game Panel.
*
* #param g the Graphics context in which to paint
*/
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // Make sure you do this
// For this example, using black as the color to draw
g.setColor(Color.BLACK);
// Iterate over the tile list and draw them to the JPanel
for (Tile tile : tiles) {
Rectangle tileRect = tile.getBounds();
g.fillRect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
}
}
}
Second is the GameFrame, which extends JFrame. This is just a basic JFrame which adds a GamePanel object. I've also included the main method which will ensure the GUI is initialized on the Event Dispatch Thread.
GameFrame
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GameFrame extends JFrame {
private static final String TITLE = "Tile Game"; // Game Frame Window Title
private final JPanel gamePanel;
/**
* Sole constructor for GameFrame.
*
* #param width the width of the game in pixels
* #param height the height of the game in pixels
*/
public GameFrame(int width, int height) {
gamePanel = new GamePanel(width, height);
}
/**
* Performs final configuration and shows the GameFrame.
*/
public void createAndShow() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(TITLE);
add(gamePanel);
pack();
setVisible(true);
}
/**
* Entry point for the program.
*
* #param args not used
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
GameFrame gameFrame = new GameFrame(640, 480);
gameFrame.createAndShow();
}
});
}
}
Finally, the other classes used in the example for completeness, Tile and StoneTile. Personally I don't see much benefit from using Rectangle inside the model, but each to their own and I wanted to keep the example somewhat similar to your currently implementation.
Tile
import java.awt.Rectangle;
public abstract class Tile {
private int x, y, width, height;
public Tile(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public Rectangle getBounds() {
return new Rectangle(x, y, width, height);
}
}
StoneTile
public class StoneTile extends Tile {
public StoneTile(int x, int y) {
super(x, y, 100, 100);
}
}
One final comment. I notice that your sTile ArrayList is static, this should probably not be the case as it belongs to the class rather than a particular instance of the class.
How do I draw the unique rectangle(cube) inside the oval:
http://i.stack.imgur.com/pmHTl.jpg
For me it is tricky to use graphics to draw a rectangle such as the one shown. Any advice on what to do.
Ok. I will try to make myself as clear as I can. What I have so far are the orange ovals and the slim gray oval that is behind it. I only need to create one of these "dots" in a class and i will make many objects of them. The Task I need help in is drawing the "rectangle" shape you see that is in the orange dot using J Component possibly. By request I will add a picture of what I have so far if this edit does not fulfill the need for you to understand my problem.
Thanks
Edit : Here is the Code that I have for creating the ovals if that is of interest to you-
public void paint(Graphics g) {
Color c = (Color.orange);
g.setColor(Color.gray);
g.fillOval(3,3,60,60);
g.setColor(c);
g.fillOval(0,0,60,60);
}
Edit: My Attempt at an SSCCE -->
NanoBot Class(Where I am Creating the Bot in paint)
/**
* #author (Omar Ahmed)
*/
import javax.swing.*;
import java.awt.*;
public class NanoBot extends Image
{
public NanoBot(int x, int y, int w, int h)
{
super(x,y,w,h);
}
public void paint(Graphics g) {
Color c = (Color.orange);
g.setColor(Color.gray);
g.fillOval(3,3,60,60);
g.setColor(c);
g.fillOval(0,0,60,60);
//g.setColor(Color.black);
//g.fillOval(10,20,10,10);
//g.fillOval(40,20,10,10);
}
}
And The Driver:
/** Bot Swarm
* Date: May, 2013
* Author: Omar Ahmed
*/
import java.awt.*;
import javax.swing.*;
public class Driver {
private JFrame win;
private NanoBot bot1;
public Driver() {
win = new JFrame(" Swarm ");
win.setLayout(null);
win.setVisible(true);
win.setBounds( 20, 20, 800, 700);
win.getContentPane().setBackground(Color.white);
bot1=new NanoBot(50,50,70,70);
win.add(bot1,0);
}
Hope This Helps
Your first step is to break down your requirements...
You need to draw 3 shapes, front, top, side.
The front's y position is offset by 0.412 of the overall height. It's width is 0.77 of the overall width.
The top's height is 0.412 of the overall height, and it has a horizontal inset of 0.2 of the overall width...
The side's x position is offset by 0.77 of the overall width and has an inset of 0.47 of the over all width.
This is all very important, as you want to ensure that the shapes can resize reasonably well...
Now, you could simply use Graphics#drawLine and Graphics#drawRectangle to build the shape, but that, to me, is a lot of work...
Instead the 2D Graphics is very powerful and contains many wonderful things, the one of interest today is the Shape API, which allows you to define many different shapes, which can be drawn or filled.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestChip {
public static void main(String[] args) {
new TestChip();
}
public TestChip() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
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);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = 44;
int height = 17;
Front front = new Front(width, height);
Top top = new Top(width, height);
Side side = new Side(width, height);
draw(g2d, front, Color.BLACK, Color.YELLOW);
draw(g2d, top, Color.BLACK, Color.GRAY);
draw(g2d, side, Color.BLACK, Color.DARK_GRAY);
g2d.dispose();
}
protected void draw(Graphics2D g2d, Shape shape, Color foreground, Color background) {
g2d.setColor(background);
g2d.fill(shape);
g2d.setColor(foreground);
g2d.draw(shape);
}
}
public class Front extends Path2D.Float {
public Front(float width, float height) {
float frontWidth = width * 0.77f;
float yOffset = height * 0.412f;
moveTo(0, yOffset);
lineTo(frontWidth, yOffset);
lineTo(frontWidth, height);
lineTo(0, height);
closePath();
}
}
public class Side extends Path2D.Float {
public Side(float width, float height) {
float xOffset = width * 0.77f;
float inset = height * 0.47f;
moveTo(xOffset, inset);
lineTo(width, 0);
lineTo(width, inset);
lineTo(xOffset, height);
closePath();
}
}
public class Top extends Path2D.Float {
public Top(float width, float height) {
float inset = width * 0.2f;
float shapeHeight = height * 0.412f;
moveTo(inset, 0);
lineTo(width, 0);
lineTo(width - inset, shapeHeight);
lineTo(0, shapeHeight);
closePath();
}
}
}
Your job is to now go away, study the example, study the referenced tutorials, study the associated API docs and figure out how to align the above shape within you circle and draw it's legs...
A hint. Make a "Bug" class that knows how to renderer all this and simply translate the position of the Graphics as required...
Hello people,
I am trying to write an animated character for a multitouch screen. I want my object to have 5 eyes and each of whose pupil to be dragged and dropped differently, within the eye of course.
I have tried to do it all in a single class and the problem seems to be assigning mouse handlers to each of the five pupils! In other words, if I move one pupil, all the pupils are moving.
Then, I resorted to using a bespoke class just to the pupil. When I use it by itself, the pupil is draggable. However, when I use it as an object in the eyes class, the pupil is static! No clicks registered, no mouse activity tracked.
I have looked at tutorials, other related issues with mouse handlers and could not make any progress. I changed the code a dozen times from various tutorials and suggestions before finally posting here. Any ideas where I am missing the cue? Any pointers would be greatly appreciated.
Thanks in advance.
PS: I know I am yet to put the constraints on the pupil movement within the eye.
Code for main eyes class:
package rollEyes;
import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FiveEyes extends JPanel
{
private static final long serialVersionUID = 1L;
private static final int SIZE = 512;
private int a = SIZE / 2;
private int b = a;
private int r = 4 * SIZE / 5;
private int n;
int circleSize=30;
Pupil dc = new Pupil(1);
public FiveEyes(int n)
{
super(true);
this.setPreferredSize(new Dimension(SIZE, SIZE));
this.n = n;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
a = getWidth() / 2;
b = getHeight() / 2;
int m = Math.min(a, b);
r = 4 * m / 5;
int r2 = Math.abs(m - r) / 2;
int numOfEyes = 5;
for (int i = 0; i < numOfEyes ; i++)
{
Graphics2D g2d2 = (Graphics2D) g;
double t = 2 * Math.PI * i / n;
int x = (int) Math.round(a + r * Math.cos(t));
int y = (int) Math.round(b + r * Math.sin(t));
drawEyeSockets(g2d2, x,y, 2*r2,2*r2);
}
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
create();
}
});
}
public void drawEyeSockets(final Graphics2D g2, int x, int y, int w, int h)
{
g2.drawOval(x,y,w,h);
dc.drawCircle(g2, x+12, y+12);
}
private static void create()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FiveEyes fivey = new FiveEyes(5);
f.add(fivey);
f.pack();
f.setVisible(true);
}
}
Code for the Pupil class:
package rollEyes;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Pupil extends JPanel
{
private static final long serialVersionUID = 1L;
int radius=50;
int x_after = 50;
int y_after = 50;
MouseHandler mh ;
private static int n =1;
public Pupil(int n)
{
super(true);
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
drawCircle(g2d,x_after,y_after);
}
public void drawCircle(final Graphics2D g2d, int x, int y)
{
g2d.setColor(Color.BLUE);
g2d.fillOval(x, y, radius/2, radius/2);
mh = new MouseHandler();
this.addMouseListener(mh);
this.addMouseMotionListener(mh);
}
private static void create()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Pupil dc = new Pupil(n);
f.add(dc);
f.pack();
f.setVisible(true);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
create();
}
});
}
private class MouseHandler extends MouseAdapter
{
boolean circleClicked=false;
public void mouseReleased(MouseEvent e)
{
circleClicked = false;
}
public void mousePressed(MouseEvent me)
{
circleClicked = true;
}
public void mouseDragged(MouseEvent me)
{
if (circleClicked)
{
x_after = me.getX();
y_after = me.getY();
repaint();
}
}
}
}
You have Pupil extend JPanel, but really shouldn't be doing that. Instead, use the concepts that you've learned in your current Pupil class -- how to draw a movable circle, and extend it in the larger FiveEyes class, only this time create a List<Pupil> and draw them. My suggestions:
Make Pupil not extend JPanel. Instead give it the machinery to draw circles in certain locations and to have that location changed.
Also you will need to give it a way to recognize if its circle has been clicked by giving it a contains(Point p) method. One way to do this is to use a Shape object, or you can roll your own method.
Give FiveEyes a List<Pupil> that in reality is an ArrayList<Pupil> and fill it with Pupil objects.
In FiveEyes paintComponent(...) method, iterate through this List telling each Pupil to draw itself.
In your FiveEyes MouseAdapter's mousePressed(...) method, iterate through your Pupil List to see if a Pupil has been clicked on. If so, move it.
Alternatively, you could create a Pupil BufferedImage, put it into an ImageIcon, and put that into a JLabel, and then allow your FiveEyes class's MouseAdapter to drag the labels around.
First of all, this code
mh = new MouseHandler();
this.addMouseListener(mh);
this.addMouseMotionListener(mh);
must only be called once. You're adding millions of mouse handlers to the component!
And you need five instances of Pupil, one for each eye. Right now, you have only one, so of course the rendering will only yield one result.
Lastly, you must make sure that only one eye is "active" at a time (i.e. not all of them should receive mouse events or not all of them should process them).