Custom button is not correct size? - java

First of all, yes, I know I'm a newbie, and this is my first attempt at making a custom Component.
Okay, so in my project, I'm trying to make a custom button that does three things:
Draw a background
Get the icon of a selected app
Draw the app's name in the button.
It can do all three of those, except that the button is tiny:
The icon is the Jar application and the name is "Test Application".
This is the paintComponent(Graphics) method in my class, AppButton:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D antiAlias = (Graphics2D) g;
antiAlias.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.blue);
//g.fillRoundRect(x, y, width, height, arcWidth, arcHeight);
g.fillRoundRect(this.getX(), this.getY(), this.getWidth(), this.getHeight() - 25, 20, 20);
g.setColor(Color.red);
FontMetrics metrics = g.getFontMetrics();
int widthOfAppName = metrics.stringWidth(this.appName);
g.drawString(this.appName, this.getWidth() / 2 - (widthOfAppName / 2), this.getHeight() - 10);
File refrenceFile = new File(this.appURL);
try {
if (refrenceFile.exists()) {
ShellFolder sf = ShellFolder.getShellFolder(refrenceFile);
this.appIcon = new ImageIcon(sf.getIcon(true));
g.drawImage(this.appIcon.getImage(), this.getWidth() / 2 - (this.appIcon.getIconWidth() / 2),
this.getHeight() / 2 - (this.appIcon.getIconHeight() / 2), JLaunch.theFrame);
//Draw the centered Image
} else {
ImageIcon noImageFound = getNoImageAvailable();
//g.drawImage(img, x, y, observer)
g.drawImage(noImageFound.getImage(), this.getWidth() / 2 - (noImageFound.getIconWidth() / 2),
this.getHeight() / 2 - (noImageFound.getIconHeight() / 2), JLaunch.theFrame);
//Draw the centered Image
}
} catch (Exception e) {
e.printStackTrace();
}
}
On a side note, if anyone has a good understanding about custom Swing Components, can you also please point me to a good tutorial or a way of learning it like you did?

Well, for one, JButton can draw images. But what you are probably looking for is JComponent.setPreferedSize() You can check out the documentation here: http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#setPreferredSize(java.awt.Dimension).

Related

Is there a way I can rotate one object and not both of them?

I am working on a project that has a GUI and tanks that move. While tanks move fine, I am not able to figure out how to move/rotate them individually. I also need to clean my code as I feel I have a lot of extra stuff going on.
Here is some code, and here's what I tried.
I have four classes. Missiles, Tanks, and Board. I am calling keylisteners in the Tank class. Should I do that in the doDrawing method? The doDrawing method is in the Board class.
private void doDrawing(Graphics g)
{
final double rads = Math.toRadians(120);
final double sin = Math.abs(Math.sin(rads));
final double cos = Math.abs(Math.cos(rads));
final int w = (int) Math.floor(tank1.getX() * cos + tank1.getX() * sin);
final int h = (int) Math.floor(tank1.getY() * cos + tank1.getY() * sin);
Graphics2D g2d = (Graphics2D) g;
g2d.translate(w, h);
g2d.rotate(rot, tank1.getX(), tank1.getY());
AffineTransform backup = g2d.getTransform();
AffineTransform trans = new AffineTransform();
g2d.setTransform(backup);
//g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);
trans.setToIdentity();
trans.rotate(rot, h, w);
trans.translate(h, w);
trans.setTransform(backup);
g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);
//g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);
g2d.drawImage(tank2.getImage(), tank2.getX(), tank2.getY(), this);
List<Missile> missiles = tank1.getMissiles();
for (Missile missile : missiles)
{
//trans.rotate(Math.toRadians(rads), w/2, h/2);
g2d.drawImage(missile.getImage(), missile.getX(), missile.getY() - 7, this);
//g2d.rotate(rot, missile.getX(), missile.getY() - 7);
}
}
So, just looking at...
private void doDrawing(Graphics g)
{
//...
Graphics2D g2d = (Graphics2D) g;
g2d.translate(w, h);
g2d.rotate(rot, tank1.getX(), tank1.getY());
AffineTransform backup = g2d.getTransform();
AffineTransform trans = new AffineTransform();
g2d.setTransform(backup);
//g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);
trans.setToIdentity();
trans.rotate(rot, h, w);
trans.translate(h,w);
trans.setTransform(backup);
g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);
g2d.drawImage(tank2.getImage(), tank2.getX(), tank2.getY(), this);
//...
}
It's immediately obvious that you're applying a transformation to the Graphics context which is never undone, so everything painted after it will have the same transformation applied to it.
The first thing you need to do is break the paint logic down so that it can paint a single entity.
The second thing you need to do is create a copy of the Graphics context before you make any modifications to it, this will prevent the parent copy from carrying those modifications forward which will then be compounded with future modifications.
Maybe something like...
private void doDrawing(Graphics g) {
// Because I don't trust anybody
Graphics2D g2d = (Graphics2D) g.create();
drawTank(tank1, g2d);
g2d.dispose();
g2d = (Graphics2D) g.create();
drawTank(tank2, g2d);
g2d.dispose();
}
private void drawTank(Tank tank, Graphics2D g) {
final double rads = Math.toRadians(120);
final double sin = Math.abs(Math.sin(rads));
final double cos = Math.abs(Math.cos(rads));
final int w = (int) Math.floor(tank.getX() * cos + tank.getX() * sin);
final int h = (int) Math.floor(tank.getY() * cos + tank.getY() * sin);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(w, h);
// `rot` should be derived from the tank ... no idea where this is
g2d.translate(tank.getX(), tank.getY());
// Origin has now been translated to the tanks position
g2d.rotate(rot, 0, 0);
g2d.drawImage(tank.getImage(), 0, 0, this);
// We no longer to need to keep these resources
g2d.dispose();
// Create a new "clean" copy
g2d = (Graphics2D) g.create();
// I don't know if it's needed, but I would, personally, calculate
// any required rotation again, but that's me
List<Missile> missiles = tank1.getMissiles();
for (Missile missile : missiles) {
g2d.drawImage(missile.getImage(), missile.getX(), missile.getY() - 7, this);
}
}
For example, see:
Java - rotate image in place
How to move paint graphics along slope?
Recommendations:
Keep logic separate from display using a model-view code structure
In keeping with that, create a Tank logical class, one that knows its own position and its own orientation (or angle), and save your java.util.List<Tank> in the program's model.
Change the Tank position and orientation within a listener.
Use the View classes (the GUI classes) only to a) draw the state of the class's model and b) get input from the user.
Side recommendation: If this is Swing, avoid KeyListeners and instead use Key Bindings
You can use separate affine transforms for each tank
If your tanks move/rotate synchronously I guess the reason is that they all receive the same key events and react the same way.
Put the keylistener (or even better KeyBindings as suggested in another answer) into your Board class. The listener will check which key is pressed/released and give the correct tank the command to move or turn or whatever. This way you can make one tank react to WASD, while the other reacts on up/down/left/right.
The methods in the tank class however do not react to key events but could be named like forward, backward, turnleft, turnright.
The doDrawing method should do exactly as it's name suggests: just perform the drawing. This can happen any amount of times to get good screen refresh rates or redraw the board after another window no longer hides your game.

