Java - Draw text in the center of an image - java

I need to write text in the center of an image. The text to write is not always the same.
The code I'm using is here:
// Here I first draw the image
g.drawImage(img, 22, 15, 280, 225, null);
// I get the text
String text = photoText.getText();
// Set the text color to black
g.setColor(Color.black);
// I draw the string
g.drawString(text, 79.5F, 220.0F);
The problem is that the text isn't at the center of the image, what can I do?
I only need to draw the text at the horizontal center.

Using a JLabel is less work, but FontMetrics, shown here, will let you manage the geometry directly.

One possible solution: draw the image in a JPanel, being sure to set the panel's preferredsize as the size of the image, have the JPanel use a GridBagLayout, and place the text in a JLabel that is added to the JPanel, without GridBagConstraints. This is one way to center the JLabel in the JPanel.

The easy way is to use a JLabel with an Icon and Text. Then set the horizontal/vertical text position to CENTER and the text is painted in the center of the image.
From your code it looks like you are trying to paint the text near the bottom of the image. In this case you can use the JLabel with an Icon as a container. Then you can set the layout to something like a BoxLayout and add another label with the text.
No custom painting is required for either approach.
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class LabelImageText extends JPanel
{
public LabelImageText()
{
JLabel label1 = new JLabel( new ColorIcon(Color.ORANGE, 100, 100) );
label1.setText( "Easy Way" );
label1.setHorizontalTextPosition(JLabel.CENTER);
label1.setVerticalTextPosition(JLabel.CENTER);
add( label1 );
//
JLabel label2 = new JLabel( new ColorIcon(Color.YELLOW, 200, 150) );
label2.setLayout( new BoxLayout(label2, BoxLayout.Y_AXIS) );
add( label2 );
JLabel text = new JLabel( "More Control" );
text.setAlignmentX(JLabel.CENTER_ALIGNMENT);
label2.add( Box.createVerticalGlue() );
label2.add( text );
label2.add( Box.createVerticalStrut(10) );
//
JLabel label3 = new JLabel( new ColorIcon(Color.GREEN, 200, 150) );
add( label3 );
JLabel text3 = new JLabel();
text3.setText("<html><center>Text<br>over<br>Image<center></html>");
text3.setLocation(20, 20);
text3.setSize(text3.getPreferredSize());
label3.add( text3 );
//
JLabel label4 = new JLabel( new ColorIcon(Color.CYAN, 200, 150) );
add( label4 );
JTextPane textPane = new JTextPane();
textPane.setText("Add some text that will wrap at your preferred width");
textPane.setEditable( false );
textPane.setOpaque(false);
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
StyledDocument doc = textPane.getStyledDocument();
doc.setParagraphAttributes(0, doc.getLength(), center, false);
textPane.setBounds(20, 20, 75, 100);
label4.add( textPane );
}
public static class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("LabelImageText");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new LabelImageText() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
I mean I have to write a text in the center of the image and then save the image
You can use Screen Image to create an image of any component. This assumes you are displaying the image and text on a GUI.
Or, if you are talking about just reading in an image adding text to the image and then saving the image, then you will need to create a BufferedImage and draw the image on it and then draw the text on it. You will need to use the FontMetrics class as mentioned by Trashgod. My suggestion won't help.

I used TextLayout to get the text properly centered:
Here's how to create the image and save it as a file:
int imgWidth = 250;
int imgHeight = 250;
var img = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
var backgroundColor = new Color(0, 150, 100);
g.setPaint(backgroundColor);
g.fillRect(0, 0, imgWidth, imgHeight);
var font = new Font("Arial", Font.PLAIN, 80);
g.setFont(font);
g.setPaint(Color.WHITE);
String text = "0";
var textLayout = new TextLayout(text, g.getFont(), g.getFontRenderContext());
double textHeight = textLayout.getBounds().getHeight();
double textWidth = textLayout.getBounds().getWidth();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// Draw the text in the center of the image
g.drawString(text, imgWidth / 2 - (int) textWidth / 2,
imgHeight / 2 + (int) textHeight / 2);
String imgFormat = "png";
ImageIO.write(img, imgFormat, new File("/home/me/new_image." + imgFormat));

Related

Graphic2D generating image with automatic text positioning

How can I auto-position text inside an image when generating one? For example, I have this method to generate my .png image:
public static void generatePNG(String message){
try {
int width = 400, height = 400;
// TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
// into integer pixels
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = bi.createGraphics();
Font font = new Font("TimesRoman", Font.BOLD, 15);
ig2.setFont(font);
FontMetrics fontMetrics = ig2.getFontMetrics();
int stringWidth = fontMetrics.stringWidth(message);
int stringHeight = fontMetrics.getAscent();
ig2.setPaint(Color.black);
ig2.drawString(message, (width - stringWidth) / 2, height / 2 + stringHeight / 4);
ImageIO.write(bi, "PNG", new File("myimg.png"));
} catch (IOException ie) {
ie.printStackTrace();
}
}
But this centers text in my img, which was ok for testing, but now I want to add multiple lines to image and to start from top to bottom. The message I build with StringBuilder that I parse in my method is separated in new lines by System.lineSeparator() I also don't know how is it with width and height as my width must remain the same, while height can change as much as it wants, but how can I know how much will it need just by the message?
I assume that this is an example of an XY-problem. If your goal is to generate an image, with a certain text, automatically adjusted for some font and line breaks, then you could do this on your own. You could use FontMetrics and its methods to compute the proper size of the image, and the proper locations that are then drawn with drawString calls.
But this is complicated. And it's far, far, far more complicated than it looks at the first glance. I'm not even talking about النص العربي (arabic text), but even about the seemingly most trivial elements of fonts.
The most simple solution therefore is probably to rely on the hundreds of thousands of lines of time-tested code that have already been written by experts in this field, in order to solve this problem.
Which means:
Just drop the text into a JTextArea, and then create an image from that.
The following is an MCVE that shows how this could be achieved.
The core is the createTextImage method, which allows you to create an image from the text, with a certain font and colors. Optionally, you may specify the width of the image, and leave the daunting task of doing the line breaks to the JTextArea.
You may notice the "HTML" checkbox at the top. When it is enabled, the input is passed to a createHtmlImage method, so that you can even enter something like
<html>
This is <u>underlined</u> <br>
or in <i>italics</i>
</html>
to obtain an image of the rendered HTML output.
The full code is here:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class GenerateTextImage {
public static void main(String[] args) {
String text = "This is a text" + "\n"
+ "with one line that is muuuuuuuuuuuuuuuuch longer than the others" + "\n"
+ "and some empty lines" + "\n"
+ "\n"
+ "\n"
+ "as a test.";
SwingUtilities.invokeLater(() -> createAndShowGui(text));
}
private static void createAndShowGui(String initialText) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
JPanel controlPanel = new JPanel();
JCheckBox htmlCheckBox = new JCheckBox("HTML", false);
controlPanel.add(htmlCheckBox);
f.getContentPane().add(controlPanel, BorderLayout.NORTH);
JPanel mainPanel = new JPanel(new GridLayout(1, 2));
f.getContentPane().add(mainPanel, BorderLayout.CENTER);
JTextArea inputTextArea = new JTextArea();
JScrollPane sp0 = new JScrollPane(inputTextArea);
sp0.setBorder(BorderFactory.createTitledBorder("Input:"));
mainPanel.add(sp0);
ImagePanel imagePanel = new ImagePanel();
JScrollPane sp1 = new JScrollPane(imagePanel);
sp1.setBorder(BorderFactory.createTitledBorder("Image:"));
mainPanel.add(sp1);
Runnable updateImage = () -> {
if (!htmlCheckBox.isSelected()) {
String text = inputTextArea.getText();
BufferedImage image = createTextImage(text);
imagePanel.setImage(image);
} else {
String text = inputTextArea.getText();
BufferedImage image = createHtmlImage(text);
imagePanel.setImage(image);
}
};
inputTextArea.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
updateImage();
}
public void insertUpdate(DocumentEvent e) {
updateImage();
}
public void removeUpdate(DocumentEvent e) {
updateImage();
}
private void updateImage() {
updateImage.run();
}
});
htmlCheckBox.addChangeListener(e -> {
updateImage.run();
});
inputTextArea.setText(initialText);
f.setSize(1200, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static BufferedImage createTextImage(String text) {
return createTextImage(text, -1, new Font("TimesRoman", Font.BOLD, 15), Color.BLACK, Color.WHITE);
}
/**
* Creates an image with the given text, using the given font and foreground- and background color.<br>
* <br>
* If the given width is not positive, then the width of the image will be computed
* to show the longest line that appears in the text. If the given width is positive,
* then the lines of the given text will be wrapped (at word boundaries) if possible,
* so that the whole text can be displayed.
*
* #param text The text
* #param width The image width
* #param font The font
* #param foreground The foreground color
* #param background The background color
* #return The image
*/
private static BufferedImage createTextImage(String text, int width, Font font, Color foreground, Color background) {
JTextArea textArea = new JTextArea(text);
textArea.setFont(font);
textArea.setForeground(foreground);
textArea.setBackground(background);
if (width > 0)
{
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setSize(new Dimension(width, Short.MAX_VALUE));
}
Dimension size = textArea.getPreferredSize();
int w = Math.max(1, size.width);
if (width > 0)
{
w = width;
}
int h = Math.max(1, size.height);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
SwingUtilities.paintComponent(g, textArea, new JPanel(), 0, 0, w, h);
g.dispose();
return image;
}
private static BufferedImage createHtmlImage(String text) {
return createHtmlImage(text, new Font("TimesRoman", Font.BOLD, 15), Color.BLACK, Color.WHITE);
}
/**
* Creates an image with the given HTML string, using the given font and foreground- and background color.<br>
*
* #param html The HTML string
* #param font The font
* #param foreground The foreground color
* #param background The background color
* #return The image
*/
private static BufferedImage createHtmlImage(String html, Font font, Color foreground, Color background) {
JLabel label = new JLabel(html);
label.setOpaque(true);
label.setFont(font);
label.setForeground(foreground);
label.setBackground(background);
Dimension size = label.getPreferredSize();
int w = Math.max(1, size.width);
int h = Math.max(1, size.height);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
SwingUtilities.paintComponent(g, label, new JPanel(), 0, 0, w, h);
g.dispose();
return image;
}
static class ImagePanel extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage image;
public void setImage(BufferedImage image) {
this.image = image;
repaint();
}
#Override
public Dimension getPreferredSize() {
if (image == null || super.isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(image.getWidth(), image.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
g.drawImage(image, 0, 0, null);
}
}
}
}
Edit: A small addendum for one of the comments. It centers the text horizontally, using the snippet from https://stackoverflow.com/a/3213361/3182664 . Note that this is not tested thoroughly. At some point, questions, comments and edits boil down to "Write some code for me". I'm a freelancer. You can hire me.
private static BufferedImage createTextImage(String text, int width, Font font, Color foreground, Color background) {
JTextPane textPane = new JTextPane();
textPane.setText(text);
// See https://stackoverflow.com/a/3213361/3182664
StyledDocument doc = textPane.getStyledDocument();
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
doc.setParagraphAttributes(0, doc.getLength(), center, false);
textPane.setFont(font);
textPane.setForeground(foreground);
textPane.setBackground(background);
if (width > 0)
{
//textPane.setLineWrap(true);
//textPane.setWrapStyleWord(true);
textPane.setSize(new Dimension(width, Short.MAX_VALUE));
}
Dimension size = textPane.getPreferredSize();
int w = Math.max(1, size.width);
if (width > 0)
{
w = width;
}
int h = Math.max(1, size.height);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
SwingUtilities.paintComponent(g, textPane, new JPanel(), 0, 0, w, h);
g.dispose();
return image;
}

