Move JPanel in JFrame - java

I'm looking to move a jpanel inside of a JFrame, and it won't seem to budge. I can set it's location in the paint() method, but it won't update in repaint. Please help! Here is my code:
public void paint(Graphics g) {
g.drawImage(playerImg, x, 50, null);
this.setLocation(x, 50);
}
public void update() {
this.repaint();
}
public void keyPressed(KeyEvent key) {
if(key.getKeyCode() == KeyEvent.VK_UP) {
x = x + 50;
System.out.println("e");
update();
}
}

"I can set it's location in the paint() method" - Don't, seriously, you should never modify the state of any component within any paint method, in fact, you've broken the paint chain by not calling super.paint, which is going to cause you no end of other problems.
Instead, set the parent containers layout manager to null, you will now find that the component disappears. This is because the layout manager is responsible for setting the size and position of the component, which you will have to take over control of.
Instead of overriding paint you should be overriding paintComponent and calling super.paintComponent. Take a look at Performing Custom Painting for more details

Related

JPanel flickering with mouseMoved method

I have a simple JPanel that I'm using to display an image. I want some functionality where I'm able to pan the image by dragging it. I have something like this (note the code I have compiles and runs properly; the code below is heavily truncated to just get an idea of what I'm doing):
public class Data2DPanel extends JPanel {
public Data2DPanel(Data2D data) {
// Set image
this.image = Data2D.data2DToBufferedImage(data);
// Set mouse listener
Data2DMouseAdapter data2DMouseAdapter = new Data2DMouseAdapter();
this.addMouseListener(data2DMouseAdapter);
this.addMouseMotionListener(data2DMouseAdapter);
}
private class Data2DMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
switch (actionState) {
case PAN:
xOffset = xOffsetInit + e.getX()-xPosInit;
yOffset = yOffsetInit + e.getY()-yPosInit;
paintComponent(getGraphics());
break;
}
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
// Paint image
g2.drawImage(image,x,y,width,height,this);
}
}
}
The problem is that there is a lot of flickering. I've checked stackoverflow/google and it seems a lot of the flickering problems are because people override the paint method instead of the paintComponent method. I've also checked isDoubleBuffered and it returns true. Intuitively, I feel maybe the mouseDragged method is updating too much for paintComponent() to keep up, but I figured double buffering should still prevent flickering from occuring. My question is if there's something inherently wrong with this approach and if there's a standard or elegant solution to this.
paintComponent(getGraphics()); should be repaint(). Compounded problems.
You never want to make a call to getGraphics() for custom painting. The only Graphics object used to paint should be the one provided in the paint method.
You should never call paintXxx to try and "force" a repaint of the component. You should call repaint() and allow the RepaintManager to handle all the update and paint cycle

JApplet repaint() doesn't work

