This is my fourth question here in about two weeks... I think I have a lot to learn.
Anyway, I'm hoping the sample I've prepared below will explain the problem better than I can, but since I changed all of my components to Swing rather than AWT, they've all been drawing every component in the panel inside themselves whenever I call repaint().
This code is essentially my program stripped down as much as I can without destroying the problem completely. I've stuck a couple of my custom listbox components in there to demonstrate the issue, and made the border red to make it slightly easier to see what's drawing where.
Test.java:
package test;
import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;
public class Test extends JFrame
{
public static void main(String[] args)
{
Test test1 = new Test();
}
public Test()
{
super("Test Frame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(800, 480);
setLocationRelativeTo(null);
loadTestScreen();
}
public void loadTestScreen()
{
TestScreen newTestScreen = new TestScreen();
newTestScreen.setSize(new Dimension(getWidth() - getInsets().left - getInsets().right, getHeight() - getInsets().top - getInsets().bottom));
setContentPane(newTestScreen);
}
}
TestScreen.java:
package test;
import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.util.*;
import java.net.URI;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class TestScreen extends JPanel implements ComponentListener, MouseListener
{
int border, LT;
public TestScreen()
{
setLayout(null);
setOpaque(true);
setBackground(Color.WHITE);
addComponentListener(this);
border = 8;
LT = 4;
///////////////////////////////////////////////////////////
ArrayList<String> testList = new ArrayList<String>();
testList.add("1");
testList.add("2");
testList.add("3");
testList.add("4");
testList.add("5");
testList.add("6");
add(new ListBox(testList), 0);
add(new ListBox(testList), 1);
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setStroke(new BasicStroke((float)(LT)));
g2d.setColor(new Color(255, 0, 0));
g2d.drawRoundRect(border, border, getWidth() - border - border, getHeight() - border - border, border + border, border + border);
}
public void componentHidden(ComponentEvent e){}
public void componentShown(ComponentEvent e){}
public void componentMoved(ComponentEvent e)
{
componentResized(e);
}
public void componentResized(ComponentEvent e)
{
getComponent(0).setLocation(20, 20);
getComponent(0).setSize(100, 100);
getComponent(1).setLocation(200, 200);
getComponent(1).setSize(150, 150);
repaint();
}
public void mouseEntered(MouseEvent e)
{
repaint();
}
public void mouseExited(MouseEvent e)
{
repaint();
}
public void mouseReleased(MouseEvent e){} public void mouseDragged(MouseEvent e){} public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
repaint();
}
}
ListBox.java:
package test;
import java.beans.*;
import java.util.*;
import java.awt.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeSupport;
import javax.swing.*;
public class ListBox extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener
{
public ArrayList<String> data;
int border, LT;
int selectedIndex = 0;
int mousedIndex = -1;
public int viewedHeight = 0;
int itemHeight;
int numberOfDisplayedItems;
Font miniFont;
FontMetrics miniMetrics;
private final PropertyChangeSupport pcs = new PropertyChangeSupport( this );
public ListBox(ArrayList<String> list)
{
setVisible(true);
setOpaque(true);
border = 8;
LT = 4;
addMouseListener(this);
addMouseMotionListener(this);
addComponentListener(this);
addMouseWheelListener(this);
data = list;
miniFont = new Font("Calibri", Font.PLAIN, 15);
miniMetrics = getFontMetrics(miniFont);
itemHeight = miniMetrics.getAscent() + miniMetrics.getDescent() + border + border;
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(new Color(93, 138, 168));
g2d.setStroke(new BasicStroke((float)LT / 2));
g2d.setClip(-(LT / 2), -(LT / 2), getWidth() + LT, getHeight() + LT);
int cumulativeDist = -viewedHeight;
for (int i = 0; i < data.size(); i++)
{
if (selectedIndex == i)
g2d.fillRect(0, cumulativeDist, getWidth(), itemHeight);
cumulativeDist += itemHeight;
g2d.drawLine(0, cumulativeDist, getWidth(), cumulativeDist);
}
g2d.drawRect(0, 0, getWidth(), getHeight());
g2d.setFont(miniFont);
g2d.setColor(new Color(42, 60, 76));
cumulativeDist = -viewedHeight + border + miniMetrics.getAscent();
for (int i = 0; i < data.size(); i++)
{
if (mousedIndex == i){
g2d.drawString(data.get(i), border + border / 2, cumulativeDist);
} else {
g2d.drawString(data.get(i), border, cumulativeDist);
}
cumulativeDist += itemHeight;
}
}
public String getSelectedItem()
{
return data.get(selectedIndex);
}
public void mouseReleased(MouseEvent e){} public void mouseEntered(MouseEvent e){} public void mouseDragged(MouseEvent e){}public void mouseClicked(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
int old = selectedIndex;
int mouseHeight = viewedHeight + e.getY();
int ID = mouseHeight / itemHeight;
System.out.println(mouseHeight / itemHeight);
selectedIndex = ID;
pcs.firePropertyChange("selectedIndex", old, selectedIndex);
}
public void componentHidden(ComponentEvent e){} public void componentShown(ComponentEvent e){} public void componentMoved(ComponentEvent e){}
public void componentResized(ComponentEvent e)
{
numberOfDisplayedItems = (int)((getHeight() / itemHeight) + 0.5);
repaint();
}
public void mouseWheelMoved(MouseWheelEvent e)
{
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
{
if (e.getUnitsToScroll() > 0)
{
viewedHeight += itemHeight;
}else{
viewedHeight -= itemHeight;
}
if (viewedHeight > (data.size() * itemHeight) - getHeight())
viewedHeight = (data.size() * itemHeight) - getHeight();
if (viewedHeight < 0)
{
viewedHeight = 0;
}
mouseMoved((MouseEvent)e);
repaint();
}
}
public void mouseMoved(MouseEvent e)
{
int mouseHeight = viewedHeight + e.getY();
int ID = mouseHeight / itemHeight;
mousedIndex = ID;
repaint();
}
public void mouseExited(MouseEvent e)
{
mousedIndex = -1;
repaint();
}
}
Thanks, and if you find any other glaring issues in my programming, please don't hesitate to tell me :P
The immediate problem—painting artifacts in ListBox—is caused by not honoring the opaque property. Using true promises to paint all of the bits, but your paintComponent() fails to do so.
Here are a few more suggestions:
Don't neglect the event dispatch thread.
Do use event listeners, but also consider the available adapters, which have convenient null implementations.
Don't reinvent existing components, and override paintComponent() only as a last resort.
Do learn to use layout managers and borders.
Yes there is much to learn, but this kind of experimentation is invaluable.
You should call setVisible(true); only once you've set up the component, at the end of your constructor.
Related
I am trying to rotate a JLabel 90 degrees that shows the current time 90 degrees. After doing some research, most people have recommended using Graphics2D and AffineTransform. This almost works, but when the minute in the time is updated, the new digit appears to merge with the old digit.
This does not happen for the seconds. Does anybody have any idea how to fix this issue or have an alternate solution?
Driver class:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class Driver extends JFrame implements KeyListener {
private boolean running = true;
ClockWidget clockWidget;
static Dimension screenSize;
public static void main(String[] args) {
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
DisplayMode displayMode = new DisplayMode((int) screenSize.getWidth(), (int) screenSize.getHeight(), 32,
DisplayMode.REFRESH_RATE_UNKNOWN);
new Driver().run(displayMode);
}
public void run(DisplayMode displayMode) {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
getContentPane().setBackground(Color.BLACK);
setFont(new Font("Arial", Font.PLAIN, 24));
Screen screen = new Screen();
screen.setFullScreen(displayMode, this);
initClockWidgit();
addKeyListener(this);
System.out.println("RUNNING");
while (running) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
quitProgram(screen);
return;
}
public void initClockWidgit() {
clockWidget = new ClockWidget();
clockWidget.setFont(new Font("Arial", Font.PLAIN, 36));
clockWidget.setForeground(Color.WHITE);
clockWidget.setBackground(Color.BLUE);
clockWidget.setBounds((int) (screenSize.getWidth() * 0.90), (int) (screenSize.getHeight() * 0.10), 250, 100);
add(clockWidget);
new Thread(clockWidget).start();
}
public void quitProgram(Screen screen) {
screen.restoreScreen();
clockWidget.disable();
}
#Override
public void keyPressed(KeyEvent keyEvent) {
int keyCode = keyEvent.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {
running = false;
}
keyEvent.consume();
}
#Override
public void keyReleased(KeyEvent keyEvent) {
keyEvent.consume();
}
#Override
public void keyTyped(KeyEvent keyEvent) {
keyEvent.consume();
}
}
ClockWidget Class:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.swing.JLabel;
public class ClockWidget extends RotatedJLabel implements Runnable{
private String currentTime;
private boolean running;
public ClockWidget() {
running = true;
}
#Override
public void run() {
while(running) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("hh:mm:ss a");
currentTime = simpleDateFormat.format(calendar.getTime());
setText(currentTime);
}
}
public void disable() {
running = false;
}
}
RotatedJLabel Class:
[import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import javax.swing.Icon;
import javax.swing.JLabel;
public class RotatedJLabel extends JLabel {
public RotatedJLabel() {
super();
}
public RotatedJLabel(Icon image) {
super(image);
}
public RotatedJLabel(Icon image, int horizontalAlignment) {
super(image, horizontalAlignment);
}
public RotatedJLabel(String text) {
super(text);
}
public RotatedJLabel(String text, Icon icon, int horizontalAlignment) {
super(text, icon, horizontalAlignment);
}
public RotatedJLabel(String text, int horizontalAlignment) {
super(text, horizontalAlignment);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform aT = g2.getTransform();
Shape oldshape = g2.getClip();
double x = getWidth()/2.0;
double y = getHeight()/2.0;
aT.rotate(Math.toRadians(90), x, y);
g2.setTransform(aT);
g2.setClip(oldshape);
super.paintComponent(g);
}
}
A few things jump out at me:
I wouldn't use a JLabel for this purpose, it's a complicate component, starting with a JPanel and simply painting the text would be simpler. In my testing it was very hard to get the sizing hints to work correctly when the graphics context was rotated.
You're not managing the component's "new" sizing hints, this could be an issue when coupled with more complex layouts, as the width of the component should now be the height and visa-versa
I'd recommend the key bindings API over KeyListener
Swing is NOT thread safe, updating the UI from outside the context of the UI could produce any number of issues; instead of using a Thread, you should be using a Swing Timer, and since you probably really only want to update the seconds, running it at a much slower speed. See Concurrency in Swing and How to Use Swing Timers for more details
And Calendar and Date are effectively deprecated. See Standard Calendar for more details
For example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private RotatedLabel timeLabel;
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
public TestPane() {
setLayout(new GridBagLayout());
timeLabel = new RotatedLabel(currentTime());
add(timeLabel);
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timeLabel.setText(currentTime());
}
});
timer.start();
}
public String currentTime() {
LocalTime lt = LocalTime.now();
return lt.format(formatter);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public class RotatedLabel extends JPanel {
private String text;
public RotatedLabel() {
super();
setOpaque(false);
setFont(UIManager.getDefaults().getFont("label.font"));
}
public RotatedLabel(String text) {
this();
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
revalidate();
repaint();
}
protected Dimension getTextBounds() {
FontMetrics fm = getFontMetrics(getFont());
return new Dimension(fm.stringWidth(text), fm.getHeight());
}
#Override
public Dimension getPreferredSize() {
Dimension size = getTextBounds();
return new Dimension(size.height, size.width);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform aT = g2.getTransform();
double x = getWidth() / 2.0;
double y = getHeight() / 2.0;
aT.rotate(Math.toRadians(90), x, y);
g2.setTransform(aT);
FontMetrics fm = g2.getFontMetrics();
float xPos = (getWidth() - fm.stringWidth(getText())) / 2.0f;
float yPos = ((getHeight() - fm.getHeight()) / 2.0f) + fm.getAscent();
g2.drawString(text, xPos, yPos);
g2.dispose();
}
}
}
Now, if you "absolutely, must, no questions asked" use a component like Label, then I recommend using JLayer instead.
Unfourtantly, I've not had time to update my examples to use JLayer, but they use the predecessor library, JXLayer
Java rotating non-square JPanel component
Is there any way I can rotate this 90 degrees?
I have a program which is simple in function. On start, it creates a random circle which it places in the window/frame. When that circle is clicked, it should dissappear, and spawn a new circle elsewhere. the issue is, my program does this, but you see all the past circles unless you minimize/reopen the window. I cannot get it to repaint without my help... and I do NOT know why. Here is my code.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
public class Core extends JFrame implements MouseListener{
public static ArrayList<Ellipse2D> list = new ArrayList<Ellipse2D>();
Random r = new Random();
public Ellipse2D spawn(){
int x = r.nextInt(this.getWidth());
int y = r.nextInt(this.getHeight());
while(x<75||x>this.getWidth()-150){
x = r.nextInt(this.getWidth());
}
while(y<75||y>this.getHeight()-150){
y = r.nextInt(this.getHeight());
}
System.out.println("MAKING SHAPE at :" + x + " AND " + y);
return new Ellipse2D.Double(x, y, 75, 75);
}
public void mouseClicked(MouseEvent me) {
// Save the coordinates of the click lke this.
if(list.get(0).contains(me.getPoint())){
System.out.println("CLICKED SHAPE!");
list.clear();
list.add(spawn());
}
revalidate();
repaint();
}
public void mouseEntered(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
#Override
public void paint(Graphics g) {
if(list.size()==0){
System.out.println("oops");
}
if(!list.isEmpty()){
System.out.println("DRAW");
int x =(int) list.get(0).getX();
int y =(int) list.get(0).getY();
int width = (int) list.get(0).getWidth();
int height = (int) list.get(0).getHeight();
g.setColor(Color.red);
g.drawOval(x,y, width, height);
}
}
public Core(){
setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addMouseListener(this);
list.add(spawn());
setVisible(true);
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Core();
}
});
}
}
Your code works perfectly fine for me, however...
Don't override paint of top level containers like JFrame, JFrame contains a JRootPane, which contains a contentPane and may also have a visible glassPane, all of which can overpaint what is painted within the paint method. As a general rule, you shouldn't extend from JFrame (or other top level containers), you are locking yourself into a single use case, reducing the re-usability of your component and you're not actually any new functionality to the class. Instead, use a JPanel and override it's paintComponent method
Call super.paint before doing any custom painting. If, however, you use a JPanel, call super.paintComponent. Painting is performed by a series of methods which are chained together to generate the final output. See Painting in AWT and Swing and Performing Custom Painting for more details
Consider using something like x = r.nextInt(this.getWidth() - 150) + 75; and y = r.nextInt(this.getWidth() - 150) + 75; instead of your while loops, I think you might find them safer to use
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Core {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Core();
}
});
}
public Core() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CorePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CorePane extends JPanel {
private ArrayList<Ellipse2D> list = new ArrayList<Ellipse2D>();
private Random r = new Random();
public Ellipse2D spawn() {
int x = r.nextInt(this.getWidth());
int y = r.nextInt(this.getHeight());
x = r.nextInt(this.getWidth() - 150) + 75;
y = r.nextInt(this.getWidth() - 150) + 75;
System.out.println("MAKING SHAPE at :" + x + " AND " + y);
return new Ellipse2D.Double(x, y, 75, 75);
}
public CorePane() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent me) {
// Save the coordinates of the click lke this.
if (list.get(0).contains(me.getPoint())) {
list.clear();
list.add(spawn());
}
revalidate();
repaint();
}
});
}
#Override
public void invalidate() {
super.invalidate();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (list.isEmpty()) {
list.add(spawn());
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (!list.isEmpty()) {
for (Ellipse2D ellipse : list) {
g2d.setColor(Color.red);
g2d.draw(ellipse);
}
}
g2d.dispose();
}
}
}
Or, based on what I believe you're trying to do, you could simply do something like...
public class CorePane extends JPanel {
private Random r = new Random();
private Ellipse2D ellipse;
public Ellipse2D spawn() {
int x = r.nextInt(this.getWidth());
int y = r.nextInt(this.getHeight());
x = r.nextInt(this.getWidth() - 150) + 75;
y = r.nextInt(this.getWidth() - 150) + 75;
System.out.println("MAKING SHAPE at :" + x + " AND " + y);
return new Ellipse2D.Double(x, y, 75, 75);
}
public CorePane() {
addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent me) {
if (ellipse != null && ellipse.contains(me.getPoint())) {
ellipse = spawn();
}
revalidate();
repaint();
}
});
}
#Override
public void invalidate() {
super.invalidate();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
spawn();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (ellipse != null) {
g2d.setColor(Color.red);
g2d.draw(ellipse);
}
g2d.dispose();
}
}
I am creating a 6x6 grid memory game. Its requirements is to use an image in a panel as a replacement for the buttons.
210 x 70
The project requires to implement MouseListeners on the following conditions:
The button will show first pokebell.
When the mouse hovers over the pokeball, it changes into the second pokeball.
When the mouse moves away from the pokeball, it reverts back to the first pokeball.
When the mouse clicks the pokeball, it changes to the 3rd pokeball.
Grid MouseEvents
public void mouseEntered(MouseEvent e) {
for(i = 0; i < 36; i++){
if(e.getSource() == pkm[i]){
pkb[i].repaint();
}
}
}
public void mouseExited(MouseEvent e) {
for(i = 0; i < 36; i++){
if(e.getSource() == pkm[i]){
pkb[i].repaint();
}
}
}
PokeBall class
int start = 0;
int ht = 0, wt = 0;
URL url;
BufferedImage img, sp1;
public PokeBall(String imgLink, int w, int h, int x){
wt = w;
ht = h;
start = x;
url = this.getClass().getResource(imgLink);
try{
img = ImageIO.read(url);
}
catch(Exception e){
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
sp1 = img.getSubimage(start, 0, wt, ht);
g2d.drawImage(sp1,20,10,null);
if(start == 70) {
start = 0;
}
else {
start += 70;
}
}
My thought was that the change of sprite will be invoked on the corresponding mouse event, but instead I got the whole pokeball to animate in an infinite loop even when the mouse didn't invoke any event.
I need some ideas or suggestions on how to stop it from looping by itself and to actually call the designated pokeball.
Basically, the responsibility for painting the balls is the PokeBall class, it needs to know when the state has changed, so it makes sense to apply an MouseListener and MouseMotionListener to it. Then you don't need to care about trying to update grid locations and other fun stuff.
On your GridPane, I would then attach another MouseListener so you can detect when a ball is clicked and take appropriate action there...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MyPokeBalls {
public static void main(String[] args) {
new MyPokeBalls();
}
public MyPokeBalls() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PokeBall());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PokeBall extends JPanel {
private BufferedImage balls;
private int ballWidth = 70;
private int ballHeight = 70;
private int ballOffset = 0;
public PokeBall() {
try {
balls = ImageIO.read(new File("PokeBalls.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter ma = new MouseAdapter() {
private boolean isIn = false;
#Override
public void mouseEntered(MouseEvent e) {
ballOffset = 1;
isIn = true;
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
ballOffset = 0;
isIn = true;
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
ballOffset = 2;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (isIn) {
ballOffset = 1;
} else {
ballOffset = 2;
}
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(ballWidth, ballHeight);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (balls != null) {
Graphics2D g2d = (Graphics2D) g.create();
BufferedImage ball = balls.getSubimage(ballWidth * ballOffset, 0, ballWidth, ballHeight);
int x = (getWidth() - ball.getWidth()) / 2;
int y = (getHeight() - ball.getHeight()) / 2;
g2d.drawImage(ball, x, y, this);
g2d.dispose();
}
}
}
}
AbstractButton b=new JToggleButton(firstIcon);
b.setContentAreaFilled(false);
b.setFocusable(false);
b.setBorder(BorderFactory.createEmptyBorder());
b.setRolloverEnabled(true);
b.setRolloverIcon(secondIcon);
b.setSelectedIcon(thirdIcon);
Why reinventing the wheel?
Creating a grid of such buttons is quite simple…
I want to display a GameOver image in a pacman game after lives are over. But I call the paintGameOverScreen(Graphics g) and then I need to initialize g. Is there any other way to do this?
This is my Lives class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class Lives{
private int lives;
public Lives() {
lives = 1;
}
public void removeLife() {
lives--;
if(lives==0){
System.out.println("END GAME");
paintGameOverScreen(g);
System.exit(0);
}
}
public void paintGameOverScreen(Graphics g) {
ImageIcon i = new ImageIcon("src\image");
Image image = i.getImage();
int x = 0;
int y = 0;
g.drawImage(image, x, y, 100,100,null);
}
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(5*20, 25*20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6*20, 26*20);
}
}
You never call paint() or paintComponent() yourself, you always go through repaint() which will take care of setting up the appropriate Graphics
Just to show what #mKorbel is referring to:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Lives extends JPanel {
private int lives;
private ImageIcon gameOverImage;
public Lives() {
try {
gameOverImage = new ImageIcon(new URL("http://imgup.motion-twin.com/dinorpg/0/f/77acf80b_989624.jpg"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lives = 5;
}
public void removeLife() {
if (lives > 0) {
lives--;
System.out.println("Left lives: " + lives);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lives > 0) {
System.out.println("Still have " + lives + " lives");
g.setColor(Color.WHITE);
g.fillRect(5 * 20, 25 * 20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6 * 20, 26 * 20);
} else if (gameOverImage != null) {
System.out.println("Game over");
int x = (getWidth() - gameOverImage.getIconWidth()) / 2;
int y = (getHeight() - gameOverImage.getIconHeight()) / 2;
g.drawImage(gameOverImage.getImage(), x, y, gameOverImage.getIconWidth(), gameOverImage.getIconHeight(), this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame(Lives.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Lives lives = new Lives();
frame.add(lives);
frame.pack();
frame.setVisible(true);
// Dummy timer that reduces the lives every second. For demo purposes only of course
Timer t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lives.removeLife();
}
});
t.start();
}
});
}
}
for public void paint(Graphics g) { is there missed container,
JPanel (in some cases JComponent) could be container for todays Java
have to use paintComponent instead of paint()
inside paintComponent you can to flag for paintGameOverScreen, then there paint only BufferedImage
prepare all Objects before, as local variable, do not load any FileIO (load images) inside paint(), paintComponent()
Here is how I did:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class InitializeGraphics
{
static BufferedImage buffer = null;
static int height = 10;
static int width = 10;
static Graphics2D g2;
public InitializeGraphics() {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2 = buffer.createGraphics();
g2.fillOval(2, 2, 2, 2);
g2.dispose();
}
protected void paintComponent(Graphics g) {
int x = 0;
int y = 0;
g.drawImage(buffer, x, y, width, height, null);
}
public Graphics2D getGraphics(){
return g2;
}
}
Then somewhere:
InitializeGraphics instance=new InitializeGraphics();
Graphics2D gG2 = instance.getGraphics();
Can't seem to figure out how to only show one circle. Was trying to //g.clearRect(0, 0, 400, 400); but that clears the background too. Was also trying to just fill the background with yellow again but couldn't get that working either. Any suggestions?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class JMouseFrame extends JFrame
implements MouseListener {
Container con = null;
int x, y;
int size;
public JMouseFrame() {
setTitle("JMouseFrame");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
con = this.getContentPane();
addMouseListener(this);
}
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}
public void mouseClicked(MouseEvent e) {
int whichButton = e.getButton();
if (whichButton == MouseEvent.BUTTON1) {
size = 15;
} else if (whichButton == MouseEvent.BUTTON3) {
size = 4;
}
repaint();
}
public void mouseEntered(MouseEvent e) {
con.setBackground(Color.yellow);
}
public void mouseExited(MouseEvent e) {
con.setBackground(Color.black);
}
public void paint(Graphics g) {
//g.clearRect(0, 0, 400, 400);
g.drawOval(x - size, y - size, size * 3, size * 3);
}
public static void main(String[] args) {
JMouseFrame mFrame = new JMouseFrame();
mFrame.setSize(400, 400);
mFrame.setVisible(true);
}
public void mouseReleased(MouseEvent e) {
}
}
It looks like you're mixing AWT and Swing painting. Instead, override paintComponent(), as suggested in the example below. See Painting in AWT and Swing for details.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/3898775 */
public class MousePanel extends JPanel {
private static final int SIZE = 20;
Point p = new Point(Short.MAX_VALUE, Short.MAX_VALUE);
public MousePanel() {
this.setPreferredSize(new Dimension(400, 400));
this.setBackground(Color.yellow);
this.addMouseListener(new MouseHandler());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);
g.drawOval(p.x - SIZE, p.y - SIZE, SIZE * 2, SIZE * 2);
}
private final class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
repaint();
}
}
private void display() {
JFrame f = new JFrame("MousePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MousePanel().display();
}
});
}
}
Addendum:
I can't figure out how to make the code you posted work with what I have...
You can restore your mouse methods to the MouseHandler almost verbatim. The only difference is the need to qualify this, e.g.
#Override
public void mouseClicked(MouseEvent e) {
int whichButton = e.getButton();
if (whichButton == MouseEvent.BUTTON1) {
MousePanel.this.size = 15;
} else if (whichButton == MouseEvent.BUTTON3) {
MousePanel.this.size = 4;
}
repaint();
}
#Override
public void mouseEntered(MouseEvent e) {
MousePanel.this.setBackground(Color.yellow);
}
#Override
public void mouseExited(MouseEvent e) {
MousePanel.this.setBackground(Color.black);
}