I need to get a mirror of JLabel or JTextArea.
http://www.subirimagenes.net/i/150305101716451074.jpg
If I use a JTextArea, I'll need the letters are complety mirrored.
If I use a JLabel, I'll need format and the mirrored letters.
The example was created on Photoshop.
My idea is using graphics(), but I don't have idea how to do it.
Here's the bad news: It's not as straight-forward as we might wish, there's a limitation. In Swing, graphics transformations are applied only on the paint operation, not the general layout and event process. Therefore, in Swing the mirrored component is basically "unusable", it cannot be used for anything else than displaying the mirror image of the primary component. Coordinates of mouse clicks etc. will be wrong.
Therefore, this is all tricky stuff, a bit hackish.
There are multiple ways how you can do that.
One possibility is to use two views on one model and tell the Graphics of one of the views to flip horizontally.
Here's an example how to do so which demonstrates a flipped JEditorPane:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class MirrorText {
public static void main(final String... args) {
SwingUtilities.invokeLater(MirrorText::setupUI);
}
public static void setupUI() {
final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
final JEditorPane mirroredEditor = new JEditorPane("text/html", "") {
protected Graphics getComponentGraphics(final Graphics g) {
return horizontalFlip(super.getComponentGraphics(g), getWidth());
}
};
mirroredEditor.setDocument(editor.getDocument());
final JFrame frame = new JFrame("mirrored label");
final JPanel mirrorPanel = new JPanel(new GridLayout(1, 2));
mirrorPanel.add(new JScrollPane(editor));
mirrorPanel.add(new JScrollPane(mirroredEditor));
frame.add(mirrorPanel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
public static Graphics horizontalFlip(final Graphics g, final int width) {
final Graphics2D g2d = (Graphics2D) g;
final AffineTransform tx = g2d.getTransform();
tx.scale(-1.0, 1.0);
tx.translate(-width, 0);
g2d.setTransform(tx);
return g2d;
}
}
The advantage of this solution is that the mirror entirely the original component's MVC and observers because it is the same type of component (View/Controller) on the very same model.
The disadvantage of this solution is that you have create the mirror component in a way that is very specific to the component that is mirrored.
Another possibility is to create a decorator JComponent Mirror which can mirror an arbitrary other JComponent. This is a bit tricky, as in Java, decorators cannot override methods of the decorated object, and the Mirror needs to be updated (repainted) as well whenever the original component is updated (repainted).
Here's an incomplete example using a Mirror which hooks into the corresponding events. Incomplete because it only hooks into DocumentEvent but should also hook onto other events as well, like CaretEvent. It would be nice if Swing would have something like a PaintEvent. As far as I am aware of, it hasn't. (Well, in fact it has, but there's no corresponding PaintListener and addPaintListener().)
Also incomplete because the Mirror doesn't observe the original component's attributes like size. It only works because the GridLayout on the MirrorPanel keeps the mirror size in sync with the original component.
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
public class MirrorText {
public static void main(final String... args) {
SwingUtilities.invokeLater(MirrorText::setupUI);
}
public static void setupUI() {
final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
final MirrorPanel mirrorPanel = new MirrorPanel(new JScrollPane(editor));
editor.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
public void insertUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
public void removeUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
});
final JFrame frame = new JFrame("mirrored label");
frame.add(mirrorPanel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}
class MirrorPanel extends JPanel {
public MirrorPanel(final JComponent c) {
super(new GridLayout(1, 2));
add(c);
add(new Mirror(c));
}
public void updateMirror() {
repaint();
}
}
class Mirror extends JComponent {
private final JComponent mirroredComponent;
public Mirror(final JComponent mirroredComponent) {
this.mirroredComponent = mirroredComponent;
}
public static Graphics horizontalFlip(final Graphics g, final int width) {
final Graphics2D g2d = (Graphics2D) g;
final AffineTransform tx = g2d.getTransform();
tx.scale(-1.0, 1.0);
tx.translate(-width, 0);
g2d.setTransform(tx);
return g2d;
}
public void paint(final Graphics g) {
mirroredComponent.paint(horizontalFlip(g, mirroredComponent.getWidth()));
}
}
There probably are more possibilities as well. For example, one could override the mirrored component's paint() method to update the mirror component as well. That would get rid of getting notified, but it would lead to unnecessary paint() calls in case painting isn't done due to content change but due to buffer destruction (i.e. other window moved away).
Here's one way to create a mirror image.
Basically, you print the contents of the JTextArea on a BufferedImage. Then you reverse the pixels of the BufferedImage on the X axis.
package com.ggl.testing;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class MirrorImage implements Runnable {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MirrorImage());
}
#Override
public void run() {
frame = new JFrame("Mirror Image Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout());
JPanel textPanel = new JPanel();
JTextArea textArea = new JTextArea(15, 30);
textPanel.add(textArea);
mainPanel.add(textPanel);
MirrorPanel mirrorPanel = new MirrorPanel();
mirrorPanel.setPreferredSize(textPanel.getPreferredSize());
mainPanel.add(mirrorPanel);
TextListener listener = new TextListener(textArea, mirrorPanel);
textArea.getDocument().addDocumentListener(listener);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
listener.createImage(textArea);
frame.setVisible(true);
}
private class MirrorPanel extends JPanel {
private static final long serialVersionUID = 2496058019297247364L;
private Image image;
public void setImage(Image image) {
this.image = image;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, (getWidth() - image.getWidth(this)) / 2,
(getHeight() - image.getHeight(this)) / 2, this);
}
}
private class TextListener implements DocumentListener {
private JTextArea textArea;
private MirrorPanel mirrorPanel;
public TextListener(JTextArea textArea, MirrorPanel mirrorPanel) {
this.textArea = textArea;
this.mirrorPanel = mirrorPanel;
}
#Override
public void insertUpdate(DocumentEvent event) {
createImage(textArea);
}
#Override
public void removeUpdate(DocumentEvent event) {
createImage(textArea);
}
#Override
public void changedUpdate(DocumentEvent event) {
createImage(textArea);
}
public void createImage(JTextArea textArea) {
BufferedImage img = new BufferedImage(textArea.getWidth(),
textArea.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
textArea.printAll(g2d);
g2d.dispose();
createMirrorImage(img);
mirrorPanel.setImage(img);
}
private void createMirrorImage(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
int[][] pixels = new int[width][height];
for (int i = width - 1; i >= 0; i--) {
int j = width - i - 1;
for (int k = 0; k < height; k++) {
pixels[j][k] = img.getRGB(i, k);
}
}
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
img.setRGB(i, j, pixels[i][j]);
}
}
}
}
}
Related
I made 5 java classes that their goals were to Translate English into French and to setup the image of each words writing in the Jfield1 when you click on the Jbutton.
My applet arrive to translate each English words writing in JField1 into French words in JField2. And I add two more Classes(ImageImplement and ImageInFrame) for setting up the image But unfortunately nothing work. And I really don’t know how to use imageArray.
Please help me!!!
I am sorry my english is not so good.
THIS IS MY APPLET
My first Class is StringArrayEnglishWord
public class StringArrayEnglishWords extends JPanel {
String[] names;
Graphics2D g2d;
public StringArrayEnglishWords(){
EnglishWords();
// drawNames();
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
public void EnglishWords(){
names=new String[16];
names[0]="Tomatoes";
names[1]="Chairs";
names[2]="Car";
names[3]="Computer";
names[4]="Flowers";
names[5]="Family";
names[6]="Sister";
names[7]="Husband";
names[8]="God";
names[9]="Book";
names[10]="Watch";
names[11]="Pencil";
names[12]="Bottle";
names[13]="Map";
names[14]="Wife";
names[15]="Bag";
}
public void drawNames(){
for (int i=0; i<names.length; i++){
g2d.drawString(names[i], 50, (1+i)*16);
}
}
public int indexOfWord(String userInput)
{
for(int i=0;i<names.length;i++)
{
if(userInput.equals(names[i])){return i;}
}
return -1;
}
}
My second classes is StringArrayFrenchWords:
public class StringArrayFrenchWords extends JPanel {
String[] names2;
Graphics2D g2d;
public StringArrayFrenchWords(){
FrenchWorld();
// drawNames();
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
public void FrenchWorld(){
names2=new String[16];
names2[0]="Tomate";
names2[1]="Chaise";
names2[2]="Voiture";
names2[3]="Ordinateur";
names2[4]="Fleurs";
names2[5]="Familles";
names2[6]="Soeur";
names2[7]="Epoux, Mari";
names2[8]="Dieu";
names2[9]="Livre";
names2[10]="Montre, Regarder";
names2[11]="Crayon";
names2[12]="Bouteille";
names2[13]="Carte,plan";
names2[14]="Femmme";
names2[15]="Sac";
}
public void drawNames(){
for (int i=0; i<names2.length; i++){
g2d.drawString(names2[i], 50, (1+i)*16);
}
}
}
My third Classe is extend Japplet (Translator):
public class Translator extends JApplet implements ActionListener {
String StringField1, StringImage;
StringArrayEnglishWords Englishw;
StringArrayFrenchWords Frenchw;
ImageInJframe img;
ImageImplement Images;
Image Image;
JButton[] buttons;
JComboBox[] comboBoxes;
JButton TranslateButton;
JPanel JPanel0,JPanel1;
JPanel buttonPanel;
JPanel comboBoxesPanel;
JTextField field1,field2;
ImageIcon Imageicon;
JLabel ImageLabel;
public void init(){
setUpFields();
Englishw=new StringArrayEnglishWords();
Frenchw=new StringArrayFrenchWords();
img = new ImageInJframe(); // this was for set up the image
setUpimages();// for setup image too
}
public void setUpFields() {
TranslateButton = new JButton("TranslateButton");
field1 = new JTextField(10);
field2 = new JTextField(10);
field2.setEditable(false);
JPanel0 = new JPanel(new FlowLayout());
JPanel0.add(field1);
TranslateButton.addActionListener(this);
field1.addActionListener(this);
field2.addActionListener(this);
JPanel0.add(TranslateButton);
JPanel0.add(field2);
add(JPanel0, BorderLayout.SOUTH);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
public void setUpimages() {//setUp images too
ImageLabel.setIcon((Icon) img);
JPanel1 = new JPanel(new FlowLayout());
JPanel1.add(ImageLabel);
add(JPanel1, BorderLayout.CENTER);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == TranslateButton) {
System.out.println(field1);
StringField1 = field1.getText();
int index = Englishw.indexOfWord(StringField1);//this will give you the index of the word
if (index == -1) {
field2.setText("Word not found.");
System.out.println(field2);
} else {
field2.setText(Frenchw.names2[index]);
System.out.println("English Words=" + field1.getText());
System.out.println("French Words =" + field2.getText());
//setup Image
Image = img.getIconImage();
int index2 = img.indexOfImage(StringImage);
JPanel1.add(img);
}
}
}
}
My two classes was for seting up images bu using JFame and imageArray: ImageInJframe and ImageImplement
public class ImageInJframe extends JFrame {
JFrame Frame;
Image[] pics;
String[] names = {"apple.jpg", "bags.jpg", "bathroom.jpg", "battled.jpg", "car.png",
"chairs.jpg", "Computer.jpg", "family.jpg", "flowers.jpg", "god.jpg", "house.png", "map.jpg"
, "men.jpg", "pencil.jpg", "sisters.jpg", "tomato.jpg", "watch.jpg", "women.jpg", "book.gif"};
final int NUM_PICS=names.length;
Graphics g;
public static void main(String args[])
{
new ImageInJframe().start();
}
public void start()
{
ImageImplement panel = new ImageImplement(new ImageIcon(String.valueOf(pics)).getImage());
add(panel);
setVisible(true);
setSize(400,400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setImage();
}
public void setImage() {
pics = new Image[NUM_PICS];
}
public int indexOfImage(String userInput)
{
for(int i=0;i<pics.length;i++)
{
if(userInput.equals(pics[i])){return i;}
}
return -1;
}
}
ImageImplement extend JPanel:
public class ImageImplement extends JPanel {
Image img;
public ImageImplement(Image img) {
this.img = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
setLayout(null);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
Painting occurs on demand, when your paintComponent is called, you are expected to repaint the current state of the UI. Maybe you should start by taking a look at Painting in AWT and Swing and Performing Custom Painting for more detals about how painting works
Basically, from your paintComponent, you need to call draw, but you should pass it the Graphics context that was passed to the paintComponent method. It's dangerous and unwise to maintain a reference to a Graphics context you did not create
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new StringArrayEnglishWords());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class StringArrayEnglishWords extends JPanel {
String[] names;
public StringArrayEnglishWords() {
EnglishWords();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
drawNames(g2d);
g2d.dispose();
}
public void EnglishWords() {
names = new String[16];
names[0] = "Tomatoes";
names[1] = "Chairs";
names[2] = "Car";
names[3] = "Computer";
names[4] = "Flowers";
names[5] = "Family";
names[6] = "Sister";
names[7] = "Husband";
names[8] = "God";
names[9] = "Book";
names[10] = "Watch";
names[11] = "Pencil";
names[12] = "Bottle";
names[13] = "Map";
names[14] = "Wife";
names[15] = "Bag";
}
public void drawNames(Graphics2D g2d) {
for (int i = 0; i < names.length; i++) {
g2d.drawString(names[i], 50, (1 + i) * 16);
}
}
public int indexOfWord(String userInput) {
for (int i = 0; i < names.length; i++) {
if (userInput.equals(names[i])) {
return i;
}
}
return -1;
}
}
}
You should also be careful when modifying the Graphics context, it is shared by all the components which are within the same window, in most cases, I find it useful to take a snapshot of the Graphics context (Graphics#create) and use that, then I don't need to remember to reset it when I'm done.
Applets are also a dead technology, the plugin has been deprecated and is no longer been supported and in most cases, is actively blocked by most browsers. See Java Plugin support deprecated and Moving to a Plugin-Free Web for more details
Updated
Change...
ImageImplement panel = new ImageImplement(new ImageIcon(String.valueOf(pics)).getImage());
add(panel);
to...
JLabel panel = new JLabel(new ImageIcon(names[0]).getImage());
add(panel);
Assuming the image files are in the current directory where the program is been executed from, then it should work. If they are contained within the package with the class files, then you'll need to use Class#getResource to obtain a reference to the the image and pass the resulting URL to ImageIcon
Your ImageImplement panel is doing nothing, so even if it had a proper image to show, it wouldn't show anything.
String.valueOf(pics) makes no sense at all
You've posted a whole bunch of, what seems to be unrelated code and it's difficult to know what problem you're referring to
This...
ImageLabel.setIcon((Icon) img);
Makes no sense, img is an instance of ImageInJFrame, how is that even remotely related to a Icon?
I officially have no idea what you're trying to do
I'm quite new to swing, and I'm having an issue with graphics not showing up in my JFrame. What I should be seeing is a blue rectangle slowly moving downwards through the frame, and behind it is a plain white background. However, when I run my main class, all I see is a plain JFrame. This is my code:
Execute class
public class Execute {
public static void main (String[ ] args) {
GUI gui = new GUI();
gui.createFrame(800,600);
ElevatorOne e1 = new ElevatorOne();
e1.addElevatorOne();
}
}
ElevatorOne class (Where the graphics should be initialized and added)
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class ElevatorOne extends GUI{
int y = 100;
public void addElevatorOne() {
drawElevatorOne drawE1 = new drawElevatorOne();
frame.getContentPane().add(drawE1);
for(int i = 0; i < 130; i++) {
y++;
drawE1.repaint();
try {
Thread.sleep(50);
} catch (Exception ex) { }
}
}
#SuppressWarnings("serial")
class drawElevatorOne extends JPanel{
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLUE);
g.drawRect(200,y,40,60);
}
}
}
And finally, my GUI class (where frame is created)
import javax.swing.JFrame;
public class GUI {
JFrame frame = new JFrame();
public void createFrame(int x, int y) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.setSize(x, y);
}
}
While you've got an accepted answer, I do take issue with that answer and feel impelled to add my two cents:
I see no purpose to your having a GUI class and then having Elevator1 extend it. If you want Elevator1 to use a JFrame, then have it create a JFrame as there really is no need or benefit from your inheritance.
Myself, I'd have Elevator1 extend JPanel and then have it draw in its own paintComponent method, thus eliminating the need for drawElevatorOne inner class (which should be named DrawElevatorOne to adhere to Java naming conventions).
You are using Thread.sleep in a Swing GUI which is extremely risky to do. The only reason this works is because it is being called in the main thread. If your code were properly created and set up to start and create GUI components in the Swing event thread, this would and should fail since it would put the Swing event thread to sleep. Don't do this, don't call Thread.sleep in a method that has any risk of being called in the Swing event thread.
Instead use a Swing Timer to manage your delay.
Don't forget to (almost) always call the super.paintComponent(g) method within your oeverride. To not do this breaks the Swing painting chain and risks significant hard to debug side effects.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class ElevatorTest {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static void createAndShowGui() {
MyElevator mainPanel = new MyElevator(PREF_W, PREF_H);
JFrame frame = new JFrame("Elevator Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
// start everything on the Swing event thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class MyElevator extends JPanel {
private static final Color BACKGROUND = Color.white;
private static final int ELEVATOR_X = 200;
private static final int ELEVATOR_W = 40;
private static final int ELEVATOR_H = 60;
private static final int TIMER_DELAY = 50;
public static final int MAX_ELEVATOR_Y = 130;
private static final Color ELEVATOR_COLOR = Color.blue;
private int prefW;
private int prefH;
private int elevatorY = 0;
public MyElevator(int prefW, int prefH) {
this.prefW = prefW;
this.prefH = prefH;
setBackground(BACKGROUND);
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
// Don't forget to call the super method
super.paintComponent(g);
g.setColor(ELEVATOR_COLOR);
g.fillRect(ELEVATOR_X, elevatorY, ELEVATOR_W, ELEVATOR_H);
}
// to help size our GUI properly
#Override
public Dimension getPreferredSize() {
Dimension superSz = super.getPreferredSize();
if (isPreferredSizeSet()) {
return superSz;
}
int w = Math.max(superSz.width, prefW);
int h = Math.max(superSz.height, prefH);
return new Dimension(w, h);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (elevatorY >= MAX_ELEVATOR_Y) {
// if elevator at max, stop thimer
((Timer) e.getSource()).stop();
} else {
// advance elevator and draw it
elevatorY++;
repaint();
}
}
}
}
Your ElevatorOne class extends GUI. Therefore it inherits all of the functionality of GUI, yet you create both of them. This is probably what you intended to do:
edit: I ran this code and indeed there is a box moving as you specify.
public class Execute {
public static void main (String[ ] args) {
// GUI gui = new GUI();
// gui.createFrame(800,600);
ElevatorOne e1 = new ElevatorOne();
e1.createFrame(800, 600);
e1.addElevatorOne();
}
}
I am making a game by java and it refreshes itself 60 times per second. Every time it executes a loop and I use g2d to draw images and strings. Things work fine if I do g2d.setFont(new Font("Arial", Font.PLAIN, 8)); and drawstring and it would be normal, but if I set the font to some "unfamiliar" fonts and do the same thing, the swing would show white screen in the first second of start up then paint everything correctly and it's apparently too slow (2 secs).
I put a jpanel in a jframe and override the paint() method of jpanel to draw everything I need. I've already used SwingUtilities.invokeLater in my code.
import javax.swing.*;
import java.awt.*;
public class Window extends JFrame{
public Window(){
add(new Board());
setSize(800,600);
setVisible(true);
}
public static void main(String[] args){
new Window();
}
private class Board extends JPanel {
Font font = new Font("Bitmap", Font.PLAIN, 64);
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setFont(font);
g2d.drawString("This is slow", 220,200);
Toolkit.getDefaultToolkit().sync();
g2d.dispose();
g.dispose();
}
}
}
This is not in a loop but it's very laggy.
http://fontsov.com/download-fonts/bitmap1159.html
This is the cutie font that slows our application down. "Arial" will load blazingly fast. How can I make this less laggy?
First and foremost, for best help, create and post your minimal code example program for us to review, test, and possibly fix. Without this, it will be hard for us to fully understand your problem.
Consider:
Overriding paintComponent not paint to get the advantage of double buffering.
Avoid using invokeLater unless you're sure that the code is being called off of the Swing event thread and you are making calls that need to be on the event thread.
Put slow running code in a background thread such as that which can be found using a SwingWorker.
Putting your text in a JLabel, not drawn on a component.
Draw all static images to a BufferedImage, and displaying that in paintComponent. Then draw all changing images, such as your moving sprites, directly in the paintComponent method.
Don't forget to call your super.paintCompmonent(g) within your paintComponent(Graphics g) method override.
Edit
A BufferedImage solution could look like,....
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class FooFun {
private static void createAndShowGui() {
ChildClass mainPanel = new ChildClass();
JFrame frame = new JFrame("FooFun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
abstract class FirstClass extends JPanel {
private static final int FPS = 20;
public FirstClass() {
new Timer(1000 / FPS, taskPerformer).start();
}
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent e) {
gameLoop(); //do loop here
repaint();
}
};
private void gameLoop() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
paintGame(g2d);
// Toolkit.getDefaultToolkit().sync();
// g2d.dispose();
// g.dispose();
}
public abstract void paintGame(Graphics2D g2d);
}
class ChildClass extends FirstClass {
private static final Font font = new Font("Bitmap", Font.PLAIN, 64);
private static final int PREF_W = 900;
private static final int PREF_H = 600;
private static final String NIGHT_IN_VEGAS_TEXT = "a Night in Vegas";
private static final int NIV_X = 240;
private static final int NIV_Y = 130;
private BufferedImage mainImage;
public ChildClass() {
mainImage = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = mainImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setFont(font);
g2.setColor(Color.black);
g2.drawString(NIGHT_IN_VEGAS_TEXT, NIV_X, NIV_Y);
g2.dispose();
}
#Override
public void paintGame(Graphics2D g2d) {
if (mainImage != null) {
g2d.drawImage(mainImage, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Edit 2
Or with a SwingWorker background thread....
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class FooFun {
private static void createAndShowGui() {
ChildClass mainPanel = new ChildClass();
JFrame frame = new JFrame("FooFun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
abstract class FirstClass extends JPanel {
private static final int FPS = 20;
public FirstClass() {
new Timer(1000 / FPS, taskPerformer).start();
}
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent e) {
gameLoop(); // do loop here
repaint();
}
};
private void gameLoop() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
paintGame(g2d);
}
public abstract void paintGame(Graphics2D g2d);
}
class ChildClass extends FirstClass {
private static final Font font = new Font("Bitmap", Font.PLAIN, 64);
private static final int PREF_W = 900;
private static final int PREF_H = 600;
private static final String NIGHT_IN_VEGAS_TEXT = "a Night in Vegas";
private static final int NIV_X = 240;
private static final int NIV_Y = 130;
private BufferedImage mainImage;
public ChildClass() {
imgWorker.addPropertyChangeListener(new ImgWorkerListener());
imgWorker.execute();
}
private class ImgWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
try {
mainImage = imgWorker.get();
// repaint() here if you don't have a game loop running
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
SwingWorker<BufferedImage, Void> imgWorker = new SwingWorker<BufferedImage, Void>() {
#Override
protected BufferedImage doInBackground() throws Exception {
BufferedImage img = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2.setFont(font);
g2.setColor(Color.black);
g2.drawString(NIGHT_IN_VEGAS_TEXT, NIV_X, NIV_Y);
g2.dispose();
return img;
}
};
#Override
public void paintGame(Graphics2D g2d) {
if (mainImage != null) {
g2d.drawImage(mainImage, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
it's a bit uneconomic to create a new Font each time paint() is called (which happens a lot), you could move that to your constructor.
and the font should be changed to some orthodox fonts (Arial,Calibri etc)
I'm trying to render a dynamically changing image on a JPanel. I have tried many things and I simply do not get how to do it or what I'm doing wrong and I'm at the point of going insane. I'm new to swing and this is a small but essential part of my program which enables me to see what I'm doing.
The simple requirement is the picture rendered initially in the JPanel in the code mentioned below should update at each tick with random colors. I cannot get the image to update. Instead I get another square appearing in the middle of the image I'm trying to update and that small square dynamically changes. I do not know what I'm doing wrong. Please let me know how to rectify the code below so I could get the whole image to update.
Thank you.
The code is as follows
(MainTest.java)
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
public class MainTest extends JFrame {
static ImageEditor img ;
JPanel panel;
Timer to;
public MainTest()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(265, 329);
getContentPane().setLayout(null);
img = new ImageEditor();
panel = new ImageEditor();
panel.setBounds(10, 11, 152, 151);
panel.add(img);
getContentPane().add(panel);
JButton btnIterate = new JButton("Iterate");
btnIterate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ThirdClass t = new ThirdClass(img);
to = new Timer(10,t);
to.start();
}
});
btnIterate.setBounds(10, 230, 89, 23);
getContentPane().add(btnIterate);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
MainTest frame = new MainTest();
//frame.getContentPane().add(imgx);
frame.setVisible(true);
}
});
}
}
(ImageEditor.java)
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
#SuppressWarnings("serial")
public class ImageEditor extends JPanel {
private static BufferedImage img = new BufferedImage(100, 100, 1);
public ImageEditor() {
render();
}
public void paintComponent(Graphics g) {
if (img == null)
super.paintComponents(g);
else
g.drawImage(img, 0, 0, this);
}
public void render() {
float cellWidth = 10;
float cellHeight = 10;
int imgW = img.getWidth();
int imgH = img.getHeight();
float r, g, b;
Graphics2D g2 = img.createGraphics();
g2.setBackground(Color.black);
g2.clearRect(0,0,imgW,imgH);
for (int x=0; x<100; x++) {
for (int y=0; y<100; y++) {
r = (float)Math.random();
g = (float)Math.random();
b = (float)Math.random();
g2.setColor(new Color(r,g,b));
g2.fillRect((int)(x*cellWidth), (int)(y*cellHeight),
(int)cellWidth+1, (int)cellHeight+1);
}
}
g2.setColor(Color.black);
g2.dispose();
repaint();
}
public BufferedImage getImage() {
if (img == null)
img = (BufferedImage)createImage(100, 100);
return img;
}
public void setImage(BufferedImage bimg) {
img = bimg;
}
}
(ThirdClass.java)
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ThirdClass implements ActionListener {
static ImageEditor mock;
public ThirdClass(ImageEditor img)
{
mock = img;
Train();
}
public void Train()
{
mock.render();
}
#Override
public void actionPerformed(ActionEvent arg0) {
Train();
}
}
From the code:
img = new ImageEditor();
panel = new ImageEditor();
panel.setBounds(10, 11, 152, 151);
panel.add(img);
getContentPane().add(panel);
You are defining img as an ImageEditor, but you probably meant for it to be a BufferedImage. You are then adding it to panel, which is another ImageEditor - but it is being added through the superclass method JPanel.add, when you probably meant to use the method you wrote, ImageEditor.setImage().
EDIT: To summarize:
The tiny block of updating pixels you described is the BufferedImage (img.img), inside of your ImageEditor img, which is in turn inside of the ImageEditor panel, which is itself inside the applet's content pane.
replace static ImageEditor img in the MainTest class with BufferedImage img
replace img = new ImageEditor in MainTest's no-args constructor with img = new BufferedImage()
replace panel.add(img) in MainTest's no-args constructor with panel.setImage(img)
replace static ImageEditor mock in the ThirdClass class with BufferedImage mock
replace the ImageEditor img argument in ThirdClass's constructor with BufferedImage img
replace any other stuff involving img to correctly handle it as a BufferedImage
One other thing, although this problem isn't itself the cause of any program malfunction, you should rename the Train() method in ThirdClass to something like train() so it matches proper java style.
After my initial post of the above question I have gone back to the basics and managed to answer my own question.
I am inserting the complete code below so another user having the same problem would not have to go through the same process over again.
My mistake was very trivial. However for people starting swing I think this might be helpful
(MainTest.java)
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.Timer;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class MainTest extends JFrame {
ImageEditor img;
JPanel panel;
Timer t;
public MainTest()
{
setSize(600,600);
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setLayout(null);
img = new ImageEditor();
panel = img;
panel.setBounds(0, 0, 510, 510);
getContentPane().add(panel);
JButton btnStart = new JButton("Start");
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
test();
}
});
btnStart.setBounds(0, 528, 89, 23);
getContentPane().add(btnStart);
}
private void test() {
Train tx = new Train(img);
t = new Timer(100, tx);
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
MainTest frame = new MainTest();
frame.setVisible(true);
}
});
}
}
(ImageEditor.java)
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
#SuppressWarnings("serial")
public class ImageEditor extends JPanel {
private static BufferedImage img = new BufferedImage(500, 500, 1);
public ImageEditor() {
setLayout(null);
}
public void paintComponent(Graphics g) {
if (img == null)
super.paintComponents(g);
else
g.drawImage(img, 0, 0, this);
}
public void render() {
float cellWidth = 10;
float cellHeight = 10;
int imgW = img.getWidth();
int imgH = img.getHeight();
float r, g, b;
Graphics2D g2 = img.createGraphics();
g2.setBackground(Color.black);
g2.clearRect(0,0,imgW,imgH);
for (int x=0; x<100; x++) {
for (int y=0; y<100; y++) {
r = (float)Math.random();
g = (float)Math.random();
b = (float)Math.random();
g2.setColor(new Color(r,g,b));
g2.fillRect((int)(x*cellWidth), (int)(y*cellHeight),
(int)cellWidth+1, (int)cellHeight+1);
}
}
g2.setColor(Color.black);
g2.dispose();
repaint();
}
public BufferedImage getImage() {
if (img == null)
img = (BufferedImage)createImage(500, 500);
return img;
}
public void setImage(BufferedImage bimg) {
img = bimg;
}
}
(Train.java this class was named ThirdClass.java in my question)
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Train implements ActionListener {
ImageEditor temp;
Train(ImageEditor img)
{
temp = img;
}
public void train()
{
temp.render();
}
#Override
public void actionPerformed(ActionEvent e) {
train();
}
}
Thank you for all who commented and answered my question.
I'm trying to draw two lines in a Canvas in Java, calling two methods separately, but when I draw the second line, the first one disapears (Java clears the screen). How can I avoid that? I want to see the two lines. I've seen paint tutorials (how to make a program like the Paint on Windows) where the user uses the mouse to draw lines and when one line is drawn, the other do not disappear. They just call the paint method and it does not clear the screen.
I'll be grateful if anyone can help me.
Thanks.
View Class
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class CircuitTracePlotView extends JFrame {
private CircuitTracePlot circuitTracePlot;
public CircuitTracePlotView() {
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.getContentPane().add(circuitTracePlot = new CircuitTracePlot(), BorderLayout.CENTER);
this.pack();
this.setSize(250,250);
this.setLocationRelativeTo(null);
this.setVisible(true);
circuitTracePlot.drawLine();
circuitTracePlot.drawOval();
}
}
class CircuitTracePlot extends Canvas {
private final static short LINE = 1;
private final static short OVAL = 2;
private int paintType;
private int x1;
private int y1;
private int x2;
private int y2;
public CircuitTracePlot() {
this.setSize(250,250);
this.setBackground(Color.WHITE);
}
private void setPaintType(int paintType) {
this.paintType = paintType;
}
private int getPaintType() {
return this.paintType;
}
public void drawLine() {
this.setPaintType(LINE);
this.paint(this.getGraphics());
}
public void drawOval() {
this.setPaintType(OVAL);
this.paint(this.getGraphics());
}
public void repaint() {
this.update(this.getGraphics());
}
public void update(Graphics g) {
this.paint(g);
}
public void paint(Graphics g) {
switch (paintType) {
case LINE:
this.getGraphics().drawLine(10, 10, 30, 30);
case OVAL:
this.getGraphics().drawLine(10, 20, 30, 30);
}
}
}
Main class
import javax.swing.SwingUtilities;
import view.CircuitTracePlotView;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
CircuitTracePlotView cr = new CircuitTracePlotView();
}
});
}
}
You almost never should call paint(...) directly. I can count the times that I've needed to do this on one hand.
Do not get a Graphics object by calling getGraphics() on a component as that will return a non-durable Graphics object. Instead either draw in a BufferedImage and display that in the paint method or draw in the paint method (if AWT).
Since this is a Swing GUI, don't use an AWT component to draw in. Use a JPanel and override the paintComponent(...) method, not the paint(...) method. Otherwise you lose all benefits of Swing graphics including automatic double buffering.
The super.paintComponent(g) method should be called in the paintComponent(Graphics g) override, often as the first method call inside of this method. This lets the component do its own housekeeping painting, including erasing drawings that need to be erased.
Read the tutorials on Swing graphics as most of this is all well explained there. For e.g., please have a look here:
Lesson: Performing Custom Painting
Painting in AWT and Swing
Edit
To have your images persist, I suggest that you draw to a BufferedImage and then display that Image in your JPanel's paintComponent(...) method.
Or another option is to create a Collection of Shape objects, perhaps an ArrayList<Shape> and fill it with the Shapes you'd like to draw, and then in the paintComponent(...) method cast the Graphics object to a Graphics2D object and iterate through the Shape collection drawing each shape with g2d.draw(shape) as you iterate.
Since Trash posted his code,...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class CircuitTracePlot2 extends JPanel {
private static final int PREF_W = 250;
private static final int PREF_H = PREF_W;
private int drawWidth = 160;
private int drawHeight = drawWidth;
private int drawX = 10;
private int drawY = 10;
private PaintType paintType = PaintType.LINE;
public CircuitTracePlot2() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void setPaintType(PaintType paintType) {
this.paintType = paintType;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (paintType == null) {
return;
}
switch (paintType) {
case LINE:
g.drawLine(drawX, drawY, drawWidth, drawHeight);
break;
case OVAL:
g.drawOval(drawX, drawY, drawWidth, drawHeight);
break;
case SQUARE:
g.drawRect(drawX, drawY, drawWidth, drawHeight);
default:
break;
}
}
private static void createAndShowGui() {
final CircuitTracePlot2 circuitTracePlot = new CircuitTracePlot2();
JFrame frame = new JFrame("CircuitTracePlot2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(circuitTracePlot);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
int timerDelay = 2 * 1000;
new Timer(timerDelay , new ActionListener() {
private int paintTypeIndex = 0;
#Override
public void actionPerformed(ActionEvent arg0) {
paintTypeIndex++;
paintTypeIndex %= PaintType.values().length;
circuitTracePlot.setPaintType(PaintType.values()[paintTypeIndex]);
}
}).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
enum PaintType {
LINE, OVAL, SQUARE;
}
Here's a variation on your program that implements much of #Hovercraft's helpful advice. Try commenting out the call to setPaintType() to see the effect.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** #see http://stackoverflow.com/a/15854246/230513 */
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
CircuitTracePlotView cr = new CircuitTracePlotView();
}
});
}
private static class CircuitTracePlotView extends JFrame {
private CircuitTracePlot plot = new CircuitTracePlot();
public CircuitTracePlotView() {
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
plot.setPaintType(CircuitTracePlot.OVAL);
this.add(plot, BorderLayout.CENTER);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
private static class CircuitTracePlot extends JPanel {
public final static short LINE = 1;
public final static short OVAL = 2;
private int paintType;
public CircuitTracePlot() {
this.setBackground(Color.WHITE);
}
public void setPaintType(int paintType) {
this.paintType = paintType;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
switch (paintType) {
case LINE:
g.drawLine(10, 10, 30, 30);
case OVAL:
g.drawOval(10, 20, 30, 30);
default:
g.drawString("Huh?", 5, 16);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
}