Java Swing JTextField setMargin unexpected behavior - java

When I resize the window, my custom JTextField changes size. It only does this once, the first time I resize the window. It's related to this line inside paintComponent:
setMargin(new Insets(2, 25, 2, 2));
Running that command does not resize the text field until I resize the window. After resizing the window, running that command causes JTextField to become larger. Actual size of the window does not matter. The first time window size is changed, JTextField becomes larger and then it stays large until the end of time. I would prefer if the field was large as soon as I start the program, and obviously, I don't want it to randomly change size.
How can I fix the size of this JTextField so that it does not randomly change?
Here is the entire class:
/**
* From https://gmigdos.wordpress.com/2010/03/30/java-a-custom-jtextfield-for-searching/
* #author Georgios Migdos <cyberpython#gmail.com> */
public class JIconTextField extends JTextField {
private Icon icon;
private Insets dummyInsets;
public JIconTextField(int columns) throws IOException {
super(columns);
Border border = UIManager.getBorder("TextField.border");
JTextField dummy = new JTextField();
this.dummyInsets = border.getBorderInsets(dummy);
String path = "find-16x16.png";
InputStream is = Main.class.getClassLoader().getResourceAsStream(path);
setIcon(new ImageIcon(ImageIO.read(is)));
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int textX = 2;
if(this.icon!=null){
int iconWidth = icon.getIconWidth();
int iconHeight = icon.getIconHeight();
int x = dummyInsets.left + 5;//this is our icon's x
textX = x+iconWidth+2; //this is the x where text should start
int y = (this.getHeight() - iconHeight)/2;
icon.paintIcon(this, g, x, y);
}
setMargin(new Insets(2, textX, 2, 2));
}
}

Further to the comments, if you cut the code out from the paintComponent method and paste it into the constructor, you will get your desired result.
The paintComponent method for actual painting, i.e. blanking the background of the field and drawing the text. Generally, unless you specifically want to change the way the component is drawn, you should not need to override this method.

Related

How to add whitespace around an icon in matteborder

I experimented with MatteBorder in order to display an icon at the start of a JTextField (similiar to search icons being displayed in a textfield).
This is my current implementation:
JTextField textField = new JTextField("Filter", 8);
textField.setPreferredSize(new Dimension(getPreferredSize().width, 24));
Border outer = textField.getBorder();
// ugly workaround
Border padding = BorderFactory.createEmptyBorder(2, 2, 2, 2);
Border search = new MatteBorder(0, 16, 0, 0, icon);
textField.setBorder(new CompoundBorder(new CompoundBorder(outer, padding), search));
The icon is a 16x16px icon with no whitespace around it. The textfield is 24px high. I introduced a padding Border to have some whitespace around the icon (otherwise it would display a full icon and the first 4px of the icon under it). My problem is that there is no whitespace to the right of the icon (where the user enters text).
Question: Is there a way to have a defined amount of whitespace around the image, so it does not tile? Can I somehow add "padding" around the icon before I add it to the MatteBorder?
P.S. I know that I could add whitespace around the image file, but it is used in other instances where there should not be any whitespace around it.
Since a MatteBorder always tiles the icon, I would not use it. I would just write a custom border:
static void updateBorder(JTextField textField,
Icon icon) {
Border iconBorder = new AbstractBorder() {
private static final long serialVersionUID = 1;
#Override
public Insets getBorderInsets(Component c,
Insets insets)
{
insets.left = icon.getIconWidth() + 4;
insets.right = insets.top = insets.bottom = 0;
return insets;
}
#Override
public void paintBorder(Component c,
Graphics g,
int x,
int y,
int width,
int height)
{
icon.paintIcon(c, g,
x, y + (height - icon.getIconHeight()) / 2);
}
};
Border oldBorder = textField.getBorder();
// Inside text field's original border, place icon border
// with a little empty space around it.
textField.setBorder(BorderFactory.createCompoundBorder(
oldBorder,
BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(2, 2, 2, 2),
iconBorder)));
}
My problem is that there is no whitespace to the right of the icon (where the user enters text).
How about using a second CompoundBorder to add extra space on the inside of the Border?
Another possiblility is to use the Component Border which allows you to add a component to a Border. So you could add a JLabel with an Icon.