Java - Centering Text (on a Canvas)

thanks for checking out my question.
I am working a menu for the game that I'm making, and I want to center the game's title on the main menu. I've looked at several questions here on Stack Overflow, but couldn't find an answer.
Problem
The problem that I'm having is that whenever I call my centerString() method, it puts it in the center of the y-axis, but not of the x-axis. What am I doing wrong here?
Code
Method code
private void centerString(String txt, int width, int height, Graphics g, Font font) {
FontMetrics metrics = g.getFontMetrics();
int x = (width - metrics.stringWidth(txt)) / 2;
int y = (metrics.getAscent() + (height - (metrics.getAscent() + metrics.getDescent())) / 2);
g.setFont(font);
g.drawString(txt, x, y);
}
Where it's called
final Font fnt = new Font("Arial", Font.BOLD, 36);
centerString("ThatMarioEngine", toInt(screenSize.getWidth()), toInt(screenSize.getHeight()), g, fnt);
I have fixed the issue by making using the windows size, instead of the screen size. The centering issue was fixed by putting g.setFont(font); before I get the FontMetrics.

Text in center of Circle

There is a circle (planet), drawn through fillOval(). I need to write the text (number) on the circle, so that it must be always aligned exactly in the center. There is a center of the circle and its radius.
I understand that I need to calculate the width and length of the text (tried by getStringBounds), but does not work correctly.
protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Planet planet : planets) {
g2d.setColor(planet.getColor());
g2d.fillOval((int) planet.getX() - planet.getRadius(), (int) planet.getY() - planet.getRadius(),
planet.getRadius() * 2, planet.getRadius() * 2);
g.setFont(new Font("Serif", Font.PLAIN, 20));
g.setColor(Color.black);
String s = String.valueOf(250);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(s, g).getWidth();
g.setColor(Color.white);
g.drawString(s, (int) ((int) planet.getX() - textWidth/2), ((int) planet.getY() + fm.getMaxAscent() / 2));}}
You're using planet.getX()-planet.getRadius() and planet.getY() - planet.getRadius() to calculate your centered point of the circle.
Then you have to add the Text you want to paint at the centered position, which is calculated
planet.getX() - planet.getRadius() - textWidth/2 //or centered point - textWidth/2
which is your x-coordinate and
planet.getY() - planet.getRadius() + fm.getMaxAscent() / 2 //or centered point + fm.getMaxAscent()/2
which will be the y-coordinate.
But you are using planet.getX() - textWidth/2
This origin point has to be the center of the circle, not planet.getX()
In the past I've had to use both the ascent/descent values to calculate actual font height and it looks like you're only using ascent. Something to consider: not all strings will have the same max ascent/descent for a given font, so while the string "Dojo" may appear to be properly vertically aligned, the strings "cujo" and "coco" might not.
Another suggestion: instead of calculating the half-width of your text and using that to offset your drawing starting position, why not just center justify the text?