Problem:
Main.repaint() doesn't work for me. repaint() doesnt invoke my paint method in Main. I've tried calling validate before repainting but with no succes. Main paints perfectly initially or when resized but when i call repaint() in my code nothing is happening.
Here is how the program looks so far link
So im trying to create a level selection screen for a game in java. My game is a JApplet. I have a structure as follows:
my Main class which extends JApplet and contains an object of
LevelScreen class
LevelScreen has a paint method which Main invokes.
I tried to avoid using Swing since the layout managers gave me trouble with the design. So I've tried to make a structure which were simpler and more suited for my need.
paint() in Main.java
public class Main extends JApplet {
public static final int WIDTH = 700, HEIGHT = 500;
private static Main instance;
private LevelScreen levelScreen = new LevelScreen();
private View view = View.LEVELSCREEN;
public static Main getInstance() {
if (instance == null)
instance = new Main();
return instance;
}
#Override
public void init() {
setSize(WIDTH, HEIGHT);
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
if (view == View.LEVELSCREEN) {
levelScreen.mouseMoved(p);
}
}
});
}
#Override
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (view == View.LEVELSCREEN)
levelScreen.paint(g2);
}
public enum View {
GAME, LEVELSCREEN;
}
}
In the code of my Buttons i try to repaint Main because i want to make a fade out animation when mouse leaves the button. my problem is that i cant invoke the paint(Graphics g) in main with repaint()
Here i call repaint():
public void mouseExited() {
//start new thread to make fade out animation when mouse leave
mouseOver = false;
TimerTask task = new TimerTask() {
#Override
public void run() {
while (!mouseOver && opacity > 0.6) {
opacity -= 0.02;
//set level to 999 so i can see if the game repaints()
level = 999;
Main.getInstance().repaint(); //this doesnt work!!
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(task).start();
}
This is a problem with the way that you implement the singleton design pattern. The way you do it doesn't work for an applet, where the instance is created for you by the applet container. You can fix it by changing getInstance as follows:
public Main getInstance() {
return instance;
}
And add this line to the init method:
instance = this;
By the way, you should not override paint in a Swing component, which a JApplet is. You should override paintComponent instead, and call super.paintComponent(g) as the first line. This should fix the problem.
Main.getInstance().repaint(); //this doesnt work!!
I'm not surprised. You're not the one creating the instance of the JApplet, the browser is.
When you call this...
public static Main getInstance() {
if (instance == null)
instance = new Main();
return instance;
}
You are actually creating a second instance of the applet, which is NOT the one that is on the screen, so when you call repaint, Swing goes, "no point, you're not even displayable" and does nothing.
Without any more context of you code, you may not even need getInstance, instead reference the current instance using Main.this instead.
You should also consider taking a look at Performing Custom Painting.
Top level containers like JAppelt are not double buffered, which involves more work to paint directly to them. Instead, move your application to be based on something like a JPanel and override it's paintComponent method instead.
Painting is also a complex, multi-layered scheme. You MUST call super.paintXxx in order to preserve the paint chain and prevent any possible issues.

How to force repaint of a glass pane?

I have a Swing app with a glass pane over a map.
It paints dots at certain positions. When I click somewhere on the map, and the glass pane receives the message CONTROLLER_NEW_POLYGON_MARK I
want do display an additional dot at the position specified in the event data (see MyGlassPane.propertyChange).
The glass pane class is called MyGlassPane. Using the debugger I validated that addPointToMark is actually called in propertyChange.
But no additional dots appear on the screen.
How can I change the code so that PointSetMarkingGlassPane.paintComponent is called whenever an event (IEventBus.CONTROLLER_NEW_POLYGON_MARK) is fired?
public class PointSetMarkingGlassPane extends JComponent implements IGlassPane {
private final ILatLongToScreenCoordinatesConverter latLongToScreenCoordinatesConverter;
private final List<Point.Double> pointsToMark = new LinkedList<Point.Double>();
public PointSetMarkingGlassPane(final ILatLongToScreenCoordinatesConverter aConverter) {
this.latLongToScreenCoordinatesConverter = aConverter;
}
protected void addPointToMark(final Point.Double aPoint)
{
if (aPoint != null)
{
pointsToMark.add(aPoint);
}
}
#Override
protected void paintComponent(final Graphics aGraphics) {
for (final Point.Double pointToMark : pointsToMark)
{
final Point positionInScreenCoords = latLongToScreenCoordinatesConverter.getScreenCoordinates(pointToMark);
drawCircle(aGraphics, positionInScreenCoords, Color.red);
}
}
private void drawCircle(Graphics g, Point point, Color color) {
g.setColor(color);
g.fillOval(point.x, point.y, 10, 10);
}
}
public class MyGlassPane extends PointSetMarkingGlassPane implements PropertyChangeListener {
public MyGlassPane(ILatLongToScreenCoordinatesConverter aConverter) {
super(aConverter);
addPointToMark(DemoGlassPane.ARTYOM);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (IEventBus.CONTROLLER_NEW_POLYGON_MARK.equals(evt.getPropertyName()))
{
addPointToMark((Point.Double)evt.getNewValue());
invalidate();
}
}
}
As I think invalidate() only flags your component to check sizes and layout. You should call repaint() to repaint your pane.
Also I am wondering why you use propertyChangeListener for mouse clicks. I would prefer just simple mouse listener + MouseAdapter and MouseEvent x, y, buttons state.
invalidate() probably won't help you, as it flags a component for layout changes, not painting changes. Why not call repaint() instead?
For better performance, you could call the repaint method which takes a Rectangle (or four ints representing a rectangle), so that only the newly added point is repainted; I would suggest changing the return type of addPointToMark from void to java.awt.Point, and have it return the result of latLongToScreenCoordinatesConverter.getScreenCoordinates, so MyGlassPane can derive a rectangle from that Point which can then be passed to a repaint method.

Threading a paint method

I was wondering how I would thread the following code, or just a method in general:
public void run (){
public void paint(Graphics g) {
g.fillRect(20, 20, 20, 20);
for (int i = 20; i < 1000; i++) {
g.fillRect(20, i, 20, 20);
Thread.sleep(10);
}
}
}
I find that I am unable to make a thread of this code because I get an illegal start of expression error, which is fair but I do not see a way around it.
Its hard to tell what you are doing,
but seems like you are trying to override paint() of a Runnable from within its run() method.
This can surely not be done.
The logic is
Take a component
Override its paint method to draw what we need
Call method to update co-ordinates of rectangle (or in this case timer will do that)
Than call repaint() on the component so paint method may be called again and redraw the rectangle with its new co-ordinates (Timer would also take care of repainting after changing co-ordinates of Rectangle)
repeat last 2 steps as many times as needed/wanted
(when I say component I actually mean JPanel, paint method refers to overridden paintComponent(..) of JPanel as this is best practice.)
Some suggestions:
1) Dont override paint rather use JPanel and override paintComponent.
2) Dont forget to honor the paint chain and call super.XXX implementation of overridden paintComponent(Graphics g) (or any overridden method for that fact) unless purposefully leaving it out. i.e
class MyPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//do drawings here
}
}
3) If drawing in paintComponent it is usually needed to override getPreferredSize() and return Dimensions which fit the contents/drawings of JPanel, i.e:
class MyPanel extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(300,300);
}
}
3) Look at Swing Timer instead of Thread.sleep(..) as sleep will block GUI thread and make it seem to be frozen. i.e
Timer t = new Timer(10, new AbstractAction() {
int count = 20;
#Override
public void actionPerformed(ActionEvent ae) {
if (count < 1000) {
//increment rectangles y position
//now repaint container so we can see changes in co-ordinates (unless you have a timer which repaints for you too)
count++;
} else {//counter is at 1000 stop the timer
((Timer) ae.getSource()).stop();
}
}
});
t.start();
4) An alternative (because I see for now you are only moving a Rectangle which is not a Swing component) to Swing timer is TimerTask, and this can be used as long as no Swing components will be created/manipulated from within its run() method (as TimerTask does not run on EDT like Swing Timer). Note revalidate() and repaint() are Thread-safe so it can be used within TimerTask.
The advantage of the above is unnecessary code is kept of EDT (i.e moving AWT rectangle by changing co-ords) i.e
final TimerTask tt = new TimerTask() {
#Override
public void run() {
if (count < 1000) {
//increment rectangles y position
//now repaint container so we can see changes in co-ordinates (unless you have a timer which repaints for you too)
count++;
} else {//counter is at 1000 stop the timer
cancel();
}
}
};
new Timer().scheduleAtFixedRate(tt, 0, 10);//start in 0milis and call run every 10 milis