How can I map a custom image to a polygon in a game? [duplicate]

This question already has answers here:
Adding Image to a Polygon
(2 answers)
Closed 8 years ago.
I'm trying to basically remake Asteroids in java, but I'm going to use a bald eagle as a ship that shoots down Soviet flags. Right now, my bald eagle image is a square with a white outline around the eagle. I would like to remove this, is there any way to map this in a one-to-one fashion to a polygon of sorts?
Here's my code, though I don't know exactly how this will help anything:
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
GameTest t = new GameTest();
}
public static class GameTest extends JFrame {
private static final int WINDOW_WIDTH = 800;
private static final int WINDOW_HEIGHT = 500;
private GamePanel gamePanel;
public GameTest() throws IOException {
super("Deep Fried Freedom");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setLayout(new BorderLayout());
gamePanel = new GamePanel();
add(gamePanel);
center(this);
setVisible(true);
}
public static void center(JFrame frame) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Point center = ge.getCenterPoint();
int w = frame.getWidth();
int h = frame.getHeight();
int x = center.x - w / 2, y = center.y - h / 2;
frame.setBounds(x, y, w, h);
frame.validate();
}//end of center method
}
}
public class GamePanel extends JPanel {
public static BufferedImage baldEagleImage;
public GamePanel() throws IOException {
super();
baldEagleImage = ImageIO.read(new File("baldeagleimage.jpg"));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.black);// set color black
g.fillRect(0, 0, getWidth(), getHeight()); // paint background
g.drawImage(baldEagleImage, 350, 175, null);//paint the launcher
}//end paintComponent method
}//end GamePanel class
You have several ways that you can achieve this effect. Your best bet would be to use the alpha channel of your image. Just open your image in an image editing tool such as Gimp. In this set the background of your image to transparent around your image.
Another option (which is not the best) but fulfils your request is to use a paint stroke in Java2D. Have a look at using the java2d clipping feature. You can get a tutorial on this here
Usually you will have a Java object that represents the ship, and it has x and y coordinates called something like centerX and centerY. That gives you the center of the ship on your screen within the bounds of the viewable area. You modify these values when you want the ship to move up and down, and you g.drawImage the image you want to use at these coordinates as well (plus any offsets as needed to make the image appear centered to your liking).
A common method is to have a thread started upon initialization, and in that thread is a while(true) block that does an update() method on all objects that need to be updated, and then a Thread.sleep(17) that mimics a framerate of about 60 frames per second. In this method, your ship has its X and Y coords updated and then the image drawn at that location every 17 milliseconds, and that's how you get a ship (and any other object for that matter) to appear like they are moving around on the screen.

How to resize image Height to JFrame Height?

I have an image that is too tall for my JFrame even when it is maximized. I want to dynamically resize it so that the image will never be clipped by the top or bottom of the JFrame. I have inserted the image within a JLabel as an ImageIcon. I have tried setting the maximum size to no avail. How do I ensure that the height of the image will never be larger than the JFrame? I would ideally like to keep the ratio of height to width constant. The image is in a portrait orientation. Any ideas?
public class myClass extends JFrame {
private void initGUI(){
pane = getContentPane();
pane.setLayout(new BorderLayout());
next = new JButton("Next");
previous = new JButton("Previous");
page = new JLabel(loadImg());
page.setMaximumSize(this.getSize());
pane.add(next, BorderLayout.EAST);
pane.add(previous, BorderLayout.WEST);
pane.add(page, BorderLayout.CENTER);
}
}
I would ideally like to keep the ratio of height to width constant.
Check out Darryl's Stretch Icon. It will shrink/grow depending on the space available, while maintaining the width/height ratio.
You can try overriding the paintComponent(Graphics g) method and drawing the resized image yourself.
page = new JLabel(loadIMG()){
#Override
paintComponent(Graphics g)
{
//So we don't kill default behaviour
super.paintComponent(g);
int scaledWidth, scaledHeight;
//pseudo-code
scale and store into scaledWidth and scaledHeight;
render with g.drawImage(icon, x, y, scaledWidth, scaledHeight, null);
}
};

