I've experienced a problem on the Graphics drawString method. Briefly, I observe unexpected behaviour of drawString on Linux where the string goes messy if the panel size is large.
As an example. I have modified the ScrollDemo2 class from Oracle to draw strings on the drawingPane:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollDemo2 extends JPanel
{
private Dimension area;
private JPanel drawingPane;
public ScrollDemo2() {
super(new BorderLayout());
area = new Dimension(100000,1000);
drawingPane = new DrawingPane();
drawingPane.setBackground(Color.white);
drawingPane.setPreferredSize(area);
drawingPane.revalidate();
drawingPane.repaint();
JScrollPane scroller = new JScrollPane(drawingPane);
scroller.setPreferredSize(new Dimension(500,500));
add(scroller, BorderLayout.CENTER);
}
public class DrawingPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// drawString gives correct results in Windows but gives messy strings in Linux; drawLine gives correct lines in both Windows and Linux
for (int i = 0; i < 100000; i += 100)
g.drawString("Mark " + i, i, 20);
for (int i = 0; i < 100000; i += 100)
g.drawLine(i, i % 1000 / 10 + 25, i, i % 1000 / 10 + 35);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("ScrollDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ScrollDemo2();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Program runs on Windows 7
The program displays correct strings on Windows 7.
Program runs on Linux 14.04
The program displays messy strings on Linux.
A drawLine method is added in addition to the drawString method, and the drawLine method works properly in both Windows and Linux.
Why is it? How should I rewrite the code to solve the problem on Linux?
As noted in comments, if drawing a static (stable unchanging background image) use a BufferedImage. For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollDemo2B extends JPanel
{
private static final int IMG_W = 100000;
private static final int IMG_H = 1000;
// private Dimension area;
private JPanel drawingPane;
private BufferedImage img = new BufferedImage(IMG_W, IMG_H, BufferedImage.TYPE_INT_ARGB);
public ScrollDemo2B() {
super(new BorderLayout());
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.BLACK);
for (int i = 0; i < 100000; i += 100) {
g2.drawString("Mark " + i, i, 20);
}
for (int i = 0; i < 100000; i += 100) {
g2.drawLine(i, i % 1000 / 10 + 25, i, i % 1000 / 10 + 35);
}
g2.dispose();
drawingPane = new DrawingPane();
drawingPane.setBackground(Color.white);
drawingPane.revalidate();
drawingPane.repaint();
JScrollPane scroller = new JScrollPane(drawingPane);
scroller.setPreferredSize(new Dimension(500,500));
add(scroller, BorderLayout.CENTER);
}
public class DrawingPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
int w = img.getWidth();
int h = img.getHeight();
return new Dimension(w, h);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("ScrollDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new ScrollDemo2B();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Then if you have mobile sprites or dynamic images, you would draw them within the paintComponent method.
Related
So I am using a JFrame object to open up a window and add a bunch of graphics first I am adding an image then I am trying to add some lines, But it seems like the line start from the Y center of the previous image I would like for it to start at the top of the page
here is my code for the JFrame:
JFrame f = new JFrame();
JLabel trebeclef = new JLabel(new ImageIcon(getClass().getClassLoader().getResource("Some image")));
Draw d = new Draw();
f.add(trebeclef);
f.add(d);
f.setSize(1000,1000);
f.getContentPane().setBackground(Color.white);
f.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
here is the code for the Draw class
public void paint(Graphics g2) {
super.paintComponent(g2);
Graphics2D g = (Graphics2D) g2;
g.setStroke(new BasicStroke(2));
g.drawLine(0, 0, 100, 0);
}
it results in this
Any help appreciated, thanks
Trying to layout components like this isn't the easiest thing to to, especially when you consider the complexities of JLabel and the possibilities of other layout constraints.
If you have the image and you're drawing the lines via custom painting, I would just custom paint the whole thing
Starting with...
We can produce something like...
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new ClefWithLinesPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class ClefWithLinesPane extends JPanel {
private BufferedImage trebbleClef;
public ClefWithLinesPane() throws IOException {
trebbleClef = ImageIO.read(getClass().getResource("/images/ClefLines.png"));
setBackground(Color.WHITE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = 0;
int y = (getHeight() - trebbleClef.getHeight()) / 2;
g2d.drawImage(trebbleClef, x, y, this);
int[] lines = new int[] {
30, 60, 89, 120, 149
};
x = trebbleClef.getWidth();
g2d.setStroke(new BasicStroke(2));
for (int line = 0; line < lines.length; line++) {
y = lines[line];
g2d.drawLine(x, y, getWidth(), y);
}
g2d.dispose();
}
}
}
But... as you can see, the lines don't "quite" match up, now this is simply an issue with source image and you could spend some time cleaning it up, or, you could just dispense with the lines of the clef itself and do those you're self, for example...
Starting with...
We could produce something like...
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new ClefWithOutLinesPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class ClefWithOutLinesPane extends JPanel {
private BufferedImage trebbleClef;
public ClefWithOutLinesPane() throws IOException {
trebbleClef = ImageIO.read(getClass().getResource("/images/Clef.png"));
setBackground(Color.WHITE);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int minLineY = 30;
int maxLineY = 150;
int lineSpacing = (maxLineY - minLineY) / 4;
int x = 10;
g2d.setStroke(new BasicStroke(8));
g2d.drawLine(x, minLineY + 3, x, maxLineY - 3);
int y = (getHeight() - trebbleClef.getHeight()) / 2;
g2d.drawImage(trebbleClef, x + 10, y, this);
g2d.setStroke(new BasicStroke(2));
for (int line = 0; line < 5; line++) {
y = minLineY + (lineSpacing * line);
g2d.drawLine(x, y, getWidth(), y);
}
g2d.dispose();
}
}
}
Adds after setLayout. One often uses an overloaded add with an extra parameter for a layout's specific constraint.
By default the content pane the frame's adds are done upon, has a BorderLayout with center, left and so on.
The horizontal "line" in a flow is vertically centered. Setting a preferred height ensures a line component is filled up.
d.setPreferredSize(new Dimension(100, 1000));
Of course an other layout might serve better; depending on what you want - just experiment.
I have 2 JPanels inside JFrame border layout and in one JPanel I want to draw things and the other one I use to hold some JComponents(for now only JSlider). The problem is that when I change the value with the slider very weird things happen (the slider moves around and what not). Here is the whole class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Main extends JPanel {
static final int WIDTH = 800, HEIGHT = 800;
static final int pointsMin = 10, pointsMax = 300;
static JSlider sliderPoints;
static ArrayList<Point> points = new ArrayList<Point>();
public static void main(String[] args)
{
JFrame jf = new JFrame("Circle");
Main panel = new Main();
panel.setPreferredSize(new Dimension(800,650));
JPanel panel2 = new JPanel(null);
panel2.setPreferredSize(new Dimension(800,150));
Container pane = jf.getContentPane();
pane.add(panel2, BorderLayout.NORTH);
pane.add(panel,BorderLayout.CENTER);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
jf.requestFocus();
sliderPoints = new JSlider(pointsMin, pointsMax, pointsMin);
sliderPoints.setLocation(64,64);
sliderPoints.setSize(128, 32);
panel2.add(sliderPoints);
sliderPoints.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
panel.recalculate();
}
});
panel.recalculate();
}
private void recalculate()
{
points.clear();
float angle = (float) (6.2856f / sliderPoints.getValue());
for(int i=0; i < sliderPoints.getValue(); i++)
{
int x = (int) (Math.cos(angle * i) * (100));
int y = (int) (Math.sin(angle * i) * (100));
points.add(new Point(x,y));
}
revalidate();
repaint();
}
public void paint(Graphics g)
{
g.setColor(Color.red);
for (int i=0; i < points.size(); i++)
{
g.fillRect(400 + points.get(i).x - 1, HEIGHT/2 + points.get(i).y - 1, 3, 3);
}
g.dispose();
}
}
import javax.swing.*;
import java.awt.*;
public class Test1 {
int x = 70;
int y = 70;
public static void main (String[] args) {
Test1 gui = new Test1 ();
gui.go();
}
public void go() {
JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300,300);
frame.setVisible(true);
for (int i = 0; i < 130; i++) {
x++; y++;
drawPanel.repaint();
try { Thread.sleep(50);
} catch(Exception ex) { } }
}// close go() method
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.green);
g.fillOval(x,y,40,40);
}
} // close inner class
} // close outer class
page1page2
According to the page 2, the circle should be smeared in the frame... but actually, when I ran it, it just moved without smearing. Why was that?
btw, if these codes were not able to make a smearing circle, how could I make a smearing one?
cheers
As shown here, "If you do not honor the opaque property you will likely† see visual artifacts." Indeed, running your example on Mac OS X with Java 6 produces a series of circles that appear "smeared."
How could I make a smearing one?
Do not rely on painting artifacts to get the desired result; instead, render a List<Shape> as shown below.
Use javax.swing.Timer to pace animation.
Construct and manipulate Swing GUI objects only on the event dispatch thread.
Override getPreferredSize() to establish a drawing panel's initial geometry.
Code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test2 {
public static void main(String[] args) {
EventQueue.invokeLater(new Test2()::display);
}
public void display() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final MyDrawPanel drawPanel = new MyDrawPanel();
frame.add(drawPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
private int x = 30;
private int y = 30;
private final List<Shape> list = new ArrayList<>();
public MyDrawPanel() {
new Timer(50, (new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
x++;
y++;
list.add(new Ellipse2D.Double(x, y, 40, 40));
repaint();
}
})).start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.setColor(Color.green);
for (Shape s : list) {
g2d.fill(s);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
}
}
†Emphasis mine.
The intention of my code is to create a rectangle when the button is clicked. The button works fine but the rectangle itself is not showing up on the screen, and there are no errors. Thank you for helping btw.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tester {
static JButton button;
static JFrame frame;
static JPanel panel;
static Rectangle rec;
static void init(){
button = new JButton();
frame = new JFrame();
panel = new JPanel();
rec = new Rectangle(30,30,30,30);
button.setVisible(true);
panel.add(button);
frame.add(panel);
frame.setVisible(true);
panel.setVisible(true);
frame.setSize(200, 200);
button.setBackground(Color.GREEN);
button.setBounds(30, 30, 20, 20);
}
public static void main(String[] args) {
init();
ActionListener listener = new RectangleMover();
button.addActionListener(listener);
}
static class RectangleMover implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
RectanglePainter r = new RectanglePainter();
r.add(rec);
}
}
static class RectanglePainter extends JPanel{
void add(Rectangle r){
rec = r;
repaint();
}
protected void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
Random r = new Random();
int i =r.nextInt(2);
if (i==1)
g2.setColor(Color.BLUE);
else
g2.setColor(Color.RED);
g2.fill(rec);
g2.draw(rec);
}
}
}
Your generally approach is slightly skewed, rather than using another JComponent to "act" as the shape, you should be using it to paint all the shapes through it's paintComponent method
From my "red rectangle" period...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Tester {
public static void main(String[] args) {
new Tester();
}
public Tester() {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel panel;
private JButton button;
private JLabel label;
private ShapePane shapePane;
public TestPane() {
setLayout(new BorderLayout());
button = new JButton("Rectangle");
panel = new JPanel();
label = new JLabel();
button.setVisible(true);
panel.add(button);
panel.add(label);
shapePane = new ShapePane();
add(shapePane);
add(panel, BorderLayout.SOUTH);
class ClickListener implements ActionListener {
private int X = 20;
private int Y = 20;
#Override
public void actionPerformed(ActionEvent arg0) {
int width = shapePane.getWidth();
int height = shapePane.getHeight();
int x = (int)(Math.random() * (width - 20)) + 10;
int y = (int)(Math.random() * (height - 20)) + 10;
int w = (int)(Math.random() * (width - x));
int h = (int)(Math.random() * (height - y));
shapePane.add(new Rectangle(x, y, w, h));
}
}
ActionListener listener = new ClickListener();
button.addActionListener(listener);
}
}
public class ShapePane extends JPanel {
private List<Shape> shapes;
public ShapePane() {
shapes = new ArrayList<>(25);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void add(Rectangle rectangle) {
shapes.add(rectangle);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.RED);
for (Shape shape : shapes) {
g2.draw(shape);
}
}
}
}
As to answer your basic question, you could have tried calling revalidate and repaint, but because of the BorderLayout I doubt it would have worked as you would have basically been trying to replace the panel with your ShapeChanger component, and there are better ways to do that
How can i add repeat background image in Jframe?
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class myGUI extends JFrame {
/**
* Create the frame.
*/
public myGUI() {
super.setResizable(false);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.setUndecorated(true);
super.setExtendedState(JFrame.MAXIMIZED_BOTH);
super.setVisible(true);
}
}
I tried many examples, but not found code that background with a repeat.
Update, that is my GUI and ImagePanel.
The GUI:
import java.awt.BorderLayout;
import java.awt.Toolkit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class GUI extends JFrame {
private JPanel logPanel = new LogPane();
private JPanel logotipPanel = new LogotipPane();
ImagePanel mainPanel = new ImagePanel(Toolkit.getDefaultToolkit().getImage( "C:/Users/Owner/workspace/Blackjack/bin/bg.png" ));
/**
* Create the frame.
*/
public GUI() {
logPanel.setOpaque(false);
logotipPanel.setOpaque(false);
mainPanel.add(logPanel, BorderLayout.EAST);
mainPanel.add(logotipPanel, BorderLayout.NORTH);
add(mainPanel);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setUndecorated(true);
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
}
}
The ImagePanel:
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
#SuppressWarnings("serial")
class ImagePanel extends JPanel {
private Image image;
ImagePanel(Image image) {
this.image = image;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int iw = image.getWidth(this);
int ih = image.getHeight(this);
if (iw > 0 && ih > 0) {
for (int x = 0; x < getWidth(); x += iw) {
for (int y = 0; y < getHeight(); y += ih) {
g.drawImage(image, x, y, iw, ih, this);
}
}
}
}
}
So, the matter with the background with repeat resolved, but in this way i cant use all of the BorderLayouts because i adding panels into ImagePanel and not to JFrame GUI.
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) throws IOException {
final Image image = ImageIO.read(new File("logo.png"));
final JFrame frame = new JFrame();
JPanel imagePanel = new ImagePanel(image);
imagePanel.setLayout(new BorderLayout()); // setting layout as BorderLayout
JPanel anotherPanel = new JPanel(); /// multiple panel,
anotherPanel.setSize(100, 290);
anotherPanel.setOpaque(false); // THIS IS VERY MUCH IMPORTANT
anotherPanel.add(new JButton("Alpesh Gediya"));
imagePanel.add(anotherPanel); //add panel to ImagePanel
frame.add(imagePanel);
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
#SuppressWarnings("serial")
class ImagePanel extends JPanel {
private Image image;
private boolean tile;
ImagePanel(Image image) {
this.image = image;
this.tile = false;
final JCheckBox checkBox = new JCheckBox();
checkBox.setAction(new AbstractAction("Tile") {
public void actionPerformed(ActionEvent e) {
tile = checkBox.isSelected();
repaint();
}
});
add(checkBox, BorderLayout.SOUTH);
};
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (tile) {
int iw = image.getWidth(this);
int ih = image.getHeight(this);
if (iw > 0 && ih > 0) {
for (int x = 0; x < getWidth(); x += iw) {
for (int y = 0; y < getHeight(); y += ih) {
g.drawImage(image, x, y, iw, ih, this);
}
}
}
} else {
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
}
}
EDIT :
imagePanel.setLayout(new BorderLayout()); // setting layout as BorderLayout for panel as
// Jpanel have FlowLayout as Default LayoutManager.