Java graphics trouble

I have a JComponent with a listener on it. On the JComponent, I draw a big image and the mouse listener adds small images where clicks occur (one big map on which I add some dots).
How can I programatically draw something outside the paintComponent method?
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(img1, 0, 0, this);
g2.finalize();
}
private MouseListener listener;
public void initListener() {
myCanvas = this;
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
myCanvas.getGraphics().drawImage(img,e.getX(),e.getY(), myCanvas);
}
};
addMouseListener(listener);
}
My problem is with this:
public void drawDot(int x, int y){
myCanvas.getGraphics().drawImage(img, x, y, myCanvas);
}
It doesn't do anything. I have tried repaint().
You can't do this. All drawing occurs in the paintComponent() method. What you should do is build a model that represents what you want to draw, and modify the model in your mouse listener. Then call repaint() to ask that this component be redrawn when the model is modified. Inside your paint() method render the full paint from the model. For example:
List<Point> pointsToDrawSmallerImage = new ArrayList<Point>();
...
listener = new MouseAdapter() {
public void mouseClicked(MouseEvent evt ) {
pointsToDrawSmallerImage.add( evt.getPoint() );
repaint();
}
}
...
public void paintComponent(Graphics g) {
g.clear(); // clear the canvas
for( Point p : pointsToDrawSmallerImage ) {
g.drawImage(img, p.x, p.y, myCanvas);
}
}
You have to manage the drawing inside the paintComponent method. Java Graphics is not stateful, you have to take care of what you actually need to draw whatever you want inside the method. Every time the paint method is called, everything must be drawn again, there is nothing that "stays" on the canvas while adding other components
This means that you should store a list of elements that the paint method will take care to draw, eg. ArrayList<Point> points, then in paint method you should iterate them:
for (Point p : points)
draw the point
so that you just add the point to the list with the listener and call repaint.
You can find guidelines for Swing/AWT drawing here..
A particual API has the behavior you would like to have though, it is called Cocos2D and it has a port for Android/Java that you can find here.
that is not how draw works, the draw method paints everything which is in the method itself on every repaint,
that means if you call a method to draw something once, it will only be drawed for one repaint cycle and that's it.
if you want something t be drawn on click you have to add it on on click to a collection and draw the whole collection in every paint cycle, so it will stay permanently.

Categories