OK, So I have a program that displays a a String on a JPanel and it draws the lines of where the Ascents are, it draws a line at the Descent and halfway between, and a quarter way between. I have a combo box to change the font and the size of the String, and i have a JTextField to change the Text itself. I used actionListeners to update the String. Whenever i update the text of the String via the JTextField, the program glitches out, and shows a copy of the image of the JTextField on the top right corner of the JFrame.
Code:
package com.Patel.RichClients;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
//a class to demonstrate
public class StringAscent extends JFrame {
private static Font font;
private int fontSize = 50;
private StringPanel panel;
private JComboBox fontOptions;
private JComboBox fontSizeOptions;
private JTextField text;
// constructor
public StringAscent() {
// set the initial font to Times new Roman
font = new Font("Agency FB", Font.PLAIN, fontSize);
setVisible(true);
setSize(520, 170);
setTitle("String Ascents");
setLayout(new BorderLayout());
//set up the components
GraphicsEnvironment ge= GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fontNames = ge.getAvailableFontFamilyNames();
fontOptions = new JComboBox(fontNames);
text = new JTextField(22);
text.setText("Swing");
String[] sizeOptions = new String[50];
for (int i = 0; i < sizeOptions.length; i ++){
sizeOptions[i] = Integer.toString(i + 1);
}
fontSizeOptions = new JComboBox(sizeOptions);
panel = new StringPanel();
//set up actionListeners
fontOptions.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JComboBox ref = (JComboBox) e.getSource();
font = new Font(fontNames[ref.getSelectedIndex()], Font.PLAIN, fontSize);
panel.repaint();
}
});
fontSizeOptions.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
JComboBox ref = (JComboBox) e.getSource();
fontSize = Integer.parseInt(sizeOptions[ref.getSelectedIndex()]);
font = new Font(font.getName(), Font.PLAIN, fontSize);
panel.repaint();
}
});
text.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
panel.repaint();
}
});
//add components
add(panel, BorderLayout.NORTH);
add(fontOptions, BorderLayout.WEST);
add(text, BorderLayout.CENTER);
add(fontSizeOptions, BorderLayout.EAST);
}
public static void main(String[] args) {
Runnable showGui = new Runnable() {
public void run() {
StringAscent gui = new StringAscent();
}
};
SwingUtilities.invokeLater(showGui);
}
// inner JPanel class
private class StringPanel extends JPanel {
// constructor
public StringPanel() {
}
public Dimension getPreferredSize() {
return new Dimension(400, 100);
}
public void paintComponent(Graphics g) {
g.setColor(Color.black);
// set up the string
g.setFont(font);
// FontMetric is an object that holds information relevant to the
// rendering of the font
FontMetrics metric = g.getFontMetrics();
int ascent = metric.getAscent();
String string = text.getText();
g.drawString(string, 100, 50);
int x = 50;
// draw Ascent line
g.drawLine(x, 50 - ascent, x + 180, 50 - ascent);
// draw Ascent/2 line
g.drawLine(x, 50 - (ascent / 2), x + 180, 50 - (ascent / 2));
// draw Ascent/4 line
g.drawLine(x, 50 - (ascent / 4), x + 180, 50 - (ascent / 4));
// draw baseline line
g.drawLine(x, 50, x + 180, 50);
// draw Descent line
g.drawLine(x, 50 + metric.getDescent(), x + 180, 50 + metric.getDescent());
}
}
}
Add super.paintComponent(g) to the start of your paintComponent method
public void paintComponent(Graphics g) {
super.paintComponent(g)
g.setColor(Color.black);
//...
Basically, one of the jobs of paintComponent is to prepare the Graphics context for painting the current component. In Swing, the Graphics is a shared resource which is used by all the components within a window when been painted, so it's important to make sure that context is prepared properly before you paint on it
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting works
Related
I use the setBackground() method in the Driver Class to change the background color but it does not work.
package paint1;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JPanel;
/**
*
* #author Rehan Shakir
*/
public class PaintPanel extends JPanel {
private final ArrayList<Point> point = new ArrayList<>();
private Color color;
private final JButton red,blue,yellow,green,gray;
private int x = 0;
private int y = 0;
public PaintPanel()
{
setLayout(null);
red = new JButton(" Red ");
red.setBounds(0, 0, 80, 50);
red.setBackground(Color.red);
add(red);
blue = new JButton(" Blue ");
blue.setBounds(82,0 , 80, 50);
blue.setBackground(Color.BLUE);
add(blue);
yellow = new JButton("Yellow");
yellow.setBounds(163,0 , 80, 50);
yellow.setBackground(Color.yellow);
add(yellow);
green = new JButton(" Green");
green.setBounds(242,0 , 80, 50);
green.setBackground(Color.green);
add(green);
gray = new JButton(" Gray ");
gray.setBounds(322,0 , 80, 50);
gray.setBackground(Color.gray);
add(gray);
handler h = new handler();
red.addActionListener(h);
blue.addActionListener(h);
yellow.addActionListener(h);
green.addActionListener(h);
gray.addActionListener(h);
setBackground(Color.RED);
addMouseMotionListener(
new MouseMotionAdapter()
{
#Override
public void mouseDragged(MouseEvent e)
{
x = e.getX();
y = e.getY();
repaint();
}
}
);
}
private class handler implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
String s = e.getActionCommand();
if(s.equals(" Red "))
color = Color.RED;
else if(s.equals(" Blue "))
color = Color.blue;
else if(s.equals("Yellow"))
color = Color.yellow;
else if(s.equals(" Green"))
color = Color.green;
else if(s.equals(" Gray "))
color = Color.gray;
}
}
#Override
public void paintComponent(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, 20, 5);
}
}
<<>>
Here, I use the setBackground() method to change the color but it does not work.
package paint1;
import java.awt.BorderLayout;
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
*
* #author Rehan Shakir
*/
public class Paint1 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
JFrame Jf = new JFrame("A Simple Paint Program");
PaintPanel f = new PaintPanel();
f.setBackground(Color.red); //To Change BACKGROUND COLOR
Jf.add(f,BorderLayout.CENTER);
Jf.add(new JLabel("Drag The Mouse to Draw"),BorderLayout.SOUTH);
Jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Jf.setBackground(Color.black);
Jf.setVisible(true);
Jf.setSize(800,600);
}
}
Please provide me the solution, how can I change the background color of my JFrame? I just want to make the background color of JFrame from the default color to White color.
You've forget to call the parent method in paintComponent.
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g); // add this line to consider background!!!
g.setColor(color);
g.fillOval(x, y, 20, 5);
}
Important: Don't use setBounds() but rather learn the LayoutManager concept. This will help you to make your UI independed to OS, display resolution and window resizing.
I need some help with my code; I have a program that will show the graph of Ohm's Law. The graph was showing before I put a save button. When i run the program, it will only show the everything except for the graph. Also, I have problems in saving the current and voltage array into a .txt file. Please help!
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.*;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.Charset;
import static java.nio.file.Files.newBufferedWriter;
import java.nio.file.StandardOpenOption;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class DrawGraph extends JPanel {
double current[] = new double [999];
double voltage[] = new double [999];
final int TEXT = 20;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int w = 400;
int h = 400;
g2.setStroke(new BasicStroke(3));
g2.drawLine(TEXT, TEXT, TEXT, h-TEXT);
g2.drawLine(TEXT, h-TEXT, w-TEXT, h-TEXT);
for(int x= 0; x<1000; x++ )
{
current[x]=x+1;
voltage[x]=x+1;
}
g2.setPaint(Color.red);
g2.setStroke(new BasicStroke(2));
g2.draw(new Line2D.Double(TEXT, h-TEXT, w-TEXT ,TEXT ));
// Draw labels.
g2.setPaint(Color.black);
Font font = g2.getFont();
FontRenderContext frc = g2.getFontRenderContext();
LineMetrics lm = font.getLineMetrics("0", frc);
float sheight = lm.getAscent() + lm.getDescent();
// Ordinate label.
g2.setFont(new Font("Century Gothic", Font.PLAIN, 15));
String s = "Voltage V";
float sY = TEXT + ((h - 2*TEXT) - s.length()*sheight)/2 + lm.getAscent();
for(int r = 0; r < s.length(); r++)
{
String letter = String.valueOf(s.charAt(r));
float swidth = (float)font.getStringBounds(letter, frc).getWidth();
float sX = (TEXT - swidth)/2;
g2.drawString(letter, sX, sY);
sY += sheight;
}
// Abcissa label.
s = "Current A";
sY = h - TEXT + (TEXT - sheight)/2 + lm.getAscent();
float swidth = (float)font.getStringBounds(s, frc).getWidth();
float sX = (w - swidth)/2;
g2.drawString(s, sX, sY);
//Gridlines
int b=TEXT+(((w-TEXT)-TEXT)/10);
g2.setPaint(Color.gray);
for(int a=1; a<=10; a++)
{
b+=36;
g2.setStroke(new BasicStroke(1));
g2.drawLine(b, h-TEXT, b, TEXT);
g2.drawLine(TEXT, b, w-TEXT, b);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Ohm's Law");
JPanel panel = new JPanel(new BorderLayout(3,1));
JPanel titlepanel = new JPanel();
titlepanel.setPreferredSize(new Dimension(400,50));
JLabel title = new JLabel("OHM'S LAW");
title.setFont(new Font("Century Gothic", Font.BOLD, 25));
titlepanel.add(title);
panel.add(titlepanel,BorderLayout.NORTH);
JPanel graphpanel = new JPanel();
graphpanel.setBackground(Color.white);
graphpanel.setPreferredSize(new Dimension(420,420));
DrawGraph drawgraph = new DrawGraph();
graphpanel.add(drawgraph);
panel.add(graphpanel,BorderLayout.CENTER);
JPanel buttonpanel = new JPanel ();
buttonpanel.setPreferredSize(new Dimension(400,50));
JButton save = new JButton("SAVE");
save.addActionListener(
new ActionListener()
{
#Override
public void actionPerformed (ActionEvent event)
{
JFrame parentFrame = new JFrame();
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Specify a file to save");
int userSelection = fileChooser.showSaveDialog(parentFrame);
if (userSelection == JFileChooser.APPROVE_OPTION)
{
java.io.File fileToSave = fileChooser.getSelectedFile();
try
{
fileToSave.createNewFile();
try (BufferedWriter writer = newBufferedWriter(fileToSave.toPath(), Charset.defaultCharset(), StandardOpenOption.WRITE))
{
writer.write("V=I\t R=1\r Voltage \t Current\n");
//writer.write("Material: " + material.getSelectedValue().toString()+"\r\nv = " + v + "\r\nE1 = " + e1 + "\r\nE2 = " + e2);
}
}
catch (IOException ex)
{
Logger.getLogger(DrawGraph.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Save as file: " + fileToSave.getAbsolutePath());
}
}
}
);
save.setFont(new Font("Century Gothic", Font.BOLD, 15));
buttonpanel.add(save);
panel.add(buttonpanel, BorderLayout.SOUTH);
frame.add(panel);
frame.getContentPane().setBackground(Color.GREEN);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGui();
}
});
}
}
JPanel graphpanel = new JPanel();
graphpanel.setBackground(Color.white);
graphpanel.setPreferredSize(new Dimension(420,420));
DrawGraph drawgraph = new DrawGraph();
graphpanel.add(drawgraph);
panel.add(graphpanel,BorderLayout.CENTER);
You add your DrawGraph component to a JPanel. By default a JPanel uses a FlowLayout() which respects the preferred size of any component added to it. Your custom DrawGraph component has a preferred size of 0, so there is nothing to paint.
Every Swing component is responsible for determining its own preferred size so you need to override the getPreferredSize() method of your DrawGraph components to return its preferred size so the layout manager can do its job.
Read the section from the Swing tutorial on Custom Painting for more information and working examples.
Also, don't use setPreferredSize(). The layout manager will determine the preferred size of the panel based on the components added to it.
First things first. Make a JFrame derived class and in that class insert separately your buttonPanel which extends JPanel on the BorderLayout.SOUTH and your DrawGraph panel on the BorederLayout.Center. Don't add butttonPanel to the window you are drawing in.
I also suggest to change name from DrawGraph to GraphPanel you can do it just by clicking on any reference to the class and hitting alt+shift+r if you use Eclipse IDE.
So to conclude:
public class MainWindow extends JFrame(){
public void createGUI(){
add(graphPanel = new GraphPanel(), BorderLayout.CENTER);
add(buttonPanel = new JPanel(), BorderLayout.SOUTH);
}
}
and build your solution around that.
For my application I am designing a script editor. At the moment I have a JPanel which contains another JPanel that holds the line number (positioned to the left), and a JTextArea which is used to allow users to type their code in (positioned to the right).
At the moment, I have implemented a JScrollPane on the JTextArea to allow the user to scroll through their code.
For the JPanel containing the line numbers, they increment every time the user presses the enter key.
However, the problem is that I want the same JScrollPane (the one implemented on the JTextArea) to control the scrolling of the line number JPanel as well; i.e. when the user scrolls on the JTextArea, the line number JPanel should also scroll. But since the line numbers are held in a JPanel, I cant add that component to a JTextArea.
The constructor for the JPanel class containing the JTextArea and the line number JPanel:
private ScriptEditor() {
setBackground(Color.WHITE);
lineNumPanel = new LineNumberPanel();
scriptArea = new JTextArea();
scriptArea.setLineWrap(true);
scriptArea.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
scriptArea.setMargin(new Insets(3, 10, 0, 10));
JScrollPane scrollPane = new JScrollPane(scriptArea);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(width, height));
scriptArea.addKeyListener(this);
add(lineNumPanel);
add(scrollPane);
}
The constructor for the line number JPanel which adds JLabels within itself to represent the line numbers:
public LineNumberPanel() {
setPreferredSize(new Dimension(width, height));
box = Box.createVerticalBox();
add(box);
//setup the label
label = new JLabel(String.valueOf(lineCount));
label.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
//setup the label alignment
label.setVerticalAlignment(JLabel.TOP);
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalTextPosition(JLabel.TOP);
setAlignmentY(TOP_ALIGNMENT);
box.add(label);
}
You should use JScrollPane#setRowHeaderView to set the component that will appear at the left hand side of the scroll pane.
The benefit of this is the row header won't scroll to the left as the view scrolls to the right...
The example deliberately uses line wrapping...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Element;
import javax.swing.text.Utilities;
public class ScrollColumnHeader {
public static void main(String[] args) {
new ScrollColumnHeader();
}
public ScrollColumnHeader() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JTextArea ta = new JTextArea(20, 40);
ta.setWrapStyleWord(true);
ta.setLineWrap(true);
JScrollPane sp = new JScrollPane(ta);
sp.setRowHeaderView(new LineNumberPane(ta));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class LineNumberPane extends JPanel {
private JTextArea ta;
public LineNumberPane(JTextArea ta) {
this.ta = ta;
ta.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
revalidate();
repaint();
}
#Override
public void removeUpdate(DocumentEvent e) {
revalidate();
repaint();
}
#Override
public void changedUpdate(DocumentEvent e) {
revalidate();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
FontMetrics fm = getFontMetrics(getFont());
int lineCount = ta.getLineCount();
Insets insets = getInsets();
int min = fm.stringWidth("000");
int width = Math.max(min, fm.stringWidth(Integer.toString(lineCount))) + insets.left + insets.right;
int height = fm.getHeight() * lineCount;
return new Dimension(width, height);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
FontMetrics fm = ta.getFontMetrics(ta.getFont());
Insets insets = getInsets();
Rectangle clip = g.getClipBounds();
int rowStartOffset = ta.viewToModel(new Point(0, clip.y));
int endOffset = ta.viewToModel(new Point(0, clip.y + clip.height));
Element root = ta.getDocument().getDefaultRootElement();
while (rowStartOffset <= endOffset) {
try {
int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index);
String lineNumber = "";
if (line.getStartOffset() == rowStartOffset) {
lineNumber = String.valueOf(index + 1);
}
int stringWidth = fm.stringWidth(lineNumber);
int x = insets.left;
Rectangle r = ta.modelToView(rowStartOffset);
int y = r.y + r.height;
g.drawString(lineNumber, x, y - fm.getDescent());
// Move to the next row
rowStartOffset = Utilities.getRowEnd(ta, rowStartOffset) + 1;
} catch (Exception e) {
break;
}
}
}
}
}
And as I just discovered, #camickr has a much more useful example, Text Component Line Number
Create an Outer Panel which holds the Line Number panel and Text Area.
Then put this new panel into the Scroll Pane so you end up with this arrangement:
Which in code is something like:
private ScriptEditor() {
setBackground(Color.WHITE);
JPanel outerPanel = new JPanel();
lineNumPanel = new LineNumberPanel();
scriptArea = new JTextArea();
scriptArea.setLineWrap(true);
scriptArea.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
scriptArea.setMargin(new Insets(3, 10, 0, 10));
outerPanel.add(lineNumPanel, BorderLayout.WEST)
outerPanel.add(scriptArea, BorderLayout.CENTER)
JScrollPane scrollPane = new JScrollPane(outerPanel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(width, height));
scriptArea.addKeyListener(this);
add(lineNumPanel);
add(scrollPane);
}
I suggest placing both components into a panel, then place that panel into the scroll pane.
Ok, i know that this question has been asked before, but i just can't seem to get my application to work with the help from other peoples posts.
I am just trying to make a screen with a picture i made and then 3 buttons (just like a game menu). I have done the image and i am now trying to make the 3 buttons like this.
my code at the moment is this:
public class Test {
static int WIDTH = 700;
static int HEIGHT = 600;
public static void main(String[] args) {
JLabel label = new JLabel();
JFrame f = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("hello again");
button.addActionListener ((ActionListener) new Action1());
label.setIcon(new ImageIcon("C:\\Users\\barney\\workspace\\game\\res\\background.jpg"));
f.add(panel);
f.add(label);
f.setVisible(true);
f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
f.setSize(WIDTH, HEIGHT);
f.setResizable(false);
f.setLocationRelativeTo(null);
f.setTitle("2d platformer v1.1");
f.setVisible(true);
}
}
So how would i add the buttons like in the picture
also could you tell me if i have set my code out wrong as i am new to java.
thanks.
I'm going to be smacked around for this...
label.setLayout(new GridBagLayout());
GridBagConstraints gbc = GridBagConstraints();
gbc.insets = new Insets(4, 0, 4, 0);
gbc.gridwidth = GridBagConstraints.REMAINDER;
label.add(button, gbc);
// add the other buttons
Updated
Personally, I'd create a custom JPanel, that would paint the background and then add buttons to it, using the GridBagLayout from above
Updated
The problem with this question is "it depends"
It depends on how you want to implement the actual buttons. Because you've left "blank" spaces, I assume you intend to render some buttons onto the image. I would personally, render a full image and then add some normal buttons onto, but it's your project not mine.
If you wanted to use something like JButton (or any other Component), you would need a custom layout manager to help you align the components with gaps you have left. Hence the reason I would simply do a nice background and use something like GridBagLayout instead...
I apologize, I may have gone slightly over board, but I was having fun.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ButtonImage {
public static void main(String[] args) {
new ButtonImage();
}
public ButtonImage() {
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 MenuPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class MenuPane extends JPanel {
public static final Rectangle NEW_GAME_BOUNDS = new Rectangle(221, 157, 262, 85);
public static final Rectangle LOAD_GAME_BOUNDS = new Rectangle(221, 276, 262, 85);
public static final Rectangle EXIT_GAME_BOUNDS = new Rectangle(221, 396, 262, 85);
private BufferedImage img;
private Rectangle selectedBounds;
public MenuPane() {
try {
img = ImageIO.read(getClass().getResource("/vtr1a.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter mouseHandler = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
if (getNewGameBounds().contains(e.getPoint())) {
System.out.println("in new");
selectedBounds = getNewGameBounds();
} else if (getLoadGameBounds().contains(e.getPoint())) {
System.out.println("in load");
selectedBounds = getLoadGameBounds();
} else if (getExitGameBounds().contains(e.getPoint())) {
System.out.println("in exit");
selectedBounds = getExitGameBounds();
} else {
selectedBounds = null;
}
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
if (getNewGameBounds().contains(e.getPoint())) {
System.out.println("New Game");
} else if (getLoadGameBounds().contains(e.getPoint())) {
System.out.println("Load Game");
} else if (getExitGameBounds().contains(e.getPoint())) {
System.out.println("Exit Game");
}
}
};
addMouseListener(mouseHandler);
addMouseMotionListener(mouseHandler);
}
#Override
public Dimension getPreferredSize() {
return img == null ? super.getPreferredSize() : new Dimension(img.getWidth(), img.getHeight());
}
protected Point getImageOffset() {
Point p = new Point();
if (img != null) {
p.x = (getWidth() - img.getWidth()) / 2;
p.y = (getHeight() - img.getHeight()) / 2;
}
return p;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
Point p = getImageOffset();
g2d.drawImage(img, p.x, p.y, this);
drawText(g2d, "New Game", getNewGameBounds());
drawText(g2d, "Load Game", getLoadGameBounds());
drawText(g2d, "Exit Game", getExitGameBounds());
g2d.dispose();
}
}
protected void drawText(Graphics2D g2d, String text, Rectangle bounds) {
FontMetrics fm = g2d.getFontMetrics();
g2d.setColor(Color.GRAY);
if (selectedBounds != null) {
if (bounds.contains(selectedBounds)) {
RadialGradientPaint rpg = new RadialGradientPaint(
new Point(bounds.x + (bounds.width / 2), bounds.y + (bounds.height / 2)),
Math.min(bounds.width, bounds.height),
new float[]{0f, 1f},
new Color[]{new Color(252, 180, 42), new Color(97, 205, 181)}
);
g2d.setPaint(rpg);
RoundRectangle2D fill = new RoundRectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height, 22, 22);
g2d.fill(fill);
g2d.setColor(Color.WHITE);
}
}
g2d.drawString(
text,
bounds.x + ((bounds.width - fm.stringWidth(text)) / 2),
bounds.y + ((bounds.height - fm.getHeight()) / 2) + fm.getAscent());
}
protected Rectangle getNewGameBounds() {
return getButtonBounds(NEW_GAME_BOUNDS);
}
protected Rectangle getLoadGameBounds() {
return getButtonBounds(LOAD_GAME_BOUNDS);
}
protected Rectangle getExitGameBounds() {
return getButtonBounds(EXIT_GAME_BOUNDS);
}
protected Rectangle getButtonBounds(Rectangle masterBounds) {
Rectangle bounds = new Rectangle(masterBounds);
Point p = getImageOffset();
bounds.translate(p.x, p.y);
return bounds;
}
}
}
You could, just as easily, use separate images for each button and instead of rendering the text/graphics, render a image at the required position.
Check out Custom Painting and 2D Graphics for some more info.
I guess you didn't add Button to JFrame
f.add(button)
As you want different buttons than Swing button, to make the things simpler, you can draw your buttons in a JPanel (using the paintComponent method and Java 2D). Each JPanel will be a button. Them, you will use some mouse events to detect clicks, etc. Another approach is do the same thing that you are doing with your background (several JLabels with images) and register mouse events in each label. You can also create your own Look And Feel for your buttons, but this is not so simple. Of course, you will need to change your layout manager to fit your buttons.
I need to resize the font of multiple JLabel based on the scaling factor used to resize the container. To do this, I am setting the font of each JLabel to null so that they take the font of the container. It works, but it also produces strange results.
To be specific, the text seems to "lag" behind the container and sometimes it gets even truncated. I would like to avoid this behavior. Any idea how?
Example code simulating the behavior:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.geom.AffineTransform;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TextResize implements Runnable {
public static void main(String[] args) {
TextResize example = new TextResize();
SwingUtilities.invokeLater(example);
}
public void run() {
JFrame frame = new JFrame("JLabel Text Resize");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800, 400));
Container container = frame.getContentPane();
container.setLayout(new BorderLayout());
final JPanel labelContainer = new JPanel(new GridBagLayout());
labelContainer.setBorder(BorderFactory.createLineBorder(Color.black));
//initial font
final Font textFont = new Font("Lucida Console", Font.PLAIN, 10).deriveFont(AffineTransform.getScaleInstance(1, 1));
labelContainer.setFont(textFont);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(0, 10, 0, 10);
c.weightx = 1;
for (int i = 0; i < 5; i++) {
JLabel f = new JLabel("Text here with possibly looooooooong words");
f.setBorder(BorderFactory.createLineBorder(Color.green));
f.setFont(null);//take the font from parent
c.gridy = i;
labelContainer.add(f, c);
}
JSlider slider = new JSlider(0,50000,10000);
slider.addChangeListener(new ChangeListener() {
double containerWidth = labelContainer.getPreferredSize().getWidth();
double containerHeight = labelContainer.getPreferredSize().getHeight();
#Override
public void stateChanged(ChangeEvent ev) {
JSlider source = (JSlider) ev.getSource();
double scale = (double) (source.getValue() / 10000d);
//scaling the container
labelContainer.setSize((int) (containerWidth * scale), (int) (containerHeight * scale));
//adjusting the font: why does it 'lag' ? why the truncation at times?
Font newFont = textFont.deriveFont(AffineTransform.getScaleInstance(scale, scale));
labelContainer.setFont(newFont);
//print (font.getSize() does not change?)
System.out.println(scale + " " + newFont.getTransform() + newFont.getSize2D());
}
});
container.add(slider, BorderLayout.NORTH);
JPanel test = new JPanel();
test.setLayout(null);
labelContainer.setBounds(5, 5, labelContainer.getPreferredSize().width, labelContainer.getPreferredSize().height);
test.add(labelContainer);
container.add(test, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
Picture:
http://i.stack.imgur.com/tZLOO.png
Thanks,
-s
You can use any of the following methods:
by #trashgod
by #StanislavL
by #coobird
I sort of solved the problem adding:
#Override
protected void paintComponent(Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(java.awt.RenderingHints.KEY_TEXT_ANTIALIASING, java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
super.paintComponent(g2d);
}
Thanks anyway.
If performance speed is an issue, then you might find the following information about the 3 methods pointed to by MKorbel above useful.
Coobird's code has some limitations if used on a multi-call basis (eg in a sizeChanged Listener or a LayoutManager)
Trashgod's method is between 2 and 4 times slower than Stanislav's (but it also is designed to fill the area BOTH directions as the OP asked in that question, so not unexpected.)
The code below improves on Stanislav's rectangle method (by starting from the current font size each time rather than reverting back to MIN_FONT_SIZE each time) and thus runs between 20 and 50 times faster than that code, especially when the window/font is large.
It also addresses a limitation in that code which only effectively works for labels located at 0,0 (as in the sample given there). The code below works for multiple labels on a panel and at any location.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
// Improved version of http://java-sl.com/tip_adapt_label_font_size.html
public class FontResizingLabel extends JLabel {
public static final int MIN_FONT_SIZE=3;
public static final int MAX_FONT_SIZE=240;
Graphics g;
int currFontSize = 0;
public FontResizingLabel(String text) {
super(text);
currFontSize = this.getFont().getSize();
init();
}
protected void init() {
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
adaptLabelFont(FontResizingLabel.this);
}
});
}
protected void adaptLabelFont(JLabel l) {
if (g==null) {
return;
}
currFontSize = this.getFont().getSize();
Rectangle r = l.getBounds();
r.x = 0;
r.y = 0;
int fontSize = Math.max(MIN_FONT_SIZE, currFontSize);
Font f = l.getFont();
Rectangle r1 = new Rectangle(getTextSize(l, l.getFont()));
while (!r.contains(r1)) {
fontSize --;
if (fontSize <= MIN_FONT_SIZE)
break;
r1 = new Rectangle(getTextSize(l, f.deriveFont(f.getStyle(), fontSize)));
}
Rectangle r2 = new Rectangle();
while (fontSize < MAX_FONT_SIZE) {
r2.setSize(getTextSize(l, f.deriveFont(f.getStyle(),fontSize+1)));
if (!r.contains(r2)) {
break;
}
fontSize++;
}
setFont(f.deriveFont(f.getStyle(),fontSize));
repaint();
}
private Dimension getTextSize(JLabel l, Font f) {
Dimension size = new Dimension();
//g.setFont(f); // superfluous.
FontMetrics fm = g.getFontMetrics(f);
size.width = fm.stringWidth(l.getText());
size.height = fm.getHeight();
return size;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.g=g;
}
public static void main(String[] args) throws Exception {
FontResizingLabel label=new FontResizingLabel("Some text");
JFrame frame=new JFrame("Resize label font");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(label);
frame.setSize(300,300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}