I need a little help with java, I am new to the language hence I have no idea on how to implement such.
I have already made a basic 15;40 grid JLabel Image, thanks to this site as well, what I need help with is about how to make a player(Supposed to be an Image, also shown on the grid) move around using either WASD(I don't know if Ascii-approach works on java) or Arrow Keys.
Here's my code for the Grid
public class GUI {
static Scanner cns = new Scanner(System.in);
JFrame frame = new JFrame();
ImageIcon ImageIcon = new ImageIcon("Grass.png");
JLabel[][] grid;
public GUI(int width, int length) {
Container pane = frame.getContentPane();
frame.setLayout(new GridLayout(width,length));
grid = new JLabel[width][length];
for(int y = 0; y < length; y++) {
for(int x = 0; x < width; x++) {
grid[x][y] = new JLabel();
grid[x][y].setBorder(BorderFactory.createLineBorder(Color.black));
grid[x][y].setBorder(BorderFactory.createEmptyBorder());
}
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
for(int y = 0; y < length; y++) {
for(int x = 0; x < width; x++) {
pane.add(grid[x][y]= new JLabel(new ImageIcon("Grass.png")));
grid[x][y].setBorder(BorderFactory.createLineBorder(Color.black));
grid[x][y].setBorder(BorderFactory.createEmptyBorder());
frame.add(grid[x][y]);
}
}
}
public static void main(String[] args) {
new GUI(15, 40);
}
}
Couple of suggestions here:
1 - Make GUI extend JFrame and implement KeyListener.
You have to override KeyListener methods (keyPressed, keyReleased, and keyTyped) and you should override repaint() from JFrame.
The repaint method should call super.repaint() at the end to update frames.
2 - Have the fields of GUI store data about the things that need to be drawn.
You probably should store the grid, or what's in the grid as fields.
3 - The constructor should initialize, not render.
Rendering should be done in repaint(). The constructor should do something like this:
super();
JPanel frame = new JPanel();
add(frame);
pack();
addKeyListener(this);
repaint();
This answer is kinda unfinished but I'll look more into it and update this. Particularly, the JPanel is an element in the JFrame.
UPDATE: Here is a small working example. Use WASD to move the rectangle around the screen.
This is the JFrame.
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
public class MyJFrame extends JFrame implements KeyListener {
private MyJPanel frame;
public MyJFrame() {
super();
frame = new MyJPanel();
add(frame);
pack();
addKeyListener(this);
repaint();
}
public static void main(String[] args) {
MyJFrame window = new MyJFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setTitle("Test");
}
#Override
public void repaint() {
super.repaint();
}
#Override
public void keyPressed(KeyEvent e) {
frame.keyPressed(e);
repaint();
}
#Override
public void keyTyped(KeyEvent e) { }
#Override
public void keyReleased(KeyEvent e) { }
}
This is the JPanel.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import javax.swing.JPanel;
public class MyJPanel extends JPanel {
private int x = 0;
private int y = 0;
public MyJPanel() {
setPreferredSize(new Dimension(200,200));
}
#Override
public void update(Graphics g) {
paint(g);
}
#Override
public void paint(Graphics g) {
g.setColor(Color.red);
g.drawRect(x,y,20,20);
}
public void keyPressed(KeyEvent e) {
int k = e.getKeyCode();
switch (k) {
case KeyEvent.VK_D:
x++;
break;
case KeyEvent.VK_A:
x--;
break;
case KeyEvent.VK_W:
y--;
break;
case KeyEvent.VK_S:
y++;
break;
}
}
}
Good luck!
Related
Okay, so I'm trying to make a program that draws a bunch of rectangles that move around the screen randomly. I have a Dot class where each dot holds its x and y values, and in my paint class I randomly change the x and y values and then repaint(). What I have right now doesn't load any thing other than a blank JFrame. I suspect that I'm drawing each dot wrong. The following is my code:
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Movement extends JFrame {
public ArrayList<Dot> dots = new ArrayList<Dot>();
Random rn = new Random();
DrawPanel drawPanel = new DrawPanel();
public Movement() {
for(int i = 0; i < 100; i ++) {
Dot dot = new Dot(5, 5);
dots.add(dot);
}
ActionListener listener = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < dots.size();i++) {
dots.get(i).setX(dots.get(i).getX() + rn.nextInt(20)-10);
dots.get(i).setY(dots.get(i).getY() + rn.nextInt(20)-10);
}
drawPanel.repaint();
}
};
Timer timer = new Timer(100, listener);
timer.start();
add(drawPanel);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
setBounds(100, 100, 500, 500);
}
private class DrawPanel extends JPanel {
protected void paintComponent(Graphics g) {
for(int i = 0; i < dots.size(); i ++) {
g.fillRect(dots.get(i).getX(), dots.get(i).getY(), 5, 5);;
super.paintComponent(g);
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Movement();
}
});
}
}
Dot class:
public class Dot {
private int x, y;
public Dot(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Any and all help is appreciated.
If you call super.paintComponent(g); after you paint your own components you have wiped out your own painting. So,
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
for(int i = 0; i < dots.size(); i ++) {
g2d.fillRect(dots.get(i).x, dots.get(i).y, 5, 5);
}
}
Also,
// don't repeat type in constructor
// use built in point instead of custom class
public ArrayList<Point> dots = new ArrayList<>();
and
ActionListener listener = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < dots.size();i++) {
dots.get(i).x = dots.get(i).x + rn.nextInt(20)-10;
dots.get(i).y = dots.get(i).y + rn.nextInt(20)-10;
}
drawPanel.repaint();
}
};
and it probably doesn't make any difference, but
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Movement mf = new Movement();
}
});
You appear to be calling super.paintComponent after you paint dots. Not only that, you appear to be calling it inside the loop, calling it every time you paint a dot, after you paint it.
So you keep painting a dot, letting super.paintComponent paint a clear panel, then painting another dot, then undoing it again, paint another, undo... and so on.
Call super.paintComponent one time, and do it before you do your custom painting.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.*;
//import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
class Rectangle extends JPanel {
private static int rect_x = 40;
private static int rect_y = 40;
private static final int rect_width = 100;
private static final int rect_height = rect_width;
KeyListener listener;
public Rectangle() {
this.listener = new KeyListener() {
#Override
public void keyTyped(KeyEvent e)
{
}
#Override
public void keyPressed(KeyEvent e)
{
//System.out.println(e.getKeyCode());
// w
if(e.getKeyCode() == 87)
{
rect_y -= 10;
//revalidate();
repaint();
}
// s
else if (e.getKeyCode() == 83)
{
rect_y += 10;
repaint();
}
// a
else if (e.getKeyCode() == 65)
{
rect_x -= 10;
repaint();
}
// d
else if (e.getKeyCode() == 68)
{
rect_x += 10;
repaint();
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
}; }
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(rect_x, rect_y, rect_width, rect_height);
g.fillRect(rect_x, rect_y, rect_width, rect_height);
g.setColor(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
// so that our GUI is big enough
return new Dimension(rect_width + 2 * rect_x, rect_height + 2 * rect_y);
}
// create the GUI explicitly on the Swing event thread
private void createAndShowGui() {
Rectangle mainPanel = new Rectangle();
JFrame frame = new JFrame("DrawRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
frame.addKeyListener(listener);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Rectangle Rct = new Rectangle();
Rct.createAndShowGui();
}
});
}
}
this code is meant to move a rectangle across the screen.
For some reason it does not update unless the window is minimised or resized.
I realise this has been asked many times before, but the answers I have found were very case specific or above my understanding.
I am new to java so sorry if I seem like a blockhead.
You're creating an Extra Rectangle object, and calling repaint() on it, and this is not the displayed object, and so your listener does not repaint the displayed JPanel. Don't do this. Only create one Rectangle object, so your repaints go to the correct reference.
e.g., change this:
Rectangle mainPanel = new Rectangle(); // ***** no!!! ****
JFrame frame = new JFrame("DrawRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel); // **** no ****
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
frame.addKeyListener(listener);
to this:
// Rectangle mainPanel = new Rectangle();
JFrame frame = new JFrame("DrawRect");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(this); // ****** note change
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
frame.addKeyListener(listener);
Other side issues:
Prefer Key Bindings to a KeyListener
Avoid magic numbers such as your key code numbers, which make debugging difficult. Instead use the KeyEvent.VK_? constants.
Follow Java naming conventions: constants should be all upper-case.
Your rect_x and y fields should not be static.
While using Swing in java, I am trying to move a circle slowly from a starting position to an end position when clicking a button. However, I can't see the circle moving. It just moves from start to end in an instant.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MyApp {
private int x = 10;
private int y = 10;
private JFrame f;
private MyDraw m;
private JButton b;
public void go() {
f = new JFrame("Moving circle");
b = new JButton("click me to move circle");
m = new MyDraw();
f.add(BorderLayout.SOUTH, b);
f.add(BorderLayout.CENTER, m);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
b.addActionListener(new Bute());
}
public static void main(String[] args) {
MyApp m = new MyApp();
m.go();
}
private class Bute implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 150; i++) {
++x;
++y;
m.repaint();
Thread.sleep(50);
}
}
}
private class MyDraw extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
I think the problem is with the action listener because when I'm doing it without using button it is working. Any suggestions?
As Andrew Thompson said, calling Thread.sleep() without defining a second thread freezes everything, so the solution is to define and run another thread like so:
class Bute implements ActionListener, Runnable {
//let class implement Runnable interface
Thread t; // define 2nd thread
public void actionPerformed(ActionEvent e) {
t = new Thread(this); //start a new thread
t.start();
}
#Override //override our thread's run() method to do what we want
public void run() { //this is after some java-internal init stuff called by start()
//b.setEnabled(false);
for (int i = 0; i < 150; i++) {
x++;
y++;
m.repaint();
try {
Thread.sleep(50); //let the 2nd thread sleep
} catch (InterruptedException iEx) {
iEx.printStackTrace();
}
}
//b.setEnabled(true);
}
}
The only problem with this solution is that pressing the button multiple times will speed up the circle, but this can be fixed by making the button unclickable during the animation via b.setEnabled(true/false). Not the best solution but it works.
As said in the comments and another answer, don't block the EDT. Thead.sleep(...) will block it, so you have two options:
Create and manage your own (new) thread.
Use a Swing Timer
In this answer I'll be using a Swing Timer, since it's easier to use. I also changed the paintComponent method to use the Shape API and change the button text to start and stop accordingly as well as reusing the same ActionListener for the button and the timer:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MovingCircle {
private JFrame frame;
private CustomCircle circle;
private Timer timer;
private JButton button;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(this.getClass().getSimpleName());
circle = new CustomCircle(Color.RED);
timer = new Timer(100, listener);
button = new JButton("Start");
button.addActionListener(listener);
circle.setBackground(Color.WHITE);
frame.add(circle);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private ActionListener listener = (e -> {
if (!timer.isRunning()) {
timer.start();
button.setText("Stop");
} else {
if (e.getSource().equals(button)) {
timer.stop();
button.setText("Start");
}
}
circle.move(1, 1);
});
#SuppressWarnings("serial")
class CustomCircle extends JPanel {
private Color color;
private int circleX;
private int circleY;
public CustomCircle(Color color) {
this.color = color;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(color);
g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
}
#Override
public Dimension preferredSize() {
return new Dimension(100, 100);
}
public void move(int xGap, int yGap) {
circleX += xGap;
circleY += yGap;
revalidate();
repaint();
}
public int getCircleX() {
return circleX;
}
public void setCircleX(int circleX) {
this.circleX = circleX;
}
public int getCircleY() {
return circleY;
}
public void setCircleY(int circleY) {
this.circleY = circleY;
}
}
}
I'm sorry, I can't post a GIF as I wanted but this example runs as expected.
I try to write a code to draw a Oval run from the top left down to the bottom right. But when I run to program, it show the blank screen on app.
Why is this happening?
Here is my code:
import javax.swing.*;
import java.awt.*;
public class SimpleAnimation {
int x = 70;
int y = 70;
public static void main(String[] arg){
SimpleAnimation gui = new SimpleAnimation();
gui.go();
}
public void go(){
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300, 300);
frame.setVisible(true);
for (int i = 0; i < 130; i++){
x++;
y++;
drawPanel.repaint();
try{
Thread.sleep(50);
} catch(Exception ex){}
}
} // close go() method
class MyDrawPanel extends JPanel{
#Override
public void paintComponents(Graphics g) {
g.setColor(Color.green);
g.fillOval(x, y, 40, 40);
}
} // close inner class
} // close outer class
You should Override paintComponent() instead of paintComponents().
So change the public void paintComponents() {...} to public void paintComponent() {...}
But just for the hint:
Try to use Timers instead of Threads. Always try not to mess with the swing thread. In your case you can use javax.swing.Timer to call the repaint in 50 ms intervals:
counter = 0;
t = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(counter>=130)
t.stop();
counter++;
x++;
y++;
drawPanel.repaint();
}
});
t.start();
javax.swing.Timer t and int counter are member variables for your class.
Good Luck.
You are not calling super.paintComponent(g); and also you need to call paintComponents rather than repaint method. Please try it with below code. I hope it helps :)
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SimpleAnimation {
int x = 70;
int y = 70;
public static void main(String[] arg) {
SimpleAnimation gui = new SimpleAnimation();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300, 300);
frame.setVisible(true);
for (int i = 0; i < 130; i++) {
x++;
y++;
drawPanel.paintComponents(drawPanel.getGraphics());
try {
Thread.sleep(50);
} catch (Exception ex) {
}
}
} // close go() method
class MyDrawPanel extends JPanel {
#Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponent(g);
g.drawOval(50, 50, 50, 50);
g.setColor(Color.green);
g.fillOval(x, y, 40, 40);
}
} // close inner class
} // close outer class
My component is bigger than the screen and parts of it are not shown (I will use scrollbars).
When I receive a call in paintComponent(g) how do I know what area should I paint?
I'm not sure if this is what you mean, but the problem is you will have to call repaint() on the JScrollPane each time you get a call in paintComponent(Graphics g) of the JPanel or else updates on the JPanel will not be visible in the JScrollPane.
Also I see you want to use JScrollBar (or maybe you confused the terminology)? I'd recommend a JScrollPane
I made a small example which is a JPanel with a grid that will change its colour every 2 seconds (Red to black and vice versa). The JPanel/Grid is larger then the JScrollPane; regardless we have to call repaint() on the JScrollPane instance or else the grid wont change colour:
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.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test().createAndShowUI();
}
});
}
private void createAndShowUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
frame.setPreferredSize(new Dimension(400, 400));
frame.pack();
frame.setVisible(true);
}
private void initComponents(JFrame frame) {
JScrollPane jsp = new JScrollPane();
jsp.setViewportView(new Panel(800, 800, jsp));
frame.getContentPane().add(jsp);
}
}
class Panel extends JPanel {
private int across, down;
private Panel.Tile[][] tiles;
private Color color = Color.black;
private final JScrollPane jScrollPane;
public Panel(int width, int height, JScrollPane jScrollPane) {
this.setPreferredSize(new Dimension(width, height));
this.jScrollPane = jScrollPane;
createTiles();
changePanelColorTimer();//just something to do to check if its repaints fine
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < across; i++) {
for (int j = 0; j < down; j++) {
g.setColor(color);
for (int k = 0; k < 5; k++) {
g.drawRect(tiles[i][j].x + k, tiles[i][j].y + k, tiles[i][j].side - k * 2, tiles[i][j].side - 2 * k);
}
}
}
updateScrollPane();//refresh the pane after every paint
}
//calls repaint on the scrollPane instance
private void updateScrollPane() {
jScrollPane.repaint();
}
private void createTiles() {
across = 13;
down = 9;
tiles = new Panel.Tile[across][down];
for (int i = 0; i < across; i++) {
for (int j = 0; j < down; j++) {
tiles[i][j] = new Panel.Tile((i * 50), (j * 50), 50);
}
}
}
//change the color of the grid lines from black to red and vice versa every 2s
private void changePanelColorTimer() {
Timer timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (color == Color.black) {
color = Color.red;
} else {
color = Color.black;
}
}
});
timer.setInitialDelay(2000);
timer.start();
}
private class Tile {
int x, y, side;
public Tile(int inX, int inY, int inSide) {
x = inX;
y = inY;
side = inSide;
}
}
}
In the Panel class if we comment the line updateScrollPane(); in paintComponent(Graphics g) we wont see the grid change colour.
You can find out the area that actually has to be painted by querying the clip bounds of the Graphics object.
The JavaDoc seems to be a bit out-dated for this method: It says, that it may return a null clip. However, this is obviously never the case (and other Swing classes also rely on the clip never being null!).
The follwing MCVE illustrates the difference between using a the clip or painting the whole component:
It contains a JPanel with a size of 800x800 in a scroll pane. The panel paints a set of rectangles, and prints how many rectangles have been painted.
One can use the "Use clip bounds" checkbox to enable and disable using the clip. When the clip is used, only the visible area of the panel is repainted, and the number of rectangles is much lower. (Note that the test whether a rectangle has to be painted or not is rather simple here: It only performs an intersection test of the rectangle with the visible region. For a real application, one would directly use the clip bounds to find out which rectangles have to be painted).
This example also shows some of the tricky internals of scroll panes: When the blinking is switched off, and the scroll bars are moved, one can see that - although the whole visible area changes - only a tiny area actually has to be repainted (namely the area that has become visible due to the scrolling). The other part is simply moved as-it-is, by blitting the previous contents. This behavior can be modified with JViewport.html#setScrollMode.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class PaintRegionTest
{
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final PaintRegionPanel paintRegionPanel = new PaintRegionPanel();
paintRegionPanel.setPreferredSize(new Dimension(800, 800));
final Timer timer = new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
paintRegionPanel.changeColor();
}
});
timer.setInitialDelay(1000);
timer.start();
JScrollPane scrollPane = new JScrollPane(paintRegionPanel);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JPanel controlPanel = new JPanel(new FlowLayout());
final JCheckBox blinkCheckbox = new JCheckBox("Blink", true);
blinkCheckbox.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if (blinkCheckbox.isSelected())
{
timer.start();
}
else
{
timer.stop();
}
}
});
controlPanel.add(blinkCheckbox);
final JCheckBox useClipCheckbox = new JCheckBox("Use clip bounds");
useClipCheckbox.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
paintRegionPanel.setUseClipBounds(
useClipCheckbox.isSelected());
}
});
controlPanel.add(useClipCheckbox);
frame.getContentPane().add(controlPanel, BorderLayout.SOUTH);
frame.setPreferredSize(new Dimension(400, 400));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class PaintRegionPanel extends JPanel
{
private Color color = Color.BLACK;
private boolean useClipBounds = false;
void setUseClipBounds(boolean useClipBounds)
{
this.useClipBounds = useClipBounds;
}
void changeColor()
{
if (color == Color.BLACK)
{
color = Color.RED;
}
else
{
color = Color.BLACK;
}
repaint();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(color);
Rectangle clipBounds = g.getClipBounds();
Rectangle ownBounds = new Rectangle(0,0,getWidth(),getHeight());
System.out.println("clipBounds: " + clipBounds);
System.out.println(" ownBounds: " + ownBounds);
Rectangle paintedRegion = null;
if (useClipBounds)
{
System.out.println("Using clipBounds");
paintedRegion = clipBounds;
}
else
{
System.out.println("Using ownBounds");
paintedRegion = ownBounds;
}
int counter = 0;
// This loop performs a a simple test see whether the objects
// have to be painted. In a real application, one would
// probably use the clip information to ONLY create the
// rectangles that actually have to be painted:
for (int x = 0; x < getWidth(); x += 20)
{
for (int y = 0; y < getHeight(); y += 20)
{
Rectangle r = new Rectangle(x + 5, y + 5, 10, 10);
if (r.intersects(paintedRegion))
{
g.fill(r);
counter++;
}
}
}
System.out.println("Painted "+counter+" rectangles ");
}
}
An aside: For many application cases, such an "optimization" should hardly be necessary. The painted elements are intersected against the clip anyhow, so one will probably not gain much performance. When "preparing" the elements to be painted is computationally expensive, one can consider this as one option. (In the example, "preparing" refers to creating the Rectangle instance, but there may be more complicated patterns). But in these cases, there may also be more elegant and easier solutions than manually checking the clip bounds.
All answers are wrong. So I decided to answer the question despide the fact that the question is two years old.
I believe that the correct answer is calling g.getClipBounds() inside of paintComponent(Graphics g) method. It will return the rectangle in the control's coordinate system of the area which is invalidated and must be redrawn.