x-y plot and rotating text of axis-label

The code below plots some simple x-y data, but it has two problems that I do not know how to fix.
First, it plots negative values for some of the data points, which means lines extending southward below the x-axis. Since the data points are selected at random, you may have to resize the frame a bit in order to view new random numbers to be plotted in a way that shows this bug. All data values will be positive, so I want all deflections to project northward above the blue bottom marker line, and I need to make sure that no deflections extend southward below the blue bottom marker line.
Second, the y-axis label takes up too much real estate on the screen. It needs to be rotated -90 degrees. However, all the examples I have seen for this involve rotating the entire panel using a graphics2d object. I do not want to rotate the entire panel. Instead, I just want to rotate the text of the y-axis label.
Can anyone show me how to change the code below to fix these two specific problems?
The code is in the following two files:
GUI.java
import java.awt.*;
import javax.swing.*;
class GUI{
GUI() {
// Create a new JFrame container.
JFrame jfrm = new JFrame("X-Y Plot");
// Specify FlowLayout for the layout manager.
jfrm.getContentPane().setLayout(new FlowLayout());
int frameHeight = 400;
int frameWidth = 300;
// Give the frame an initial size.
jfrm.setSize(frameWidth, frameHeight);
// Terminate the program when the user closes the application.
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a text-based label.
JVertLabel myVLabel = new JVertLabel("y-axis label");
int width = myVLabel.WIDTH;
PaintPanel myPP = new PaintPanel(frameWidth-width-50-20,frameHeight-70);
jfrm.add(myPP);
jfrm.add(myVLabel);// Add the label to the frame.
// Display the frame.
jfrm.setVisible(true);
}
public static void main(String args[]) {
// Create the frame on the event dispatching thread.
SwingUtilities.invokeLater(new Runnable() {public void run(){new GUI();}});
}
public class JVertLabel extends JComponent {
private String text;
public JVertLabel(String s) {
text = s;
}//constructor
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.rotate(Math.toRadians(-90));
g2d.drawString(text, 0, 0);
}
}
}
PaintPanel.java
import java.awt.*;
import javax.swing.*;
import java.util.*;
class PaintPanel extends JPanel {
Insets ins; // holds the panel's insets
Random rand; // used to generate random numbers
PaintPanel(int w, int h) {
setOpaque(true);// Ensure that panel is opaque.
setPreferredSize(new Dimension(w, h));// Set preferred dimension as specfied.
rand = new Random();
}
protected void paintComponent(Graphics g) {// Override paintComponent() method.
super.paintComponent(g);// Always call superclass method first.
int height = getHeight();// Get height of component.
int width = getWidth();// Get width of component.
ins = getInsets();// Get the insets.
// Get dimensions of text
Graphics2D g2d = (Graphics2D) g;
Font font = new Font("Serif", Font.PLAIN, 12);
FontMetrics fontMetrics = g2d.getFontMetrics();
String xString = ("x-axis label");
int xStrWidth = fontMetrics.stringWidth(xString);
int xStrHeight = fontMetrics.getHeight();
String yString = "y-axis-label";
int yStrWidth = fontMetrics.stringWidth(yString);
int yStrHeight = fontMetrics.getHeight();
int leftStartPlotWindow = ins.left + 5 + yStrWidth;
int hPad = 3;
// Fill panel by plotting random data in a bar graph.
for (int i = leftStartPlotWindow + hPad; i <= width - leftStartPlotWindow - hPad + yStrWidth + 1; i += 4) {
int h = Math.abs(rand.nextInt(height - ins.bottom));//Get rand# betw/0 and max height of drawing area.
// If generated value w/in or too close to border, change it to just outside border.
if (h <= ins.top) {
h = ins.top + 1;
}
g.drawLine(i, Math.abs(height - ins.bottom - xStrHeight - 5), i, h);// Draw a line that represents data.
}
g.setColor(Color.blue);
g.drawRect(leftStartPlotWindow, ins.bottom + 2, width - leftStartPlotWindow - ins.right - hPad, height - xStrHeight - 6);
g.setColor(Color.red);
g.drawRect(ins.left, ins.bottom, width - ins.left - 1, height - ins.bottom - 1);
g.drawString(xString, (width / 2) - (xStrWidth / 2), height - ins.bottom - 6);
g.drawString(yString, ins.left, height / 2);
}
}
All data values will be positive, so I want all deflections to project northward above the blue bottom marker line, and I need to make sure that no deflections extend southward below the blue bottom marker line.
You need to calculate the random height so that all values fit into the space available. So the calculation would be something like:
int randomHeight = panelHeight - offset.top - offset.bottom - heightForTheXAxisText;
Then you don't have to worry about negative values or the top of the line extending outside the bounds of the panel.
all the examples I have seen for this involve rotating the entire panel using a graphics2d object. I do not want to rotate the entire panel. Instead, I just want to rotate the text of the y-axis label.
Set the rotation of the Graphics object, the draw the text, then restore the rotation of the Graphics object back to 0.
Or, you create create a new Graphcis object from the current Graphics object, then apply the rotation, draw the text and then dispose of the temporaray Graphics object.
JFreeChart addresses both issues by default, as shown in this example.
In your example,
You'll have to create the data model before trying to render it. Then you can scan it for min and max to determine the limits of your range axis. List<Double> may be a suitable choice.
You can rotate the range label by altering the graphics context's AffineTransform, as shown in RotateText.