Is there a way to make JTextField for my address bar larger and curvier

I'm making a browser just to practice my Java skills, is there a way to make my address bar which is a JTextField, larger instead of the swing's default value and also curvier. Here's my code.
//imports of the GUI
//import java.awt.*;
//import java.awt.event.*;
//import javax.swing.*;
//import javax.swing.event.*;
//import javax.swing.text.*;
//import javax.swing.GroupLayout.*;
//extends is to use the GUI class
public class ReadFile extends JFrame {
private JTextField addressBar; //to have the address bar
private JEditorPane display; //display the html information
//constructor
//Set the frame icon to an image loaded from a file.
public ReadFile() {
super("SPHERE"); //name of the browser
addressBar = new JTextField("enter an URL", 50); //inside the URL
addressBar.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent event){
loadCrap(event.getActionCommand());
}
}
);
add(addressBar, BorderLayout.NORTH);
display = new JEditorPane();
display.setEditable(false);
display.addHyperlinkListener(
new HyperlinkListener(){
public void hyperlinkUpdate(HyperlinkEvent event){
if(event.getEventType()==HyperlinkEvent.EventType.ACTIVATED){
loadCrap(event.getURL().toString());
}
}
}
);
add(new JScrollPane(display), BorderLayout.CENTER);
setSize(600,200);
setVisible(true);
}
//load crap to display on the screen
private void loadCrap(String userText){
try{display.setPage(userText);
addressBar.setText(userText);}catch(Exception e){System.out.println("crap!")}
}
}
I want to make a really usable browser, like I want the html and its' CSS pages to show, what else do I have to learn to make this work.
Almost all of this comes down to manipulating the border, but this may not produce the results your after, for example...
JTextField field = new JTextField(10);
field.setBorder(new CompoundBorder(field.getBorder(), new EmptyBorder(10, 0, 10, 0)));
Creating a rounded border is more difficult...
and also curvier
There are a few ways you might achieve, this for example, you could create a Border of your own, for example...
public class RoundedBorder extends AbstractBorder {
#Override
public Insets getBorderInsets(Component c, Insets insets) {
insets.left = 5;
insets.right = 5;
insets.top = 5;
insets.bottom = 5;
return insets;
}
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2d = (Graphics2D) g.create();
RoundRectangle2D shape = new RoundRectangle2D.Float(0, 0, width - 1, height - 1, 20, 20);
g2d.setColor(Color.BLACK);
g2d.draw(shape);
g2d.dispose();
}
}
Then apply it to your field...
field.setBorder(new CompoundBorder(new RoundedBorder(), new EmptyBorder(10, 0, 10, 0)));
Which produces something like...
But I don't like this, as, if you look closely, the area outside the border is still painted...You could have the border fill this area, but I like having the ability to provide transparent capabilities to components, so instead, you could fake it...
Basically, what this does is creates a custom component that can paint the around the field, but, because it can better control the painting process, can also provide transparency outside the border effect...
public class FakeRoundedBorder extends JPanel {
private JTextField field;
public FakeRoundedBorder(JTextField field) {
this.field = field;
setBorder(new EmptyBorder(5, 5, 5, 5));
field.setBorder(new EmptyBorder(10, 0, 10, 0));
setLayout(new BorderLayout());
add(field);
setOpaque(false);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RoundRectangle2D shape = new RoundRectangle2D.Float(0, 0, getWidth() - 1, getHeight() - 1, 20, 20);
g2d.setColor(field.getBackground());
g2d.fill(shape);
g2d.setColor(Color.BLACK);
g2d.draw(shape);
g2d.dispose();
}
}
This is just a bunch of examples of course, you'll need to clean it up and provide customisation to the values yourself ;)
I'm not sure what you mean by "curvier". But here's a way to resize it and set the font:
addressBar.setFont(new Font("TimesRoman", Font.ITALIC, 30));

