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.)
Related
I'm just learning how GUI works and I wanted to write a code where following happens:
firstly we see red rectangle
after a click it changes into a circle in gradient (I picked orange and pink) + the background is black.
The problem is, I don't know how to notify the change when I use repaint(), I tried creating the first picture with another method - fail, maybe I lack some knowledge. Currently we get just the second output that doesn't change after the click.
This is the code at the moment I got stuck:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGUI implements ActionListener {
JButton button;
JFrame frame;
public void work() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button = new JButton("Color change");
button.addActionListener(this);
mojpanel panel = new mojpanel();
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300,300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
}
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import javax.swing.*;
import java.awt.Graphics2D;
public class mojpanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
GradientPaint gradient = new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D) g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
}
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) {
SimpleGUI aplGUI = new SimpleGUI();
JFrame frame = new JFrame();
mojpanel panel = new mojpanel();
frame.add(panel);
aplGUI.work();
}
}
import java.awt.*;
public class Painting extends SimpleGUI {
public void paint(Graphics g) {
g.setColor(Color.red);
g.drawRect(100, 100, 100, 100);
g.fillRect(100, 100, 100, 100);
}
}
When you create new classes, you short invest more thought into them. You have two classes which are supposed to be interchangeable, one painting a red rectangle, the other painting a circle with a color gradient, with the ability to switch from the former to the latter.
So when you have one class extending JPanel and the other extending SimpleGUI, there is no way to exchange one for the other. Further, the names mojpanel and Painting do not reflect their purposes.
Besides that, you have it backward. Don’t implement an action that calls repaint(), followed by an attempt to recognize that repaint() has been called, to modify the GUI afterwards. Instead, change the GUI’s state and after the GUI has changed in a way that the visual appearance needs to be updated, call repaint. Note that most properties of the Swing components do already trigger an according repaint automatically when you change them.
You may create two classes extending JComponent having a custom paintComponent method and replace one with the other when the action has been triggered. But there’s a less intrusive way. Let the classes implement Icon and set the icon property of a component, e.g. a JLabel. This is one of the properties that will trigger the painting automatically:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SimpleGUI {
static class GradientOval implements Icon {
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
GradientPaint gradient
= new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D)g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
#Override
public int getIconWidth() {
return 200;
}
#Override
public int getIconHeight() {
return 200;
}
}
static class RedRectangle implements Icon {
#Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(Color.RED);
g.fillRect(100, 100, 100, 100);
}
#Override
public int getIconWidth() {
return 200;
}
#Override
public int getIconHeight() {
return 200;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel content = new JLabel(new RedRectangle());
JButton button = new JButton("Change To Circle");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
content.setIcon(new GradientOval());
}
});
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, content);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
I don’t know which level of Java knowledge you have. The code
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
content.setIcon(new GradientOval());
}
});
creates an instance of an anonymous inner class implementing ActionListener. You can simplify this code using a lambda expression:
button.addActionListener(e -> content.setIcon(new GradientOval()));
To demonstrate the interaction between component properties and repaints, here an approach using a custom component:
import java.awt.*;
import javax.swing.*;
public class SimpleGUI {
static class DualAppearance extends JComponent {
private boolean first = true;
public boolean isFirst() {
return first;
}
public void setFirst(boolean shouldBeFirst) {
if(shouldBeFirst != first) {
first = shouldBeFirst;
repaint();
}
}
public void next() {
if(first) {
first = false;
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
if(first) {
g.setColor(Color.RED);
g.fillRect(100, 100, 100, 100);
}
else {
GradientPaint gradient
= new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D)g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(200, 200);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DualAppearance content = new DualAppearance();
JButton button = new JButton("Change To Second");
button.addActionListener(e -> content.next());
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, content);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
This DualAppearance component follows the usual pattern. When being requested to paint itself, it will always paint itself according to the current state. This may happen multiple times without a state change, due to other reasons, e.g. being requested by the system. When its own state changes and requires a repaint, which only this component can know, it will invoke repaint.
You can easily modify this example code to toggle between both appearances by replacing
JButton button = new JButton("Change To Second");
button.addActionListener(e -> content.next());
with
JButton button = new JButton("Toggle");
button.addActionListener(e -> content.setFirst(!content.isFirst()));
I am currently working on a simple pong-like game, but I got stucked with positioning the rectangle.
I want to change it's Y position to be at the half of the actual panel's height.
package com.game;
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame("gEngine");
Player playerOne = new Player(frame);
Player playerTwo = new Player(frame);
}
}
package com.game;
import javax.swing.*;
import java.awt.*;
public class Player {
public Player(JFrame frame) {
MyPanel panel = new MyPanel();
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.BLACK);
frame.setSize(1280, 720);
frame.setVisible(true);
}
}
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(50, 60, 20, 120);
}
}
Your code has a lot of "problems". I suggest you to find some kind of tutorials or something. You frame.setVisible(true) and stuff inside Player's class constructor. Do you realize that every time you create a Player object, all these things will be applied to the JFrame? Is this necessary? Maybe you should do them only once. Also in order to paint the compnent according to its position according size, you can do g.fillRect(50, getHeight() / 2, 20, 120);
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame("gEngine");
Player playerOne = new Player();
Player playerTwo = new Player();
// Set the proper layout manager
frame.setLayout(new GridLayout());
frame.add(playerOne.getMyPanel());
frame.add(playerTwo.getMyPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBackground(Color.BLACK);
frame.setSize(1280, 720);
frame.setVisible(true);
}
public static class Player {
private JPanel myPanel;
public Player() {
this.myPanel = new MyPanel();
}
public JPanel getMyPanel() {
return myPanel;
}
}
static class MyPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
// let the component be painted "natural"
super.paintComponent(g);
// Do custom painting
g.setColor(Color.WHITE);
g.fillRect(50, getHeight() / 2, 20, 120);
}
}
}
Edit (based on comment):
The background is the default one because GridLayout splits the screen into 2 panels (in the middle of the frame). Even the frame has BLACK background, these 2 panels cover it. So the background you see is from the panels, and not from the frame. In order to change it you will have to change the background of the panels (and make them non-transparent):
static class MyPanel extends JPanel {
public MyPanel() {
super();
setOpaque(true);
setBackground(Color.BLACK);
}
#Override
public void paintComponent(Graphics g) {
// let the component be painted "natural"
super.paintComponent(g);
// Do custom painting
g.setColor(Color.WHITE);
g.fillRect(50, getHeight() / 2, 20, 120);
}
}
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.
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;
I am trying to mess around with some graphics in java, however i can't get it to work. The JFrame comes up with the button i created, but the JFrame is just gray with no red line that i want it to draw.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Shapes extends JFrame implements ActionListener{
JButton button = new JButton("click");
public Shapes() {
setVisible(true);
setSize(500, 500);
button.addActionListener(this);
button.setSize(20, 20);
setLayout(new FlowLayout());
add(button);
repaint();
}
public static void main(String[] args){
Shapes s = new Shapes();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
g2.drawLine(5, 10, 10, 20);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button){
repaint();
}
}
}
Two things:
1). You don't really want to do custom painting on a top-level container such as a JFrame. Instead you want to use a JPanel
class Panel extends JPanel
{
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
g2.drawLine(5, 10, 10, 20);
}
}
And add it to your JFrame: add(new Panel()); (Or create an object if you want).
2). setVisible(true); should be the very last thing that you do while setting up your window. So change your constructor:
public Shapes() {
setSize(500, 500);
button.addActionListener(this);
button.setSize(20, 20);
setLayout(new FlowLayout());
add(button);
add(new Panel()) // added from part 1
repaint();
setVisible(true);
}
For more information go through the "performing custom painting tutorials."