So, I need to make the stick-man movable by a user-input. When the user clicks on a part (Head, hands, feet and posterior) and he should move, and have no idea how to go about this..
If possible, there also needs to be a confine around the character, likely rectangular, so that there is a limit to how far each part can be pulled.
See below for my code;
// Created by Charlie Carr - (28/11/17 - /11/17)
import java.awt.*;
import java.applet.Applet;
import javax.swing.*;
import java.awt.Graphics;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
//Imports complete
//Suppress warning about undeclared static final serialVersionUID field in VS Code
#SuppressWarnings("serial")
public class Animator extends JPanel {
public static class AnimatorWindow extends JPanel {
public void paint(Graphics page) {
setBackground(Color.gray);
setForeground(Color.white);
super.paintComponent(page);
page.drawString("Stickmen Animation Station", 150, 15);
//draw the head
//x1, y1, x2, y2
page.drawOval(90, 60, 20, 20);
// draw the body
page.drawLine(100, 80, 100, 110);
// draw the hands
page.drawLine(100, 90, 80, 105);
page.drawLine(100, 90, 120, 105);
//draw the legs, he hasn't a leg to stand on..
page.drawLine(100, 110, 85, 135);
page.drawLine(100, 110, 115, 135);
}
}
public static void main(String[] args) {
AnimatorWindow displayPanel = new AnimatorWindow();
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(displayPanel, BorderLayout.CENTER);
//declare window size
int x = 480;
int y = 240;
JFrame window = new JFrame("GUI");
window.setContentPane(content);
window.setSize(x, y);
window.setLocation(101, 101);
window.setVisible(true);
}
}
Use MouseListenerto deal with mouse events.
Also, you should override the paintComponent() method instead of paint(), because paint() also paints the border and other stuff.
public static class AnimatorWindow extends JPanel implements MouseListener{
public AnimatorWindow(){
setBackground(Color.gray);
setForeground(Color.white);
//add the listener
addMouseListener(this);
}
public void paintComponent(Graphics page) {
super.paintComponent(page);
//You should not alter the Graphics object passed in
Graphics2D g = (Graphics2D) page.create();
//draw your stuff with g
g.drawString("Stickmen Animation Station", 150, 15);
.......
//finish
g.dispose();
}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseClicked(MouseEvent e){
//implement your clicking here
//Use e.getX() and e.getY() to get the click position
}
}
For more on swing events, check this site
EDIT: Your problem also includes animation, and you can use a javax.swing.Timer to do that.
Related
I'm using Mouselistener to make my head's eyes open when MouseInside is true, and to close when MouseInside is false. (aka when the mouse is hovering over the head the eyes are open and when it's not they're closed). I started off by creating the class Head which extends JPanel, and creating the private boolean mouseInside. I then created the dimension, added a border and then created the nested class MyMouseListener which initialized the boolean as true or false depending on the position of the mouse, then calling repaint. I then added the mouselistener to my Head Object. Below this I constructed my Head object, and the eye objects depending on whether the mouseInside boolean was true or false. Below that I created my JFrame to demonstrate and construct the Head.
I'm not sure why it's not not working, here's my code (I'm a new programmer)..
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
public class Head extends JPanel
{
private boolean mouseInside;
public Head(boolean mouseInside)
{
this.setPreferredSize(new Dimension(500, 500));
this.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
class MyMouseListener extends MouseAdapter {
public void mouseEntered(MouseEvent e) {
final boolean mouseInside = true;
repaint();
}
public void mouseExited(MouseEvent e) {
final boolean mouseInside = false;
repaint();
}
}
this.addMouseListener(new MyMouseListener());
}
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
//face
g2.setStroke(new BasicStroke(3.0f));
g2.setPaint(Color.YELLOW);
g2.fill(new Ellipse2D.Double(10, 200, 120, 140));
//eyes.. open in mouseinside is true and closed if not
if (mouseInside == true) {
g2.setPaint(Color.WHITE);
g2.fill(new Ellipse2D.Double(90, 250, 20, 20));
g2.setPaint(Color.WHITE);
g2.fill(new Ellipse2D.Double(40, 250, 20, 20));
}
else if (mouseInside == false) {
g2.setPaint(Color.BLACK);
g2.fill(new Rectangle2D.Double(90, 250, 20, 5));
g2.setPaint(Color.BLACK);
g2.fill(new Rectangle2D.Double(40, 250, 20, 5));
}
//nose
g2.setPaint(Color.ORANGE);
g2.fill(new Rectangle2D.Double(65, 270, 20, 20));
//mouth
g2.setStroke(new BasicStroke(4.0f));
g2.setPaint(Color.RED);
g2.fill(new RoundRectangle2D.Double(50,300,50,15,15,10));
}
public static void main(String[] args) {
JFrame f = new JFrame("Head demo");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLayout(new FlowLayout());
f.add(new Head(true));
f.add(new Head(false));
f.add(new Head(false));
f.pack();
f.setVisible(true);
}
}
Just remove mouseInside parameter that you pass to the Head class constructor or change its name.
Your class variable mouseInside is shadowed with that parameter.
It's very difficult to read your code as format. However, you seem to have a variable of the same name in different scopes.
final boolean mouseInside = false;
Isn't going to do anything. Remove the final boolean. (The old restriction on accessing finals of enclosing context only referred to parameters and locals of methods and constructors, not fields of objects and classes.)
So I have a custom JComponent (An almost completed button, if you will). Here is the source code to the class:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LukeButton extends JComponent implements MouseListener{
//ArrayList of listeners
private final ArrayList<ActionListener> listeners = new ArrayList<ActionListener>();
Shape rec = new RoundRectangle2D.Float(10, 10, 110, 60, 50, 75);
BasicStroke border = new BasicStroke(5);
SpringLayout layout = new SpringLayout();
private String text;
public LukeButton(String text){
this.text = text;
this.setLayout(layout);
this.addMouseListener(this);
}
//Adds a listeners to the list
public void addActionListener(ActionListener e){
listeners.add(e);
}
//Called when button is provoked
public void fireActionListeners(){
if(!listeners.isEmpty()){
ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "LukeButton");
for(ActionListener l: listeners){
l.actionPerformed(evt);
}
}
}
//Listens for click on my component
public void mousePressed(MouseEvent e){
if(rec.contains(e.getPoint())){
rec = new RoundRectangle2D.Float(10, 10, 100, 55, 50, 75);
repaint();
fireActionListeners();
}
}
public void mouseReleased(MouseEvent e){
if(rec.contains(e.getPoint())){
rec = new RoundRectangle2D.Float(10, 10, 110, 60, 50, 75);
repaint();
}
}
//When mouse enters, make border bigger
public void mouseEntered(MouseEvent e){
border = new BasicStroke(8);
repaint();
}
//When mouse leaves, make border smaller
public void mouseExited(MouseEvent e){
border = new BasicStroke(5);
repaint();
}
public Dimension getPreferredSize(){
return new Dimension(130, 80);
}
//Draws my button
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.BLACK);
g2.setStroke(border);
g2.draw(rec);
g2.setColor(new Color(0, 204, 204));
g2.fill(rec);
g2.setColor(Color.BLACK);
g2.drawString(text, 47, 45);
}
//Methods that must be over written.
public void mouseClicked(MouseEvent e){
}
}
My problem is that I do not know how to center the text variable (More or less what the variable consists of) based on the size of the String. The beggining of the String is always in a fixed point. For example if the text variable is equal to something short, the string is going to be far on the left. But if the string is too long, it goes far off the right side of the component. Does anyone know how to center my text variable so it changes it's position based on the size of the string(or a different/better solution of coarse)? Thanks for taking your time to read :)
You can get the Rectangle required to paint the text by using:
FontMetrics fm = g2d.getFontMetrics();
Rectangle2D rect = fm.getStringBounds(text, g2d);
Then to center the text you would get the x/y positions using something like:
int x = (getSize().width - rect.width) / 2;
int y = ((getSize().height - rect.height / 2) + rect.height;
In this program a polygon is created to show up into a JPanel tab.
In-order to make it show I had to over-ride the shape and create a setter method for it. Unfortunately it is not showing and the program is not running either.
The error:
Exception in thread "main" java.lang.IllegalArgumentException: adding
a window to a container
at SelectShape component1 = new SelectShape(x, y, vert); in method
Page1.
The only way it would work is by making a frame and removing the JTab and assigning the shape onto the frame but that is not what I want to make. I want to make a program that can distribute shapes to * different tabs* using one graphics method.
Here is the code:
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
/* This program create a graphics component that draws a polygon
*/
public class SelectShape extends JFrame
{
private JTabbedPane tabbedPane;
private JPanel panel1;
// //////////////////////////
static int[] x = { 20, 40, 50, 65, 80, 95 }; // Co-ords for a polygon
static int[] y = { 60, 105, 105, 110, 95, 95 };
static int vert = 6;
public SelectShape() throws IOException // Builds GUI
{
setTitle("Program");
setSize(900, 600);
setBackground(Color.gray);
JPanel topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);
// Create the tab pages
createPage1();
// Create a tabbed pane
tabbedPane = new JTabbedPane();
tabbedPane.addTab("Shape Panel", panel1);
}
public void createPage1() throws IOException // Creates JPanel
{
panel1 = new JPanel();
panel1.setLayout(null);
SelectShape component1 = new SelectShape(x, y, vert); //error
SelectShape component2 = new SelectShape(x, y, vert); //over-rides shape
component1.setBounds(290, 70, 120, 40);
component2.setBounds(290, 70, 120, 40);
panel1.add(component1); // is not displayed!
panel1.add(component2); // component2 overwrites component1!!!
panel1.setVisible(true);
}
// overrides javax.swing.JComponent.paintComponent
public void paintComponent(Graphics g) {
// Recover Graphics2D
Graphics2D g2 = (Graphics2D) g;
// Construct a polygon then draw it
Polygon polygon = new Polygon(x, y, vert);
g2.draw(polygon);
g2.fill(polygon);
}
public SelectShape(int[] x, int y[], int vert) { // setter method
this.x = x;
this.y = y;
this.vert = vert;
}
public static void main(String[] args) throws IOException {
SelectShape mainFrame = new SelectShape(); //Frame
mainFrame.setVisible(true);
}
}
I think you are mixing many concepts together in your code which eventually leads to ununderstandable code.
JFrame does not extends JComponent and does not have a paintComponent method. Consider using the #Override annotation on methods that override another. This will allow you to easily mistakes like this.
No need to extend JFrame anyway and never override the paint() method of a Top-level container (JDialog, JFrame, ...)
Always invoke the super method of paintXXX methods
public SelectShape(int[] x, int y[], int vert) { // setter method is not a setter method. It is a constructor that takes 3 arguments and assign them. In all cases, this does absolutely nothing in your case because you made those variables static. Avoid the use of static unless if you describe constants, in which case it should be also followed by the final keyword.
Start the UI, and perform all modifications to the UI, on the Event Dispatching Thread (EDT). This can easily be done by using SwingUtilities.invokeLater().
The error you are seeing: Exception in thread "main" java.lang.IllegalArgumentException: adding a window to a container is thrown because you are trying to add a JFrame to a JComponent which is forbidden. JFrame cannot be added to anything. If you want to do that, you need to use JDesktopPane and add JInternalFrame (but that is another story).
I am not too sure as to what you are trying to achieve, but here is a working code derived from yours which works much better:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
/* This program create a graphics component that draws a polygon
*/
public class SelectShape extends JPanel {
// Constants
private static final int[] x = { 20, 40, 50, 65, 80, 95 }; // Co-ords for a polygon
private static final int[] y = { 60, 105, 105, 110, 95, 95 };
private static final Polygon POLYGON = new Polygon(x, y, Math.min(x.length, y.length));
private static final Ellipse2D CIRCLE = new Ellipse2D.Double(100, 40, 45, 45);
// Class variables
private final Shape shape;
private Dimension preferredSize;
public SelectShape(Shape shape) {
this.shape = shape;
Rectangle bounds = shape.getBounds();
this.preferredSize = new Dimension(bounds.x + bounds.width, bounds.y + bounds.height);
}
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.BLUE);
g2.draw(shape);
g2.fill(shape);
}
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame mainFrame = new JFrame("Program");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SelectShape polygon = new SelectShape(POLYGON);
SelectShape circle = new SelectShape(CIRCLE);
// Create a tabbed pane
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Polygon", polygon);
tabbedPane.addTab("Circle", circle);
mainFrame.add(tabbedPane);
mainFrame.pack();
mainFrame.setVisible(true);
}
});
}
}
hi there i'm trying to improve myself about java2D and first of all i'm dealing with drawing polygons. However, i can not see the polygon on frame. I read some tutorials and examples but as i said i face with problems. here is the sample code of drawing a polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.JFrame;
public class jRisk extends JFrame {
private JFrame mainMap;
private Polygon poly;
public jRisk(){
initComponents();
}
private void initComponents(){
mainMap = new JFrame();
mainMap.setSize(800, 600);
mainMap.setResizable(false);
mainMap.setVisible(true);
mainMap.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int xPoly[] = {150,250,325,375,450,275,100};
int yPoly[] = {150,100,125,225,250,375,300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
}
protected void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
/**
* #param args
*/
public static void main(String[] args) {
new jRisk();
}
}
JFrame does not have a paintComponent(Graphics g) method. Add the #Override annotation and you will get a compile time error.
1) Use JPanel and override paintComponent (you would than add JPanel to the JFrame viad JFrame#add(..))
2) Override getPreferredSize() to return correct Dimensions which fit your drawing on Graphics object or else they wont be seen as JPanel size without components is 0,0
3) dont call setSize on JFrame... rather use a correct LayoutManager and/or override getPrefferedSize() and call pack() on JFrame after adding all components but before setting it visible
4) Have a read on Concurrency in Swing specifically about Event Dispatch Thread
5) watch class naming scheme should begin with a capital letter and every first letter of a new word thereafter should be capitalized
6) Also you extend JFrame and have a variable JFrame? Take away the extend JFrame and keep the JFrame variable as we dont want 2 JFrames and its not good practice to extend JFrame unless adding functionality
Here is your code with above fixes (excuse picture quality but had to resize or it was going to 800x600):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JRisk {
private JFrame mainMap;
private Polygon poly;
public JRisk() {
initComponents();
}
private void initComponents() {
mainMap = new JFrame();
mainMap.setResizable(false);
mainMap.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int xPoly[] = {150, 250, 325, 375, 450, 275, 100};
int yPoly[] = {150, 100, 125, 225, 250, 375, 300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
mainMap.add(p);
mainMap.pack();
mainMap.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JRisk();
}
});
}
}
As per your comment:
i am preparing a map which includes lots of polygons and yesterday i
used a JPanel over a JFrame and i tried to check if mouse was inside
of the polygon with MouseListener. later i saw that mouseListener gave
false responds (like mouse is not inside of the polygon but it acts
like it was inside the polygon). so i deleted the JPanel and then it
worked
Here is updated code with MouseAdapter and overridden mouseClicked to check if click was within polygon.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JRisk {
private JFrame mainMap;
private Polygon poly;
public JRisk() {
initComponents();
}
private void initComponents() {
mainMap = new JFrame();
mainMap.setResizable(false);
mainMap.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int xPoly[] = {150, 250, 325, 375, 450, 275, 100};
int yPoly[] = {150, 100, 125, 225, 250, 375, 300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
if (poly.contains(me.getPoint())) {
System.out.println("Clicked polygon");
}
}
};
p.addMouseListener(ma);//add listener to panel
mainMap.add(p);
mainMap.pack();
mainMap.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JRisk();
}
});
}
}
JFrame does not extend JComponent so does not override paintComponent. You can check this by adding the #Override annotation.
To get this functionality extract paintComponent to a new class which extends JComponent. Don't forget to call super.paintComponent(g) rather than super.paintComponents(g).
Replace
protected void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
With
protected void paint(Graphics g){
super.paint(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tetris extends JFrame {
public Tetris() {
add(new GamePanel());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
setSize(800, 600);
setVisible(true);
setLocationRelativeTo(null);
setTitle("Tetris");
}
public class GamePanel extends JPanel {
public GamePanel(){
TetrisBoard tetraBoard= new TetrisBoard();
GridBagLayout layout= new GridBagLayout();
this.setLayout(layout);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 2;
c.gridy = 1;
c.ipadx = 190;
c.ipady = 390;
c.insets.left= 360;
layout.setConstraints(tetraBoard, c);
this.add(tetraBoard);
setBackground(Color.WHITE);
}
#Override
public void paint(Graphics g){
super.paint(g);
g.setFont(new Font("Birth Std", Font.PLAIN, 12));
g.setColor(Color.LIGHT_GRAY);
g.drawString("200", 36, 63);
g.drawString("200", 36, 88);
g.drawString("200", 36, 114);
}
}//GamePanel class
public class TetrisBoard extends JPanel implements Runnable{
private Thread animator= new Thread(this);
private final int DELAY= 50;
public TetrisBoard(){
setFocusable(true);
//setBackground(Color.WHITE);
setDoubleBuffered(true);
//this.setBackground(Color.BLACK);
setOpaque(false);
}
#Override
public void addNotify() {
super.addNotify();
animator = new Thread(this);
animator.start();
}//addNotify
#Override
public void paint (Graphics g){
super.paint(g);
g.drawRect (20, 30, 130, 50);
}//paint
#Override
public void run() {
long beforeTime, timeDiff, sleep;
beforeTime = System.currentTimeMillis();
while (true) {
repaint();
timeDiff = System.currentTimeMillis() - beforeTime;
sleep = DELAY - timeDiff;
if (sleep < 0)
sleep = 2;
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
beforeTime = System.currentTimeMillis();
}
}
}//TetrisBoard class
public static void main(String[] args) {
Tetris t = new Tetris();
}
}
With this code the result is that it doesn't paint anything at all. I just want the background to be transparent not the images painted over the background, but it looks like the paint method doesn't paint if I setOpaque(false).
Edit: as requested I posted a simple code, TetraBoard is added to the GamePanel (using that GridBagLayout), and GamePanel is added to the frame, those 3 classes are separate files. I want TetraBoard to have a transparent background, so that I can see the background of GamePanel, but what I paint on tetraboard must be visible. If I setOpaque(false), TetraBoard is transparent, but it set on transparent everything I paint on it.
Edit: Assuming I understand what you're trying to do, replace the following line in the TetrisBoard constructor:
setOpaque(false);
with:
setBackground(new Color(0,0,0,0));
For example:
JPanel p = new JPanel() {
#Override
public void paintComponent(Graphics g) { // as suggested Andrew
g.setColor(Color.RED);
g.drawArc(0, 0, 100, 100, 0, 360); // arc will be painted on transparent bg
}
};
p.setBackground(new Color(0, 0, 0, 0)); // as suggested Perry
...
So, you have to do two actions:
1) override paintComponent(Graphics g) of JPanel
2) and set bg color to transparent: new Color(0, 0, 0, 0)