JButton settext specific position

I have a JButton with an image icon and I need to position the text.
Is there a way to position the text of a JButton in a specific position instead of using CENTER,LEADING, TOP and so on?
I've never tried it on a JButton before, but maybe you can add a JLabel as a component on the button. Then you can use layout managers or Borders to position the label more appropriately.
Here are some example of adding a component to a JLabel with an Icon:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class LabelImageText extends JPanel
{
public LabelImageText()
{
JLabel label1 = new JLabel( new ColorIcon(Color.ORANGE, 100, 100) );
label1.setText( "Easy Way" );
label1.setHorizontalTextPosition(JLabel.CENTER);
label1.setVerticalTextPosition(JLabel.CENTER);
add( label1 );
//
JLabel label2 = new JLabel( new ColorIcon(Color.YELLOW, 200, 150) );
label2.setLayout( new BoxLayout(label2, BoxLayout.Y_AXIS) );
add( label2 );
JLabel text = new JLabel( "More Control" );
text.setAlignmentX(JLabel.CENTER_ALIGNMENT);
label2.add( Box.createVerticalGlue() );
label2.add( text );
label2.add( Box.createVerticalStrut(10) );
//
JLabel label3 = new JLabel( new ColorIcon(Color.GREEN, 200, 150) );
label3.setLayout( new GridBagLayout() );
add( label3 );
JLabel text3 = new JLabel();
text3.setText("<html><center>Text<br>over<br>Image<center></html>");
text3.setLocation(20, 20);
text3.setSize(text3.getPreferredSize());
label3.add( text3 );
//
JLabel label4 = new JLabel( new ColorIcon(Color.CYAN, 200, 150) );
add( label4 );
JTextPane textPane = new JTextPane();
textPane.setText("Add some text that will wrap at your preferred width");
textPane.setEditable( false );
textPane.setOpaque(false);
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
StyledDocument doc = textPane.getStyledDocument();
doc.setParagraphAttributes(0, doc.getLength(), center, false);
textPane.setBounds(20, 20, 75, 100);
label4.add( textPane );
}
public static class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("LabelImageText");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new LabelImageText() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
I doubt the JTExtPane example will work with a button because the text pane will intercept the mouse events and so you will not be able to click the button when you click on the text. So I would stick with the examples that add a label to the button.
Spanish:
Puedes sobreescribir el método setText(String str).
Google Traductor English: You can overwrite the method setText (String str).
private static final String HTML_1 = "<html><body style='margin:0px 0px 0px 0px'>";
private static final String HTML_2 = "</html>";
#Override
public void setText(String txt) {
if (txt.isEmpty()) {
txt = "Dispositivo";
}
String txt2 = HTML_1 + txt + HTML_2;
super.setText(txt2);
}
you can use set bounds method to set the postion of the text on the button
JLabel positiongLabel = new JLabel();
positiongLabel.setBounds(100,50,300,30);
for more detail click here

Clear previously drawn string over an image

I am wondering how I can clear a previously drawn string before drawing a new one over an image, if I do not clear the strings overlap. I have tried graphics#drawRect and overriding paintComponent(Graphics), still no eval.
Here is my code:
paintComponent(Graphics)
public class SplashScreen extends JLabel
{
private static final long serialVersionUID = 5515310205953670741L;
private static final String ROOT = "./assets/img/";
private static final SplashScreen INSTANCE = new SplashScreen( get( new File( ROOT, "splash.png" ) ), get( new File( ROOT, "splash-bar.png" ) ) );
private final BufferedImage background;
private final BufferedImage foreground;
private final JLabel label;
private SplashScreen( BufferedImage background, BufferedImage foreground )
{
this.background = background;
this.foreground = foreground;
label = new JLabel( new ImageIcon( background ) );
JWindow window = new JWindow();
window.setSize( background.getWidth(), background.getHeight() );
window.getContentPane().add( label );
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
window.setLocation( dimension.width / 2 - window.getSize().width / 2, dimension.height / 2 - window.getSize().height / 2 );
window.setVisible( true );
}
public void updateStatus( String status )
{
Graphics g = background.getGraphics();
g.drawString( status, 304, 301 );
g.dispose();
label.repaint();
}
public void updateBar( int width )
{
Graphics g = background.getGraphics();
g.drawImage( foreground, 73, 309, width, foreground.getHeight(), null );
g.dispose();
label.repaint();
}
private static BufferedImage get( File file )
{
try {
return ImageIO.read( file );
} catch( IOException e ) {
throw new RuntimeException( e.getMessage() );
}
}
public static SplashScreen getInstance()
{
return INSTANCE;
}
}
Any help is greatly appreciated. :-)
Thanks.
You don't need to do custom painting.
Here are a couple of different ways to paint text on a label with an Icon:
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
public class LabelImageText extends JPanel
{
public LabelImageText()
{
JLabel label1 = new JLabel( new ColorIcon(Color.ORANGE, 100, 100) );
label1.setText( "Easy Way" );
label1.setHorizontalTextPosition(JLabel.CENTER);
label1.setVerticalTextPosition(JLabel.CENTER);
add( label1 );
//
JLabel label2 = new JLabel( new ColorIcon(Color.YELLOW, 200, 150) );
label2.setLayout( new BoxLayout(label2, BoxLayout.Y_AXIS) );
add( label2 );
JLabel text = new JLabel( "More Control" );
text.setAlignmentX(JLabel.CENTER_ALIGNMENT);
label2.add( Box.createVerticalGlue() );
label2.add( text );
label2.add( Box.createVerticalStrut(10) );
//
JLabel label3 = new JLabel( new ColorIcon(Color.GREEN, 200, 150) );
add( label3 );
JLabel text3 = new JLabel();
text3.setText("<html><center>Text<br>over<br>Image<center></html>");
text3.setLocation(20, 20);
text3.setSize(text3.getPreferredSize());
label3.add( text3 );
//
JLabel label4 = new JLabel( new ColorIcon(Color.CYAN, 200, 150) );
add( label4 );
JTextPane textPane = new JTextPane();
textPane.setText("Add some text that will wrap at your preferred width");
textPane.setEditable( false );
textPane.setOpaque(false);
SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
StyledDocument doc = textPane.getStyledDocument();
doc.setParagraphAttributes(0, doc.getLength(), center, false);
textPane.setBounds(20, 20, 75, 100);
label4.add( textPane );
}
public static class ColorIcon implements Icon
{
private Color color;
private int width;
private int height;
public ColorIcon(Color color, int width, int height)
{
this.color = color;
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
g.setColor(color);
g.fillRect(x, y, width, height);
}
}
private static void createAndShowUI()
{
JFrame frame = new JFrame("LabelImageText");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new LabelImageText() );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Then to change the text you just change the text in the component you are using to display the text.
If none of these help then the way to do custom painting is to override the paintCompnent() method of the JLabel. You should not be painting the text directly on the BufferedImage.
Basically, you can't.
What you should try doing is keep a reference to the original background image and when you need to change the text, copy it to a temp image and draw the String there, replacing it (the temp copy) as the label's icon...
A better solution might be to paint the text directly as part of the labels paint process by overriding the paintComponent method

Why is the custom pie chart not displaying?

Enter text, push button, why does the pie chart does not display?
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class PieChart extends JFrame {
private JTextField jtfParticipation = new JTextField();
private JTextField jtfProjects = new JTextField();
private JTextField jtfQuizzes = new JTextField();
private JTextField jtfFinalExam = new JTextField();
private JButton jbtCreatePieChart = new JButton("Create Pie Chart");
public PieChart() {
// Text panel
JPanel panel1 = new JPanel(new GridLayout(8, 0));
panel1.setBorder(new TitledBorder("Input percentages:"));
// A font to change from the default Plain font to Arial font
Font arialFont = new Font("Arial", Font.PLAIN, 12);
JLabel jlblParticipation = new JLabel("Participation %");
JLabel jlblProjects = new JLabel("Projects %");
JLabel jlblQuizzes = new JLabel("Quizzes %");
JLabel jlblFinalExam = new JLabel("Final Exam %");
// The labels use the new font
jlblParticipation.setFont(arialFont);
jlblProjects.setFont(arialFont);
jlblQuizzes.setFont(arialFont);
jlblFinalExam.setFont(arialFont);
// Adds the objects to the panel
panel1.add(jlblParticipation);
panel1.add(jtfParticipation);
panel1.add(jlblProjects);
panel1.add(jtfProjects);
panel1.add(jlblQuizzes);
panel1.add(jtfQuizzes);
panel1.add(jlblFinalExam);
panel1.add(jtfFinalExam);
// Assigns the text panel and the button to one panel
JPanel panel2 = new JPanel(new BorderLayout());
panel2.add(panel1, BorderLayout.CENTER);
panel2.add(jbtCreatePieChart, BorderLayout.SOUTH);
add(panel2, BorderLayout.WEST);
jbtCreatePieChart.addActionListener(new ButtonListener());
}
class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// Set the size and trigger a repaint
final PieChartGraphic pie = new PieChartGraphic();
add(pie, BorderLayout.CENTER);
pie.setPreferredSize(new Dimension());
pie.repaint();
}
}
class PieChartGraphic extends JPanel {
#Override
protected void paintComponent(Graphics slice) {
super.paintComponent(slice);
int xCenter = getWidth() / 2;
int yCenter = getHeight() / 2;
int radius = (int) (Math.min(getWidth(), getHeight()) * 0.4);
int x = xCenter - radius;
int y = yCenter - radius;
double inputIsDouble;
int inputIsInt;
int availablePercentage = 100;
int currentAngle = 0;
ArrayList<JTextField> jtfs = new ArrayList<>();
jtfs.add(jtfProjects);
jtfs.add(jtfParticipation);
jtfs.add(jtfQuizzes);
jtfs.add(jtfFinalExam);
ArrayList<Color> color = new ArrayList<>();
color.add(Color.RED);
color.add(Color.GREEN);
color.add(Color.BLUE);
color.add(Color.WHITE);
for (int i = 0; i < jtfs.size(); i++) {
inputIsDouble = userInput(jtfs.get(i).getText(), availablePercentage);
inputIsInt = (int) (inputIsDouble * 3.6);
// Sets the color of the filled
slice.setColor(color.get(i));
// Sets the start point and size of the angle
slice.fillArc(x, y, 2 * radius, 2 * radius, currentAngle, inputIsInt);
currentAngle += inputIsInt;
availablePercentage -= inputIsDouble;
}
// Places the text strings
slice.setColor(Color.BLACK);
slice.drawString("Participation - " +
"\jtfParticipation.getText() + "%", 1 / 4 * x, 1 / 4 * y);
slice.drawString("Projects - " + jtfProjects.getText() + "%", 3 / 4 * x, 1 / 4 * y);
slice.drawString("Quizzes -- " + jtfQuizzes.getText() + "%", 1 / 4 * x, 3 / 4 * y);
slice.drawString("Final - " + jtfFinalExam.getText() + "%", 3 / 4 * x, 3 / 4 * y);
}
}
public static double userInput(String inputIsString, int availablePercentage) {
return new Double(inputIsString).doubleValue();
}
public static void main(String[] args) {
PieChart frame = new PieChart();
frame.setTitle("CMIS Pie Chart");
frame.setSize(334, 215);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
}
}
Here are a few things to consider:
After creating panel3, the component sizes are never set:
panel3.setSize(200, 200);
PieChartGraphic pieChartGraphic = new PieChartGraphic();
pieChartGraphic.setSize(200, 200);
panel3.add(pieChartGraphic);
After adding jpanel3, you should call
jpanel3.repaint();
to paint the Graphics of the added component.
There is user input and validation happening half way through the paintComponent call. This should be done immediately before you do any painting, etc.
I've used:
public static double userInput(String inputIsString, int availablePercentage) {
return new Double(inputIsString).doubleValue();
}
and can see the Piechart.
I think you have a couple problems here.
First is the size/repaint problem #Reimeus pointed out. You need to set the size of the pie chart, and then cause it to be repainted.
Next, the way you add a new PieChartPanel with each button click seems an odd way to do things. You will end up creating new panels that are just stacked on top of each other each time the user presses the button. Why not just have the Pie chart panel permanently added, and just update what is displayed on it, depending on the state?
I changed the following bits of code. First I changed the action listener to remove the extra panel and set a size on the panel. I also added a repaint, although that doesn't seem to matter:
class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// Set the size and trigger a repaint
final PieChartGraphic pie = new PieChartGraphic();
add(pie, BorderLayout.EAST);
pie.setPreferredSize( new Dimension( 300, 300 ) );
pie.repaint();
}
}
I then simplified the paintComponent method to remove all of your code:
protected void paintComponent(Graphics slice) {
super.paintComponent(slice);
slice.setColor( Color.RED );
slice.fillRect( 0, 0, getWidth(), getHeight() );
}
Finally, I changed the JFrame to be resizable. This is an easy way during testing to trigger a repaint. With these changes, when I click on the button, I don't see anything, but if I resize the window, I see the red box.

Categories

Resources