Java Help! paintComponent woes

I am new to Java and am learning new things everyday.
I am trying to learn how to take TWO DIFFERENT .png files and rotate them SEPARATELY using the paint component.
I am able to make both images paint and have separate movement, however the rotate function does NOT seem to recognize each 2DGraphic variable as independent.
Both images will be rotated to the last '.rotate' angles. I have looked up online but every tutorial referring to rotating a graphic are only dealing with one image. This works fine. I just can not get two images to rotate differently. I want P1GFX to be rotated separately from P2GFX.
Here is the code.
THIS CODE WORKS, however they are BOTH rotated to whatever the last .rotate specifies.
public void paintComponent(Graphics frogp1) {
Graphics2D P1GFX = (Graphics2D)frogp1;
Graphics2D P2GFX = (Graphics2D)frogp1;
P1GFX.rotate(90, 150 / 2, 150 / 2);
P2GFX.rotate(40, 50, 50);
P1GFX.drawImage(p1.getImage1(), p1x, p1y,this);
P2GFX.drawImage(p2.getImage2(), p2x, p2y, this);
}
So, I tried making multiple parameters in paintComponent! That should work right? NOPE!
THIS CODE Makes NO IMAGE appear at all! Nothing is drawn on the screen when there are more than one parameter inside paintComponent!
public void paintComponent(Graphics frogp1, Graphics frogp2) {
Graphics2D P1GFX = (Graphics2D)frogp1;
Graphics2D P2GFX = (Graphics2D)frogp2;
P1GFX.rotate(90, 150 / 2, 150 / 2);
P2GFX.rotate(40, 50, 50);
P1GFX.drawImage(p1.getImage1(), p1x, p1y,this);
P2GFX.drawImage(p2.getImage2(), p2x, p2y, this);
}
So I thought, hey! Maybe I need to make more than one paintComponent! Well, of course that is NOT possible without recreating my own instance of the repaint() method.
public void paintComponent1(Graphics frogp1) {
Graphics2D P1GFX = (Graphics2D)frogp1;
P1GFX.rotate(90, 150 / 2, 150 / 2);
P1GFX.drawImage(p1.getImage1(), p1x, p1y,this);
}
public void paintComponent2(Graphics frogp2) {
Graphics2D P2GFX = (Graphics2D)frogp2;
P2GFX.rotate(90, 150 / 2, 150 / 2);
P2GFX.drawImage(p2.getImage2(), p2x, p2y,this);
}
This makes repaint() do nothing, therefore nothing is drawn.
Please help me be able to rotate more than one image / Graphics2D variable !
Graphics2D P1GFX = (Graphics2D)frogp1;
Graphics2D P2GFX = (Graphics2D)frogp1;
Casting an Object means you are still using the same Object reference.
If you want two separate Graphics objects then you need to do:
Graphics2D p1gfx = (Graphics2D)frogp1.create();
Graphics2D p2gfx = (Graphics2D)frogp1.create();
then when you finish with the Graphics object you use:
p1gfx.dispose();
p2gfx.dispose();
And I change the variable names to follow Java naming conventions. Don't use all upper case characters for variable names.
You could just rotate, then unrotate and reposition:
public void paintComponent(Graphics graphics) {
Graphics2D g2d = (Graphics2D)graphics;
g2d.rotate(90, 150 / 2, 150 / 2);
g2d.drawImage(p1.getImage1(), p1x, p1y,this);
g2d.rotate(-90, 150 / 2, 150 / 2);
g2d.rotate(40, 50, 50);
g2d.drawImage(p2.getImage2(), p2x, p2y, this);
}

Java - Draw background issue

I want to draw my background image in the middle of the frame. As my image is not as big as the window, I want to put a black background.
Here is the code I use:
public void paint(Graphics g)
{
if(this.background != null)
{
int bounds_top = getHeight() / 2;
int bounds_left = getWidth() / 2;
int half_height = this.background.getHeight(null) / 2;
int half_width = this.background.getWidth(null) / 2;
g.drawImage(this.background, bounds_left - half_width, bounds_top - half_height, this.background.getWidth(null), this.background.getHeight(null), this);
this.setBackground(Color.black);
//this.setOpaque(false);
}
}
If I set the frame to be opaqe, my image is displayed but the background is gray.
If I set opaque to false, my frame is just black, no image is displayed.
So here is my question, how can I display my image and have a back background?
If you are doing this in a JPanel child, call setBackground(Color.black); in the constructor, and implement the code in paintComponent first calling super.paintComponent(g); for the black background.
You are drawing the image to the background, and then setting the background color to black. Try setting the background color to black first, and then draw the image to it. Otherwise it looks like you are drawing black over the image.
I found a litle trick to solve it:
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.BLACK);
g2.fill(new Rectangle2D.Double(0, 0, getWidth(), getHeight()));
g.drawImage(this.background, bounds_left - half_width, bounds_top - half_height, this.background.getWidth(null), this.background.getHeight(null), this);
This works well.

Categories

Resources