I wanted to have a simple program that changes the width of the rectangle with a slider.
When i run this the jpanel doesn't work properly as it fits just the panel.width width and it doesn't repaint properly.
import java.awt.*;
import javax.swing.*;
public class panel extends JPanel {
private int width = 50;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GRAY);
g.fillRect(20, 20, width, 25);
}
public void setWidth(int width) {
this.width = (width > 0) ? width : 0;
repaint();
}
public int getWidth() {
return width;
}
}
GUI:
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class Gui extends JFrame {
private panel p;
private JSlider slider;
Gui() {
super("Draw program");
setDefaultCloseOperation(EXIT_ON_CLOSE);
p = new panel();
p.setBackground(Color.RED);
slider = new JSlider(SwingConstants.HORIZONTAL, 0, 300, p.getWidth());
slider.setMajorTickSpacing(50);
slider.setPaintTicks(true);
add(p);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
p.setWidth(slider.getValue());
}
});
}
}
Your panel class overrides a key method of the Component class (which JPanel inherits from): getWidth.
The problem with this is that the Swing layout managers may use the value from this method to size the component.
Solution: Change the method name.
e.g.,
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class Gui extends JFrame {
private MyPanel p;
private JSlider slider;
Gui() {
super("Draw program");
setDefaultCloseOperation(EXIT_ON_CLOSE);
p = new MyPanel();
p.setBackground(Color.RED);
slider = new JSlider(SwingConstants.HORIZONTAL, 0, MyPanel.GRAY_MAX_LENGTH, p.getGrayWidth());
slider.setMajorTickSpacing(50);
slider.setPaintTicks(true);
add(p);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
p.setGrayWidth(slider.getValue());
}
});
}
// so we can test our code
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Gui gui = new Gui();
gui.pack();
gui.setLocationRelativeTo(null);
gui.setVisible(true);
}
});
}
}
// note that this class should not be named
// panel as class names should start with an upper-case letter
class MyPanel extends JPanel {
// constants to avoid use of "magic" numbers
public static final int GRAY_MAX_LENGTH = 300;
private static final int RECT_HEIGHT = 25;
private static final int GAP = 20;
private static final int PREF_W = GRAY_MAX_LENGTH + 2 * GAP;
private static final int PREF_H = RECT_HEIGHT + 2 * GAP;
private int grayWidth = 50;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GRAY);
g.fillRect(GAP, GAP, grayWidth, RECT_HEIGHT);
}
#Override // so our GUI is big enough
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public void setGrayWidth(int width) {
this.grayWidth = (width > 0) ? width : 0;
repaint();
}
// ***** key is to change this method's name so you don't
// override JPanel's getWidth() method
public int getGrayWidth() {
return grayWidth;
}
}
Related
Program should draw the line twice longer after clicking the button, but it only does that after clicking AND THEN resizing the window. I don't know what is happening, i thought this is gonna be easy.
Could you tell me how can I fix it?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Random;
import java.util.Stack;
class MyButtonPanel extends JPanel {
public static final int HEIGHT = 800;
public static final int WIDTH = 800;
private JButton greenButton;
private JPanel buttonPanel;
Stack<Point> points;
int X = 25;
int Y = 25;
public MyButtonPanel() {
greenButton = new GreenButton();
points = new Stack<Point>();
buttonPanel = this;
setLayout(new FlowLayout());
setPreferredSize(new Dimension(WIDTH, HEIGHT));
add(greenButton);
}
class GreenButton extends JButton implements ActionListener
{
GreenButton() {
super("LongerLine");
addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
//points.push(new Point(0,0));
X = 2 * X;
Y = 2 * Y;
validate();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//g2d.setColor(Color.WHITE);
//g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.BLACK);
//drawLines(g2d);
Line2D lin = new Line2D.Double(0,0, X, Y);
g2d.draw(lin);
}
private void drawLines(Graphics2D g2d) {
//Line2D lin = new Line2D.Float(100, 100, 250, 260);
//g2d.draw(lin);
double x1, y1, x2, y2;
/*
for(Point point: points) {
x1 = (double) point.getX();
y1 = (double) point.getY();
Line2D line = new Line2D.Double(x1,y1,200,200);
g2d.draw(line);
}
*/
}
}
public class MyActionFrame extends JFrame {
public MyActionFrame() {
super("Test akcji");
JPanel buttonPanel = new MyButtonPanel();
add(buttonPanel);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
//setResizable(false);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MyActionFrame();
}
});
}
}
This validate();, or better revalidate(); is called when a container's layout needs to be re-done, often when components are added or removed, and is not what you're doing or desiring. Instead you want to call repaint() which suggests that the component be painted.
Frame -> MainContentPane -> MapPanel -> Map -> Tile[]
JFrame -> JPanel(new GridBagLayout) -> JPanel -> JPanel(new GridLayout(31,30) -> JComponent
I'm trying to paint 32x32 pix tiles on a JPanel, but no matter where I call repaint(); it will only paint the tiles if I call validate(); on the JFrame.
If I bypass Panels and paint directly (use of the draw() methods) onto the MapPanel the images will only paint if I resize or move the JFrame so that the Frame has to be repainted by the repaintmanager. However calling repaint(), validate() or both on my ContentPanel will not paint the images.
If I understand how Java.swing paints thing right, if I call a repaint on the highest level Component it should repaint all child Components that the repaintmanager thinks should be repainted. Since I am adding components after the frame has been set to visible I need to call validate() on the highest level Component to tell the repaintmanager that there are new things. Am I right with this understanding?
Things that will not work:
telling me to add all the components before setting the frame to visible. The Map and Tile[] are going to be changing quite regularly, and it would be very impractical to reset the Frame every time I add/remove something.
public class NetScore {
public static void main(String[] args) {
MapPanel mapPanel = new MapPanel();
InfoPanel infoPanel = new InfoPanel();
ImageLoader imageLoader = new ImageLoader();
Player player = new Player("Tester", imageLoader);
JPanel contentPane = new MainContentPane((JPanel)mapPanel, (JPanel)infoPanel);
System.out.println(mapPanel.getHeight());
System.out.println(mapPanel.getWidth());
MapBuilder mapBuilder = new MapBuilder(player, imageLoader);
Map map = new Map(mapBuilder, mapPanel, player);
map.drawMap();
System.out.println();
}
}
public class MapPanel extends JPanel implements ActionListener{
Map map;
Timer clock;
public MapPanel(){
clock = new Timer(500, this);
clock.start();
}
public void addMap(Map map){
this.map = map;
this.add(map);
this.validate();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println(map == null);
System.out.println("paint mapPanel");
Graphics2D g2 = (Graphics2D) g;
if(map == null){
//map.draw(g2);
}
}
#Override
public void actionPerformed(ActionEvent e) {
//repaint();
}
}
public class MainContentPane extends JPanel implements ActionListener{
public JFrame frame;
Timer clock;
public MainContentPane(JPanel mapPanel ,JPanel infoPanel){
clock = new Timer(500, this);
clock.start();
frame = new Frame();
JPanel contentPane = new JPanel();
frame.setContentPane(contentPane);
contentPane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.weightx = 2;
c.gridx = 0;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
contentPane.add(mapPanel, c);
c.weightx = 1;
c.weighty = 1;
c.gridx = 1;
c.gridy = 0;
c.fill = GridBagConstraints.BOTH;
contentPane.add(infoPanel, c);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
class Frame extends JFrame{
public Frame(){
this.setTitle("netScore");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setBounds(100, 10, 1500, 1000);
this.setResizable(false);
}
}
}
public class Map extends JPanel{
private Tile[][] tiles;
private MapBuilder mapBuilder;
private Player player;
public Map(MapBuilder mapBuilder, MapPanel mapPanel, Player player){
this.player = player;
this.mapBuilder = mapBuilder;
this.setLayout(new GridLayout(31,30));
mapPanel.addMap(this);
}
public void loadMap(){
tiles = mapBuilder.buildMap();
}
public void drawMap(){
loadMap();
this.removeAll();
for(int i = 0; i < tiles.length; i++){
for(int p = 0; p < tiles[i].length; p++){
this.add(tiles[i][p]);
}
}
validate();
}
public void draw(Graphics2D g2){
if(tiles != null){
for(int i = 0; i < tiles.length; i++){
for(int p = 0; p <tiles[i].length; p++){
tiles[i][p].draw(g2, i*32, p*32);
}
}
}
}
// private class GlassPanel extends JComponent{
//
//
// #Override
// protected void paintComponent(Graphics g) {
// super.paintComponent(g);
// Graphics2D g2 = (Graphics2D) g;
// g2.drawImage(player.getImage(), player.getX(), player.getY(), null);
//
// }
//
// }
}
public class Tile extends JComponent{
private int id;
private boolean collision;
private BufferedImage image;
public Tile(char diCo, int id, ImageLoader imageLoader){
this.id = id;
collision = (Character.isUpperCase(diCo));
image = imageLoader.getTileImage(id, diCo);
setPreferredSize(new Dimension(32, 32));
}
// public Dimension getPreferredSize(){
// return new Dimension(32,32);
// }
public boolean isCollision() {
return collision;
}
public void draw(Graphics2D g2, int x, int y){
System.out.println("paint tile, id "+ id);
g2.drawImage(image, null, x, y);
}
public void paintComponent(Graphics g){
System.out.println("paint tile, id "+ id);
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, null, 0, 0);
}
}
Edit: Added minimal code.
This will work if I replace validate(); with revalidate(); But I don't want to use revalidate(); if nothing on the Panel needs to be invalided. Am I wright with that thinking?
public class test {
public static void main(String[] args) throws Exception {
MapPanel mapPanel = new MapPanel();
ContentPanel contentPanel = new ContentPanel((JPanel)mapPanel);
Map map = new Map();
mapPanel.add(map);
map.loadMap();
}
}
class MapPanel extends JPanel{
public MapPanel(){
//this.setBackground(Color.BLACK);
}
}
class Map extends JPanel{
BufferedImage image;
public Map(){
try {
image = ImageIO.read(new File("graphics//brick_brown0.png"));
} catch (IOException e) {
System.err.println("can't find file.");
}
setLayout(new GridLayout(31,30));
setPreferredSize(new Dimension(962,992));
}
public void loadMap(){
for(int i = 0; i < 30; i++){
for(int p = 0; p < 31; p++){
add(new Tile(image));
}
}
validate();
}
}
class Tile extends JComponent{
BufferedImage image;
public Tile(BufferedImage image){
this.image = image;
setPreferredSize(new Dimension(32,32));
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, null, null);
}
}
class ContentPanel extends JPanel implements ActionListener{
Timer clock = new Timer(100, this);
public ContentPanel(JPanel mapPanel){
clock.start();
setLayout(new BorderLayout());
JFrame frame = new Frame();
frame.setContentPane(this);
add(mapPanel);
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
private class Frame extends JFrame{
public Frame(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(100, 100, 1000, 1000);
}
}
}
The basic problem with the code as posted was that the JFrame was being set visible prior to the components being added. While there are ways to add components and make them visible after the top-level container becomes visible, they seem unnecessary in this case.
Here is a working version that uses an image generated at run-time, with a little space in the GridLayout to show that the grid is 31 x 30 components.
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class TestRepaint {
public static void main(String[] args) throws Exception {
MapPanel mapPanel = new MapPanel();
Map map = new Map();
mapPanel.add(map);
map.loadMap();
new ContentPanel((JPanel) mapPanel);
}
}
class MapPanel extends JPanel {
public MapPanel() {
this.setBackground(Color.RED);
}
}
class Map extends JPanel {
BufferedImage image;
public Map() {
image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_BGR);
setLayout(new GridLayout(31, 30,2,2));
//setPreferredSize(new Dimension(962, 992));
}
public void loadMap() {
for (int i = 0; i < 30; i++) {
for (int p = 0; p < 31; p++) {
add(new Tile(image));
}
}
validate();
}
}
class Tile extends JComponent {
BufferedImage image;
public Tile(BufferedImage image) {
this.image = image;
setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, null, null);
}
}
class ContentPanel extends JPanel implements ActionListener {
Timer clock = new Timer(100, this);
public ContentPanel(JPanel mapPanel) {
clock.start();
setLayout(new BorderLayout());
JFrame frame = new Frame();
frame.setContentPane(this);
add(mapPanel);
frame.pack();
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
private class Frame extends JFrame {
public Frame() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
//setBounds(100, 100, 1000, 1000);
}
}
}
I am learning java gui interface and wrote a program that has a button. Each time the button is clicked, a random sized rectangle will be added to the screen. But instead of adding it to the screen, the program keeps erasing the old one, which I want to keep on the screen. Here is my code. I tried to do paint() and it did not work. Thanks in advance.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class SimpleGui implements ActionListener {
JFrame frame = new JFrame();
public static void main(String[] args){
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go(){
JButton button = new JButton("Add a rectangle");
MyDrawPanel panel = new MyDrawPanel();
button.addActionListener(this);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
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();
}
class MyDrawPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(Color.blue);
int height = (int) (Math.random()*120 + 10);
int width = (int) (Math.random()*120 + 10);
int x = (int) (Math.random()*40 + 10);
int y = (int) (Math.random()*40 + 10);
g.fillRect(x, y, height, width);
}
}
}
Your paintComponent method is written to draw only one rectangle, so its behavior should come as no shock to you. If you want it to draw multiple, you have one of two options:
Create an ArrayList<Rectangle>, and in the actionPerformed method, add a new random Rectangle to this List and then call repaint(). In the paintComponent method, iterate through this List with a for-loop, painting each Rectangle.
Or you could draw the new random rectangle onto a BufferedImage that is displayed by the paintComponent method.
The first method is the easier of the two, the 2nd is better if you're worried about program responsiveness, say in an animation program.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class TwoDrawRectMethods extends JPanel {
// Array to hold our two drawing JPanels
private AddRandomRect[] addRandomRects = {
new DrawList("Using List"),
new DrawBufferedImage("Using BufferedImage")};
// constructor
public TwoDrawRectMethods() {
// add drawing rectangles onto GUI
for (AddRandomRect addRandomRect : addRandomRects) {
add(addRandomRect);
}
// button to tell rectangles to add a new Rectangle
add(new JButton(new DrawAction("Add New Rectangle")));
}
// The button's Action -- an ActionListener on "steroids"
private class DrawAction extends AbstractAction {
public DrawAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
// tell both drawing JPanels to add a new rectangle
for (AddRandomRect addRandomRect : addRandomRects) {
addRandomRect.addRectangle();
}
}
}
private static void createAndShowGui() {
TwoDrawRectMethods mainPanel = new TwoDrawRectMethods();
JFrame frame = new JFrame("TwoDrawRectMethods");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
#SuppressWarnings("serial")
class DrawList extends AddRandomRect {
private static final Color RECT_COLOR = Color.RED;
private List<Rectangle> rectList = new ArrayList<>();
public DrawList(String title) {
super(title);
}
#Override
public void addRectangle() {
rectList.add(createRandomRect());
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(RECT_COLOR);
for (Rectangle rectangle : rectList) {
g2.draw(rectangle);
}
}
}
#SuppressWarnings("serial")
class DrawBufferedImage extends AddRandomRect {
private static final Color RECT_COLOR = Color.BLUE;
private BufferedImage img = null;
public DrawBufferedImage(String title) {
super(title);
}
#Override
public void addRectangle() {
if (img == null) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Rectangle rect = createRandomRect();
Graphics2D g2 = img.createGraphics();
g2.setColor(RECT_COLOR);
g2.draw(rect);
g2.dispose();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
}
}
#SuppressWarnings("serial")
abstract class AddRandomRect extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private Random random = new Random();
public AddRandomRect(String title) {
setBorder(BorderFactory.createTitledBorder(title));
}
abstract void addRectangle();
protected Rectangle createRandomRect() {
int x1 = random.nextInt(PREF_W);
int x2 = random.nextInt(PREF_W);
int y1 = random.nextInt(PREF_H);
int y2 = random.nextInt(PREF_H);
int x = Math.min(x1, x2);
int y = Math.min(y1, y2);
int width = Math.abs(x1 - x2);
int height = Math.abs(y1 - y2);
return new Rectangle(x, y, width, height);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
When I create a Board instance from my Square instance, I try to assign the size of the window to the integers x and y. I fail to do this because it seems like on start the size is 0. In the constructor in Board.java, x and y shouldn't be -50 like they end up now.
Square.java:
package Square;
import javax.swing.*;
public class Square extends JFrame {
public Square(){
add(new Board());
setSize(800, 800);
setVisible(true);
}
public static void main(String[] args){
new Square();
}
}
Board.java
package Square;
import javax.swing.*;
import java.awt.*;
public class Board extends JPanel{
int x,y;
public Board(){
x = width-50;
y = height-50;
}
public int width = (int) getSize().getWidth();
public int height = (int) getSize().getHeight();
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y, 100, 100);
}
}
Full Code for clarification:
Square.java
package Square;
import javax.swing.*;
public class Square extends JFrame {
public Square(){
Board board = new Board();
board.start();
add(board);
setTitle("Square");
setSize(800, 800);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args){
Square square = new Square();
square.setVisible(true);
square.setLocation(2000, 150);
}
}
Board.java
package Square;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Board extends JPanel implements ActionListener{
Timer timer;
int x, y;
int velX = 0;
int velY = 0;
public Board(){
setFocusable(true);
timer = new Timer(1, this);
addKeyListener(new TAdapter());
}
class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
switch(keyCode){
case KeyEvent.VK_ESCAPE: x = width()/2-50; y = height()/2-50; break;
case KeyEvent.VK_RIGHT: velX = 1; break;
case KeyEvent.VK_DOWN: velY = 1; break;
case KeyEvent.VK_LEFT: velX = -1; break;
case KeyEvent.VK_UP: velY = -1; break;
}
}
public void keyReleased(KeyEvent e){
velX = 0;
velY = 0;
}
}
public int width(){ return (int) getSize().getWidth();}
public int height(){ return (int) getSize().getHeight();}
public void start(){
timer.setInitialDelay(100);
timer.start();
x = width()/2-50;
y = height()/2-50;
}
#Override
public void actionPerformed(ActionEvent e) {
x += velX;
y += velY;
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x,y, 100, 100);
}
}
Put the calculation into your paintComponent method. In general you want to avoid having code in paintComponent that will slow it down, but these calls shouldn't give too much penalty. Also, if your program is re-sizable, you'll need to be able to re-calculate the sizes of things on the go like this:
public void paintComponent(Graphics g){
super.paintComponent(g);
int x = getWidth() - 50;
int y = getHeight() - 50;
g.fillRect(x, y, 100, 100);
}
but of course in your real program, you will want to avoid "magic" numbers
Another issue: don't call setSize() on your JFrame. Instead, if you want to specify a hard size, do so in the JPanel by overriding its getPreferredSize() method. This will also then give you the suggested parameters that can be used for your calculations.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawRect extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = PREF_W;
private static final int DELTA = 50;
private static final Color RECT_COLOR = Color.red;
private static final int RECT_WIDTH = 100;
private static final int TIMER_DELAY = 15;
private int rectX = PREF_W - DELTA;
private int rectY = PREF_H - DELTA;
public DrawRect() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(RECT_COLOR);
g.fillRect(rectX, rectY, RECT_WIDTH, RECT_WIDTH);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
rectX--;
rectY--;
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawRect());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Also, check out the key bindings animation code from this answer of mine.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
public class GamePanel extends JPanel {
private static final int ANIMATION_DELAY = 15;
private final int HEIGHT = 400;
private final int WIDTH = 600;
private Square square;
private EnumMap<Direction, Boolean> dirMap = new EnumMap<>(Direction.class);
private Map<Integer, Direction> keyToDir = new HashMap<>();
// !! private Circle circle;
private Timer animationTimer;
public GamePanel() {
for (Direction dir : Direction.values()) {
dirMap.put(dir, Boolean.FALSE);
}
keyToDir.put(KeyEvent.VK_UP, Direction.UP);
keyToDir.put(KeyEvent.VK_DOWN, Direction.DOWN);
keyToDir.put(KeyEvent.VK_LEFT, Direction.LEFT);
keyToDir.put(KeyEvent.VK_RIGHT, Direction.RIGHT);
// !! addKeyListener(new DirectionListener());
setKeyBindings();
setBackground(Color.white);
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setFocusable(true);
square = new Square();
animationTimer = new Timer(ANIMATION_DELAY, new AnimationListener());
animationTimer.start();
}
private void setKeyBindings() {
int condition = WHEN_IN_FOCUSED_WINDOW;
final InputMap inputMap = getInputMap(condition);
final ActionMap actionMap = getActionMap();
boolean[] keyPressed = { true, false };
for (Integer keyCode : keyToDir.keySet()) {
Direction dir = keyToDir.get(keyCode);
for (boolean onKeyPress : keyPressed) {
boolean onKeyRelease = !onKeyPress; // to make it clear how bindings work
KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, 0,
onKeyRelease);
Object key = keyStroke.toString();
inputMap.put(keyStroke, key);
actionMap.put(key, new KeyBindingsAction(dir, onKeyPress));
}
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
square.display(g);
}
private class AnimationListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent evt) {
boolean repaint = false;
for (Direction dir : Direction.values()) {
if (dirMap.get(dir)) {
square.move(dir);
repaint = true;
}
}
if (repaint) {
repaint();
}
}
}
private class KeyBindingsAction extends AbstractAction {
private Direction dir;
boolean pressed;
public KeyBindingsAction(Direction dir, boolean pressed) {
this.dir = dir;
this.pressed = pressed;
}
#Override
public void actionPerformed(ActionEvent evt) {
dirMap.put(dir, pressed);
}
}
private static void createAndShowGUI() {
GamePanel gamePanel = new GamePanel();
JFrame frame = new JFrame("GamePanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
gamePanel.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
enum Direction {
UP(0, -1), DOWN(0, 1), LEFT(-1, 0), RIGHT(1, 0);
private int incrX;
private int incrY;
private Direction(int incrX, int incrY) {
this.incrX = incrX;
this.incrY = incrY;
}
public int getIncrX() {
return incrX;
}
public int getIncrY() {
return incrY;
}
}
class Square {
private int x = 0;
private int y = 0;
private int w = 20;
private int h = w;
private int step = 1;
private Color color = Color.red;
private Color fillColor = new Color(255, 150, 150);
private Stroke stroke = new BasicStroke(3f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
public void display(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(fillColor);
g2d.fillRect(x, y, w, h);
g2d.setStroke(stroke);
g2d.setColor(color);
g2d.drawRect(x, y, w, h);
g2d.dispose();
}
public void setStep(int step) {
this.step = step;
}
public void move(Direction dir) {
x += step * dir.getIncrX();
y += step * dir.getIncrY();
}
}
Hey your codestyle is horrible, but i try to help :). You can't get a size of an undrawn window. First Things first, your Constructor is wrong, you add the Board that actually create the Board Obj. Calling the Constructor of Board, which has no drawn parent yet and no x,y set. Try to initialize your variables in the Constructor. So just use width and height and fill the values in the constructor. Next, just tell your board its creation size by passing its parent size trough constructor variables.
I think you try to learn java and this is much more elegant. Furthermore, try to do all parent modification before adding some to it. So first setSize, add some Layout (Border/Flow/whatuwish) then get the frames ContentPane and add your Board component. To make things clear, you can't get e.g. the parent and parent size in Contructor because your board Obj isn't created and added yet. If you wish to getParent() and its size, create the Object add it to JFrame and than you can call getParent().getSize(). You get 0 because your JPanel isn't drawn at this time (before creation). If you wish to get the Parent Size just pass the JFrame Ref to Constructor or its size. Another Advise, don't create things in things in things, keep in mind with your code you create your JPanel as first Obj... Here is some example code:
Square:
public class Square extends JFrame {
public static void main(String[] args){
Square square = new Square();
square.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Dimension d = new Dimension(800,800);
square.setPreferredSize(d);
square.setSize(d);
//too much, every Jframe has BorderLayout enabled
square.getContentPane().setLayout(new BorderLayout());
square.getContentPane().add(new Board(square), BorderLayout.CENTER);
square.pack();
square.setVisible(true);
}
}
Board:
public class Board extends JPanel{
int x,y;
JFrame parent;
public Board(JFrame parent){
int width = parent.getPreferredSize().width;
int height = parent.getPreferredSize().height;
x = width-50;
y = height-50;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.fillRect(x,y, 100, 100);
}
}
You can take x and y values after the panel has become visible, in the next EDT cycle, by using SwingUtilities.invokeLater, for example.
I have to build a GUI that takes 4 colorable shapes (MyShape class) that are going to be grey in the beginning. Later, writing the RGB values on the three JTextField on the bottom of the GUI i'll be able to set the new color that will paint every picture that I'll click later.
Everything works great except the fact that in the DocumentListener i can't use the setText method or I'll get an IllegalStateException. I'd like to call that method in order to correct a wrong value of an RGB component: for instance, if the user writes 500, the text will automatically set the JTextField to 255.
Here is the code of the full project, so that you can run it (in the code I commented the line right before the method with the problem (I know it's kinda long to read, thank you if you'll help me anyway! :) ):
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.util.Arrays;
import java.util.List;
public class ShapesGUI extends JFrame {
private ShapesPlayGround shapesPlayGround;
private ColorPreview colorPreview;
private RGB rgb;
private List<MyShape> shapeList;
public ShapesGUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
Container cnt = getContentPane();
shapesPlayGround = new ShapesPlayGround();
colorPreview = new ColorPreview();
rgb = new RGB();
cnt.add(shapesPlayGround, BorderLayout.CENTER);
cnt.add(colorPreview, BorderLayout.WEST);
cnt.add(rgb, BorderLayout.SOUTH);
pack();
setVisible(true);
}
public void setShapeList(List<MyShape> shapeList) {this.shapeList = shapeList;}
class ShapesPlayGround extends JPanel {
MyShape[] shapes;
public ShapesPlayGround() {
setPreferredSize(new Dimension(800, 450));
setBorder(new TitledBorder("Shapes"));
shapes = new MyShape[4];
shapes[0] = new MyShape(new Rectangle2D.Double(50, 250, 40, 180)); // Rettangolo, basso sinistra
shapes[1] = new MyShape(new Rectangle2D.Double(500, 100, 250, 250)); // Quadrato, estrema destra
shapes[2] = new MyShape(new Ellipse2D.Double(75, 50, 250, 120)); // Ellisse, alto sinistra
shapes[3] = new MyShape(new Ellipse2D.Double(310, 200, 230, 230)); // Cerchio, destra
setShapeList(Arrays.asList(shapes));
addMouseListener(new MyMouseListener());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (MyShape shape : shapes) {
g2.setPaint(shape.getColor());
g2.fill(shape.getShape());
g2.setPaint(Color.black);
g2.draw(shape.getShape());
}
}
class MyMouseListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
Point p = new Point(e.getX(), e.getY());
if (shapeList != null) {
for (MyShape shape : shapes) {
if (shape.getShape().contains(p)) {
shape.setColor(rgb.getColor());
}
repaint();
}
}
}
}
}
class ColorPreview extends JPanel {
int[] rgbValue = new int[3];
JPanel panel;
Shape preview;
public ColorPreview() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JLabel(" Preview "));
panel = new JPanel();
panel.setBorder(new TitledBorder("Color"));
add(panel);
}
public void setColor(int[] rgbValue) {this.rgbValue = rgbValue;}
public int[] getColor() {return rgbValue;}
public void paintColor() {
Graphics2D g2 = (Graphics2D)getGraphics();
preview = new Rectangle2D.Double(panel.getX() + 5, panel.getY() + 20, panel.getWidth() - 10, panel.getWidth() - 10);
g2.setPaint(rgb.getColor());
g2.fill(preview);
}
}
class RGB extends JPanel {
private JPanel[] rgbPanel = new JPanel[3];
private String[] panelTitles = {"Red", "Green", "Blue"};
private JTextField[] rgbText = new JTextField[3];
private JTextField[] partialColor = new JTextField[3];
private Shape[] rgbShape;
private int[] rgbValue = new int[3];
private Color color;
public RGB() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
for (int i = 0; i < 3; i++) {
rgbPanel[i] = new JPanel();
rgbPanel[i].setLayout(new GridLayout(1, 2));
rgbPanel[i].setBorder(new TitledBorder(panelTitles[i]));
rgbText[i] = new JTextField();
partialColor[i] = new JTextField();
rgbValue[i] = 0;
rgbText[i].setText("0");
rgbText[i].getDocument().addDocumentListener(new TextChanged(i));
partialColor[i].setBackground(Color.black);
partialColor[i].setEditable(false);
rgbPanel[i].add(rgbText[i]);
rgbPanel[i].add(partialColor[i]);
add(rgbPanel[i]);
}
color = new Color(rgbValue[0], rgbValue[1], rgbValue[2]);
}
public int[] getRgbValue() {return rgbValue;}
public Color getColor() {return color;}
public void setRgbValue(int[] rgbValue) {this.rgbValue = rgbValue;}
public void setColor(Color color) {this.color = color;}
class TextChanged implements DocumentListener {
private int i;
public TextChanged(int i) {this.i = i;}
#Override
public void insertUpdate(DocumentEvent e) {listen(i);}
#Override
public void removeUpdate(DocumentEvent e) {listen(i);}
#Override
public void changedUpdate(DocumentEvent e) {listen(i);}
private int fixValue(int value) {return value < 0 ? 0 : (value > 255 ? 255 : value);}
// HERE'S THE PROBLEM!!!
private void listen(int i) {
try {
rgbValue[i] = fixValue(Integer.parseInt(rgbText[i].getText()));
} catch (NumberFormatException e) {
rgbValue[i] = 0;
}
color = new Color(rgbValue[0], rgbValue[1], rgbValue[2]);
rgb.setColor(color);
colorPreview.paintColor();
try {
rgbText[i].setText("" + rgbValue[i]);
} catch (IllegalStateException e) {
System.out.println("~Shit, Exception");
}
if (i == 0) partialColor[0].setBackground(new Color(rgbValue[0], 0, 0));
else if (i == 1) partialColor[1].setBackground(new Color(0, rgbValue[1], 0));
else if (i == 2) partialColor[2].setBackground(new Color(0, 0, rgbValue[2]));
}
}
}
public static void main(String args[]) {
new ShapesGUI();
}
}
One possible solution is to wrap your change to the text in a Runnable and queue it on the event thread using SwingUtilitiles.invokeLater(yourRunnable), but better perhaps ..... you're trying to correct the input before it is fully registered in the text component. In this situation, don't use a DocumentListener, but rather use a DocumentFilter.
Better still -- use a JSlider or a JSpinner
e.g.,
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class SpinnerEg extends JPanel {
private Map<RGB, JSlider> sliderMap = new EnumMap<>(RGB.class);
private JPanel displayPanel = new JPanel();
private Color displayPanelColor = new Color(0, 0, 0);
public SpinnerEg() {
displayPanel.setBackground(displayPanelColor);
JPanel sliderPanel = new JPanel(new GridLayout(1, 0));
for (RGB rgb : RGB.values()) {
createRgbSlider(sliderPanel, rgb);
}
displayPanel.setPreferredSize(new Dimension(400, 400));
setLayout(new BorderLayout());
add(displayPanel);
add(sliderPanel, BorderLayout.PAGE_END);
}
private void createRgbSlider(JPanel sliderPanel, final RGB rgb) {
final JSlider slider = new JSlider(0, 255, 0);
slider.setMajorTickSpacing(50);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
sliderMap.put(rgb, slider);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int red = sliderMap.get(RGB.RED).getValue();
int green = sliderMap.get(RGB.GREEN).getValue();
int blue = sliderMap.get(RGB.BLUE).getValue();
displayPanelColor = new Color(red, green, blue);
displayPanel.setBackground(displayPanelColor);
}
});
JPanel rgbPanel = new JPanel(new BorderLayout());
rgbPanel.setBorder(BorderFactory.createTitledBorder(rgb.getName()));
rgbPanel.add(slider);
sliderPanel.add(rgbPanel);
}
private static void createAndShowGui() {
SpinnerEg mainPanel = new SpinnerEg();
JFrame frame = new JFrame("SpinnerEg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum RGB {
RED("Red", Color.red), GREEN("Green", Color.green), BLUE("Blue", Color.blue);
private String name;
private Color color;
private RGB(String name, Color color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
}