Positioning animated image frames

I have a field which extends BitmapField (called AnimatedGIFField) and an AnimatorThread (extending Thread) which does the work of looping through the GIF frames, incrementing the current frame and invalidating the field, which calls the paint() method to draw the next frame (resulting in animation). The animation code works fine, but my issue is in the paint() method of the AnimatedGIFField class. I'm calling 'graphics.drawImage()' and I'm having trouble getting proper positions for x and y (the first two args to drawImage()). Positioning the AnimatedGIFField is working and is accomplished by overriding 'getPreferredWidth()' and 'getPreferredHeight()'. The relevant code is here:
public class AnimatedGIFField extends BitmapField {
/**
The current frame in the animation sequence; the AnimatorThread
increments this so paint() knows which frame to draw.
*/
private int currentFrame;
public AnimatedGIFField(GIFEncodedImage image, long style) {
super(image.getBitmap(), style);
this.image = image;
this.preferredWidth = this.image.getWidth();
this.preferredHeight = -(this.image.getHeight() * 4);
}
protected void paint(Graphics graphics) {
// Calling super draws the first background frame.
super.paint(graphics);
// Don't redraw if this is the first frame.
if (this.currentFrame != 0) {
// Draw the animation frame.
/* getFrameLeft() and getFrameTop() both return the top left-most position (0, 0). */
/* graphics.drawImage(this.image.getFrameLeft(this.currentFrame), */
/* this.image.getFrameTop(this.currentFrame), */
/* this.image.getFrameWidth(this.currentFrame), */
/* this.image.getFrameHeight(this.currentFrame), */
/* this.image, this.currentFrame, 0, 0); */
/*
Currently trying some hackish nonsense like this to position the frame, although
it probably won't scale properly across different devices/screen sizes.
*/
int x = (this.getManager().getWidth() / 2) - 45;
int y = (this.getManager().getHeight() / 2) + 83;
graphics.drawImage(x, y,
this.image.getFrameWidth(this.currentFrame),
this.image.getFrameHeight(this.currentFrame),
this.image, this.currentFrame, 0, 0);
}
}
}
What about something like this?
graphics.drawImage(this.image.getFrameLeft(this.currentFrame,
this.image.getFrameTop(this.currentFrame),
this.image.getFrameWidth(this.currentFrame),
this.image.getFrameHeight(this.currentFrame),
this.image, this.currentFrame, 0, 0);
Fixed the problem by stripping out all getPreferredWidth/Height stuff, removed the sublayout() method on my custom field and just overwrote layout() inside of my custom manager to position all fields (just the one) properly. That caused image.getFrameLeft() and image.getFrameTop() return proper values, which is where I had my positioning hacks before.
Thanks for the responses. I was making it way more complicated than it needed to be.

Categories

Resources