Hi Everyone I want to draw lines to Jlabel Icon with DrawLines() class but the program isn't drawing .When I use to frame.add(m) the program is drawing lines to frame .....
DrawLine m = new DrawLine();
frame.add(m);
but when I use to label.add(m) method .The program isn't working .I need to figure out this problem Why can't I draw lines to jlabelIcon and How can I fix this ?
This is my DrawLine Class
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.Line2D;
public class DrawLine extends JComponent {
public void paint(Graphics g) {
super.paintComponents(g);
g.drawLine(300, 152, 63, 185);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
g.drawLine(63, 185, 120, 198);
}
}
This is my main class
package com.company;
import com.sun.source.tree.Tree;
import jdk.swing.interop.SwingInterOpUtils;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.awt.geom.Line2D;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
public class Main extends JFrame {
public static void main(String[] args) {
JFrame frame = new JFrame("Display Image");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = (JPanel) frame.getContentPane();
frame.setSize(1000,560);
JLabel label = new JLabel();
label.setSize(1000,560);
label.setIcon(new ImageIcon("myimage path"));
DrawLine m = new DrawLine();
label.add(m);
panel.add(label);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
If your goal is to draw lines and images together, then your best bet for the money is to get rid of ImageIcon and JLabel and instead is to draw them all within a single paintComponent. The images can be drawn as image sprite, and the lines as lines by calling Graphics#drawLine(...) or as Line2D objects as you have using Graphics2D#draw(...)
For example, say we had two BufferedImage objects, upImg and dnImg, and two Point objects that determined the location of these sprites, upPt and dnPt
public class Foo01 extends JPanel {
// .....
private Point upPt = new Point(300, 100);
private Point dnPt = new Point(700, 650);
private BufferedImage upImg, dnImg;
And say we wanted to draw a line that connected the two image sprites, then these could all be draw within the paintComponent method like so:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // tell the JPanel to do its house-keeping painting
// make sure that neither image is null
if (upImg != null && dnImg != null) {
// draw both images at their respective locations
g.drawImage(upImg, upPt.x, upPt.y, this);
g.drawImage(dnImg, dnPt.x, dnPt.y, this);
// to get a smooth line, use rendering hiints
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// to give the line some thickness
g2.setStroke(new BasicStroke(5f));
// calculate the end-points of the line
int x1 = upPt.x + upImg.getWidth() / 2;
int y1 = upPt.y + upImg.getHeight() / 2;
int x2 = dnPt.x + dnImg.getWidth() / 2;
int y2 = dnPt.y + dnImg.getHeight() / 2;
// and then draw it
g.drawLine(x1, y1, x2, y2);
}
}
Here is an example program that does just this -- draws two images with a line connecting. I've also added a MouseAdapter to allow the user to move the first image, the green up-arrow, showing that the line will move as well, since it is calculated within the painting method:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.URL;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Foo01 extends JPanel {
private static final String UP_IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/7/7d/Green_circle_icon.jpg";
private static final String DN_IMG_PATH = "https://upload.wikimedia.org/wikipedia/commons/b/bc/Red_circle_icon.jpg";
private static final int GUI_W = 1000;
private static final int GUI_H = 800;
private Point upPt = new Point(300, 100);
private Point dnPt = new Point(700, 650);
private BufferedImage upImg, dnImg;
public Foo01() {
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
setBackground(Color.WHITE);
try {
URL url = new URL(UP_IMG_PATH);
upImg = ImageIO.read(url);
url = new URL(DN_IMG_PATH);
dnImg = ImageIO.read(url);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(GUI_W, GUI_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (upImg != null && dnImg != null) {
g.drawImage(upImg, upPt.x, upPt.y, this);
g.drawImage(dnImg, dnPt.x, dnPt.y, this);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(5f));
int x1 = upPt.x + upImg.getWidth() / 2;
int y1 = upPt.y + upImg.getHeight() / 2;
int x2 = dnPt.x + dnImg.getWidth() / 2;
int y2 = dnPt.y + dnImg.getHeight() / 2;
g.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
private Point p1 = null;
#Override
public void mousePressed(MouseEvent e) {
if (e.getX() < upPt.x || e.getX() > upPt.x + upImg.getWidth()) {
return;
}
if (e.getY() < upPt.y || e.getY() > upPt.y + upImg.getHeight()) {
return;
}
p1 = new Point(e.getX(), e.getY());
}
#Override
public void mouseReleased(MouseEvent e) {
if (p1 != null) {
moveSprite(e);
p1 = null;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p1 != null) {
moveSprite(e);
}
}
private void moveSprite(MouseEvent e) {
Point p2 = new Point(e.getX(), e.getY());
int x = upPt.x + p2.x - p1.x;
int y = upPt.y + p2.y - p1.y;
upPt = new Point(x, y);
p1 = p2;
repaint();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> {
Foo01 foo01 = new Foo01();
JFrame frame = new JFrame("Draw Sprites");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(foo01);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
If the need is to add custom painting to a JLabel you can override its paintComponent:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
public class Main extends JFrame {
private static final String BUG = "https://www.growtopiagame.com/forums/attachment.php?attachmentid=141847&d=1477126665";
public static void main(String[] args) throws MalformedURLException {
JFrame frame = new JFrame("Display Image");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawLine m = new DrawLine("A crossed bug", new ImageIcon(new URL(BUG)));
frame.add(m);
frame.pack();
frame.setVisible(true);
}
}
class DrawLine extends JLabel {
DrawLine(String text, Icon icon) {
super(text, icon, SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.BOTTOM);
setHorizontalTextPosition(SwingConstants.CENTER);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(10));
int w = getWidth(); int h = getHeight();
g2d.drawLine(0, 0, w, h); //draw right to left diagonal
g2d.drawLine(0, h, w, 0); //draw left to right diagonal
}
}
Swing is a single Thread library. All painting tasks are executed in the Event Dispatcher Thread (EDT).
Running long processes (such as sleep) on the EDT makes keeps this thread busy, so it does not do other things like updating the gui. The gui becomes unresponsive (freezes).
If you want to add a line after a certain delay, use swing Timer for the job:
public class Main extends JFrame {
private static final String BUG = "https://www.growtopiagame.com/forums/attachment.php?attachmentid=141847&d=1477126665";
public static void main(String[] args) throws MalformedURLException {
JFrame frame = new JFrame("Display Image");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawLine m = new DrawLine("A crossed bug", new ImageIcon(new URL(BUG)));
frame.add(m);
frame.pack();
frame.setVisible(true);
}
}
class DrawLine extends JLabel {
private static final int DELAY = 1500; //millies
private boolean isDrawSecondDialgonal = false;
DrawLine(String text, Icon icon) {
super(text, icon, SwingConstants.CENTER);
setVerticalTextPosition(SwingConstants.BOTTOM);
setHorizontalTextPosition(SwingConstants.CENTER);
//use timer to enable painting of a second diagonal
javax.swing.Timer timer = new javax.swing.Timer(DELAY, e-> {
isDrawSecondDialgonal = true;
repaint();
});
timer.setRepeats(false);
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(10));
int w = getWidth(); int h = getHeight();
g2d.drawLine(0, 0, w, h); //draw right to left diagonal
if(isDrawSecondDialgonal) {
g2d.drawLine(0, h, w, 0);//draw left to right diagonal
}
}
}
Related
Does anyone know how to draw solid lines when the mouse is rapidly moved?
When I move the mouse slowly the line is drawn solid, but when the mouse is moved quickly the line is drawn like a dotted line, as shown here.
The code to draw the lines is currently this:
private final class MouseL extends MouseAdapter implements MouseMotionListener
{
#Override
public void mouseClicked(MouseEvent e)
{
Point p = e.getPoint();
int half = brushDiameter / 1200;
Graphics2D g = getImage().createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setPaint(getColor());
g.fillOval(p.x - half, p.y - half, brushDiameter, brushDiameter);
g.dispose();
repaint(p.x - half, p.y - half, brushDiameter, brushDiameter);
}
#Override
public void mouseDragged(MouseEvent e)
{
mouseClicked(e);
}
But i would like to change it so that the line appears solid. Any help to achieve this is greatly appreciated thanks.
Simple: If you want to draw curves, don't draw individual points or ovals. Instead connect adjacent points with a line using g.drawLine(...). If you want a thicker curve, change the Graphics2D's Stroke width.
For example:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawCurve extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
private static final Color CURRENT_PTS_COLOR = Color.BLUE.brighter().brighter();
public static final Color IMG_COLOR = Color.RED;
public static final Stroke IMG_STROKE = new BasicStroke(4f);
private static final Color FILL_COLOR = Color.WHITE;
private BufferedImage img = null;
private List<Point> currentPts = new ArrayList<>();
public DrawCurve() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setBackground(FILL_COLOR);
g2.clearRect(0, 0, PREF_W, PREF_H);
g2.dispose();
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, null);
}
Graphics2D g2 = (Graphics2D) g;
g2.setColor(CURRENT_PTS_COLOR);
for (int i = 1; i < currentPts.size(); i++) {
int x1 = currentPts.get(i - 1).x;
int y1 = currentPts.get(i - 1).y;
int x2 = currentPts.get(i).x;
int y2 = currentPts.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
currentPts = new ArrayList<>();
currentPts.add(e.getPoint());
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
currentPts.add(e.getPoint());
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
currentPts.add(e.getPoint());
Graphics2D g2 = img.createGraphics();
g2.setColor(IMG_COLOR);
g2.setStroke(IMG_STROKE);
for (int i = 1; i < currentPts.size(); i++) {
int x1 = currentPts.get(i - 1).x;
int y1 = currentPts.get(i - 1).y;
int x2 = currentPts.get(i).x;
int y2 = currentPts.get(i).y;
g2.drawLine(x1, y1, x2, y2);
}
g2.dispose();
currentPts.clear();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("DrawCurve");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawCurve());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I have an application where the user draws lines. There is a JButton btnClear which, when the user clicks, must clear the drawings, so that the user can draw anew. I use an ActionListener on btnClear to know when it is clicked. I set a boolean Clear so that the correct IF statement is executed in paintComponent(). However, the boolean Clear keeps a False value in paintComponent() although I set it to True just before repaint(). Why is it so?
Note: I tried setting the frame's background to red just for to test the boolean in paintComponent().
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
public class Clipping extends JPanel implements MouseListener, ActionListener
{
static JFrame frame;
static JComboBox cboDraw;
static JButton btnClear;
static JButton btnClip;
double x1, y1, x2, y2;
boolean FirstPoint;
boolean Clear = false;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
CreateFrame();
}
});
}
private static void CreateFrame()
{
frame = new JFrame("Clipping");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Clipping());
frame.setSize(500,500);
frame.setVisible(true);
}
public Clipping()
{
setLayout(new BorderLayout());
JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
PopulateToolBar(toolbar);
add(toolbar, BorderLayout.WEST);
addMouseListener(this);
cboDraw.addMouseListener(this);
btnClip.addActionListener(this);
btnClear.addActionListener(this);
}
private static void PopulateToolBar(JToolBar toolbar)
{
String[] cboList = {"Line", "Polygon"};
cboDraw = new JComboBox(cboList);
cboDraw.setMaximumSize(new Dimension(70,30));
btnClip = new JButton("Set clip area");
btnClear = new JButton("Clear");
toolbar.add(cboDraw);
toolbar.addSeparator();
toolbar.add(btnClip);
toolbar.addSeparator();
toolbar.add(btnClear);
cboDraw.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClip.setAlignmentX(Component.CENTER_ALIGNMENT);
btnClear.setAlignmentX(Component.CENTER_ALIGNMENT);
toolbar.setMargin(new Insets(10,10,10,10));
toolbar.setFloatable(false);
toolbar.setBackground(Color.black);
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
if (cboDraw.getSelectedIndex() == 0) //draw line
{
g2.draw(new Line2D.Double(x1, y1, x2, y2));
}
else if (Clear == true)
{
frame.setBackground(Color.red); //ONLY FOR TESTING PURPOSE
}
}
public void mousePressed(MouseEvent e)
{
if (e.getSource() != cboDraw) //to prevent line coordinates from being saved when selecting from combobox
{
if (cboDraw.getSelectedIndex() == 0) //user wants to draw line
{
if (FirstPoint == false) //first coordinates
{
x1 = e.getX();
y1 = e.getY();
FirstPoint = true;
}
else //second coordinates
{
x2 = e.getX();
y2 = e.getY();
repaint();
FirstPoint = false;
}
}
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == btnClear)
{
Clear = true;
repaint();
Clear = false;
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
You should just do setBackground(Color.red); to call it on your JPanel instead of the JFrame
I think your actionPerformed and mousePressed are both executed at the same time. In mousePressed your if condition inside mousePressed is satisfied also if btnClear() is the source. So repaint method is called anyway, and you see no changes.
You're not calling the super.paintComponent(g) method within your paintComponent method override. Call this first, else, clear won't work.
Note that in your current code, this will not work for you since you're painting incorrectly in that you're not specifying, iterating through and drawing all the lines that need to be drawn within your paintComponent method. The way to solve this is via one of two ways:
Either create a List<Line2D> such as an ArrayList<Line2D>, fill it in your MouseListener/MouseMotionListener code, and then iterate through this List in your paintComponent method, drawing each line. If you do this, then your clear button's action would be to simply clear the List via clear() and call repaint(). No need for a boolean.
Or you could draw your lines onto a BufferedImage, and then draw the BufferedImage in your paintComponent method via the g.drawImage(...) method. If you do this, always check that the image is not null before drawing. Then in your mouse listening code, you'd draw to this image. In your clear button action, you'd create a new BufferedImage, or clear the current BufferedImage.
Also get rid of setBackground(...) calls from within paintComponent as they really shouldn't be in there.
For example of use of an ArrayList of lines:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawPanelViaArrayList extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color LINES_COLOR = Color.black;
private static final Color DRAW_LINE_COLOR = Color.pink;
private static final Stroke STROKE = new BasicStroke(3f);
private List<Line2D> lineList = new ArrayList<>();
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
public DrawPanelViaArrayList() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
add(new JButton(new ClearAction("Clear")));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// first draw the temporary line to show the user
// where he's drawing
if (x1 != x2 && y1 != y2) {
g2.setColor(DRAW_LINE_COLOR);
g2.drawLine(x1, y1, x2, y2);
}
// then draw all the lines held in the linesList.
Stroke oldStroke = g2.getStroke();
g2.setColor(LINES_COLOR);
g2.setStroke(STROKE); // draw thicker lines
for (Line2D line2d : lineList) {
g2.draw(line2d);
}
g2.setStroke(oldStroke); // reset stroke
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getPoint().x;
y1 = e.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
Line2D line = new Line2D.Double(x1, y1, x2, y2);
// add line to ArrayList
lineList.add(line);
x1 = x2 = y1 = y2 = 0;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
// draw temporary line
repaint();
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
lineList.clear();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawPanelViaArrayList());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
And what the heck, a BufferedImage version
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawPanelViaBufferedImage extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color LINES_COLOR = Color.black;
private static final Color DRAW_LINE_COLOR = Color.pink;
public static final Color CLEAR_COLOR = new Color(0, 0, 0, 0);
public static final Stroke STROKE = new BasicStroke(3f);
private int x1 = 0;
private int y1 = 0;
private int x2 = 0;
private int y2 = 0;
private BufferedImage img = new BufferedImage(PREF_W, PREF_W, BufferedImage.TYPE_INT_ARGB);
public DrawPanelViaBufferedImage() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
add(new JButton(new ClearAction("Clear")));
// if the GUI is to be re-sizeable, then consider adding a
// ComponentListener here, and resizing the BufferedImage in it
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// for smooth graphics
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// first draw the temporary line to show the user
// where he's drawing
if (x1 != x2 && y1 != y2) {
g2.setColor(DRAW_LINE_COLOR);
g2.drawLine(x1, y1, x2, y2);
}
// then draw the BufferedImage if not null
if (img != null) {
g2.drawImage(img, 0, 0, null);
}
}
// size of our GUI
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
x1 = e.getPoint().x;
y1 = e.getPoint().y;
}
#Override
public void mouseReleased(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
Line2D line = new Line2D.Double(x1, y1, x2, y2);
// draw to the BufferedImage
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(STROKE);
g2.setColor(LINES_COLOR);
g2.draw(line);
g2.dispose();
x1 = x2 = y1 = y2 = 0;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
x2 = e.getPoint().x;
y2 = e.getPoint().y;
repaint();
}
}
private class ClearAction extends AbstractAction {
public ClearAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g2 = img.createGraphics();
g2.setBackground(CLEAR_COLOR);
g2.clearRect(0, 0, getWidth(), getHeight());
g2.dispose();
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawPanelViaBufferedImage());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class DrawIma extends JPanel{
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=0;i<20;i++){
for (int j=0;j<20;j++) {
g.drawImage(BuArr[i*20+j], 20*i, 20*j, 20, 20, null);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
In this part, BuArr are the 400 blocks divided from a BufferedImage, now i want them to be draw one by one, but the method can not draw the blocks separately, how can i do this?
Swing is single thread and not thread safe.
This means that you should not perform any long running or blocking (Thread.sleep) operations within the IU thread (the Event Dispatching Thread). It also means that you can not update, modify or create UI elements outside of the EDT context.
Instead, use a Swing Timer to generate a repeated callback at a specified interval and render the portions of the image to something like a BufferedImage, which you can the paint to the component via its paintComponent method...
See Concurrency in Swing and How to use Swing Timers for more details
Because it was a good time waster
This generates a List of Rectangles which represent the individual blocks I want to paint, I then randomise the List and run the Timer, picking the top most Rectangle off the List and using BufferedImage#getSubImage to draw it from the master to the buffer, which gets painted to the screen...
import java.awt.AlphaComposite;
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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestImage {
public static void main(String[] args) {
new TestImage();
}
public TestImage() {
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 BufferedImage master;
private BufferedImage copy;
private List<Rectangle> blocks;
public TestPane() {
setBackground(Color.BLACK);
try {
master = ImageIO.read(new File("..."));
copy = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = copy.createGraphics();
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, master.getWidth(), master.getHeight());
g2d.dispose();
int blockSize = 40;
int width = master.getWidth();
int height = master.getHeight();
float aspect = Math.min(width, height) / (float) Math.max(width, height);
int blockHeight = blockSize;
blocks = new ArrayList<>(blockSize * 2);
for (int y = 0; y < master.getHeight(); y += blockHeight) {
if (y + blockHeight > master.getHeight()) {
blockHeight = master.getHeight() - y;
}
int blockWidth = blockSize;
for (int x = 0; x < master.getWidth(); x += blockWidth) {
if (x + blockWidth > master.getWidth()) {
blockWidth = master.getWidth() - x;
}
Rectangle block = new Rectangle(x, y, blockWidth, blockHeight);
blocks.add(block);
}
}
Collections.shuffle(blocks);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (blocks.isEmpty()) {
((Timer) e.getSource()).stop();
} else {
Graphics2D g2d = copy.createGraphics();
Rectangle block = blocks.remove(0);
g2d.drawImage(master.getSubimage(block.x, block.y, block.width, block.height), block.x, block.y, TestPane.this);
g2d.dispose();
repaint();
}
}
});
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return master == null ? new Dimension(200, 200) : new Dimension(master.getWidth(), master.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (copy != null) {
int x = (getWidth() - copy.getWidth()) / 2;
int y = (getHeight() - copy.getHeight()) / 2;
g2d.drawImage(copy, x, y, this);
}
g2d.dispose();
}
}
}
I'm trying to make a paint program, and this class is the main area where you drag your mouse to paint. The problem is the clip has to be rectangular, so any other lines within that rectangle of the clip (the clip gets bigger the faster you move) will get covered by the new clip, however the new clip isn't all needed.
My ideas of solutions are:
To somehow set the clip to a line (but I think that clip would have to be set in the repaint method, not the setClip() in paint component)
To save the image currently on the paint component and set it to the backgroud
Possibly set the ocupancy of the clip lower in the areas without the line?
Thank you for looking at it, here is the code (with some parts left out for simpler reading) and if you know a solution I would love to hear it. Thanks!
public class Canvas extends JPanel implements MouseMotionListener, MouseListener{
int sizeX, sizeY;
String title;
int[] backColor = new int[3];
int brushSize=20;
Point currentP = new Point();
Point pastP = new Point();
Point paintP = new Point();
int diffX, diffY;
boolean initialize=true;
boolean initClip=true;
Canvas(){
backColor[0] = newProject.colorA;
backColor[1] = newProject.colorB;
backColor[2] = newProject.colorC;
if(backColor[0]>=255){
backColor[0]=255;
}
if(backColor[1]>=255){
backColor[1]=255;
}
if(backColor[2]>=255){
backColor[2]=255;
}
sizeX = newProject.sizeX;
sizeY = newProject.sizeY;
//System.out.println(sizeX + " " + sizeY);
setSize(sizeX,sizeY);
setBackground(Color.white);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(brushSize));
if(initialize){
g.setColor(new Color(backColor[0], backColor[1], backColor[2]));
g.fillRect(0, 0, sizeX, sizeY);
g.setColor(Color.red);
g.drawRect(0,0,50,50);
System.out.println("Initialize");
}
else{
g2.drawLine(currentP.x, currentP.y, pastP.x,pastP.y);
}
//System.out.println("Paint");
}
#Override
public void mouseDragged(MouseEvent e) {
if(initClip) //if mouse has been released since last dragged
currentP = e.getPoint(); //This causes PastP and CurrentP to be equal
initClip=false; //since pastP is set equal to CurrentP afterward
pastP = currentP;
currentP = e.getPoint();
diffX=Math.abs(currentP.x-pastP.x); //find the differences to find how big of
diffY=Math.abs(currentP.y-pastP.y); //a clip it needs
if(diffX==0){ //if no movement, set it to brush size so the
diffX=brushSize; //clip shows up
}
if(diffY==0){
diffY=brushSize;
}
initialize=false;
if(currentP.x-pastP.x>0){ //figures out which direction it moved
paintP.x=pastP.x; //sets the clip variable to the correct corner
}
else{
paintP.x=currentP.x;
}
if(currentP.y-pastP.y>0){
paintP.y=pastP.y;
}
else{
paintP.y=currentP.y;
}
System.out.println(paintP);
repaint(paintP.x, paintP.y, diffX, diffY); //repaint with point PaintP and the
//difference it moved
}
#Override
public void mouseReleased(MouseEvent arg0) {
initClip=true;
}
I'm not sure why you would bother. Each time paintComponent is called by the paint system, you are expected to repaint the entire component anyway.
Instead, simply paint what you need to paint, then paint the selection on top of it...
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
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 DrawSelection {
public static void main(String[] args) {
new DrawSelection();
}
public DrawSelection() {
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 TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
private Rectangle clipRect;
public TestPane() {
try {
background = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter ma = new MouseAdapter() {
private Point cp;
#Override
public void mousePressed(MouseEvent e) {
cp = e.getPoint();
clipRect = null;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
cp = null;
}
#Override
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
int x = Math.min(p.x, cp.x);
int y = Math.min(p.y, cp.y);
int width = Math.max(p.x, cp.x) - x;
int height = Math.max(p.y, cp.y) - y;
clipRect = new Rectangle(x, y, width, height);
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return background == null ? new Dimension(200, 200) : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g2d.drawImage(background, x, y, this);
}
if (clipRect != null) {
g2d.setColor(UIManager.getColor("List.selectionBackground"));
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(clipRect);
}
g2d.dispose();
}
}
}
If you want to optimise the paint process, why not draw the parts of the image to a backing buffer and simply paint the buffer, then paint the selection or other dynamic parts on top of it within the paintComponent?
I am trying to make a piece of a JPanel transparent, but I cannot quite get it to work. Is it possible to do this?
import java.awt.*;
import javax.swing.*;
public class ClearPanel extends JPanel{
public static void main(String[] args) {
ClearPanel c = new ClearPanel();
c.setPreferredSize(new Dimension(200, 200));
c.setOpaque(false);
JPanel backPanel = new JPanel();
backPanel.setBackground(Color.CYAN);
backPanel.add(c);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(backPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(0, 0, 200, 200);
g.clearRect(45, 45, 50, 50);
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.0f));
g2.fillRect(75, 75, 50, 50);
}
}
The oval should be opaque, but the rectangles I would like to be transparent. By transparent, I mean that I should be able to see the panel behind the ClearPanel.
Going off of MadProgrammer's answer, is there any way to make that gray box draw where it is outside of the area, but remain transparent where it is in the area?
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle fill = new Rectangle(getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
Rectangle hole = new Rectangle(0, 0, 100, 100);
Area area = new Area(fill);
area.subtract(new Area(hole));
g2d.setColor(getBackground());
g2d.fill(area);
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.fill(hole);
g2d.setComposite(AlphaComposite.SrcOver.derive(1.0f));
g2d.setColor(Color.DARK_GRAY);
if(area.contains(0,0,100,200))
g2d.fillRect(0, 0, 100, 200);
g2d.dispose();
}
The problem you have is, by default, JPanel is opaque, meaning that the repaint will NOT paint anything under it.
You need to set the the panel to transparent and then take over the painting of the background.
Now, the real trick begins. If you simply fill the component and then try and paint transparent section over the top of it, you will simply be painting a transparent section over a opaque background...not very helpful.
What you need to do is not fill the area you want to remain transparent.
You can accomplish this by using a Area shape, which has a neat trick of been able to append/add and remove shapes from it.
import java.awt.AlphaComposite;
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.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransparentPane {
public static void main(String[] args) {
new TransparentPane();
}
public TransparentPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
BackgroundPane backgroundPane = new BackgroundPane();
backgroundPane.setBackground(Color.RED);
backgroundPane.setLayout(new BorderLayout());
backgroundPane.add(new TranslucentPane());
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(backgroundPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage bg;
public BackgroundPane() {
try {
bg = ImageIO.read(new File("/path/to/your/image.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return bg == null ? super.getPreferredSize() : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - bg.getWidth()) / 2;
int y = (height - bg.getHeight()) / 2;
g.drawImage(bg, x, y, this);
}
}
}
public class TranslucentPane extends JPanel {
public TranslucentPane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle fill = new Rectangle(getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
int radius = Math.min(width, height) / 2;
int x = (width - radius) / 2;
int y = (height - radius) / 2;
Ellipse2D hole = new Ellipse2D.Float(x, y, radius, radius);
Area area = new Area(fill);
area.subtract(new Area(hole));
g2d.setColor(getBackground());
g2d.fill(area);
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.25f));
g2d.fill(hole);
g2d.dispose();
}
}
}
Update
Well, that took a little longer the I expected...
Basically, we need to create a mask of the shape that subtracts the hole from the rectangle we want to display, then subtract that result from the rectangle we want to diplay
import java.awt.AlphaComposite;
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.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.File;
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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TransparentPane {
public static void main(String[] args) {
new TransparentPane();
}
public TransparentPane() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
BackgroundPane backgroundPane = new BackgroundPane();
backgroundPane.setBackground(Color.RED);
backgroundPane.setLayout(new BorderLayout());
backgroundPane.add(new TranslucentPane());
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(backgroundPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BackgroundPane extends JPanel {
private BufferedImage bg;
public BackgroundPane() {
try {
bg = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/Evil_Small.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return bg == null ? super.getPreferredSize() : new Dimension(bg.getWidth(), bg.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg != null) {
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - bg.getWidth()) / 2;
int y = (height - bg.getHeight()) / 2;
g.drawImage(bg, x, y, this);
}
}
}
public class TranslucentPane extends JPanel {
public TranslucentPane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle fill = new Rectangle(getWidth(), getHeight());
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth() - 1;
int height = getHeight() - 1;
int radius = Math.min(width, height) / 2;
int x = (width - radius) / 2;
int y = (height - radius) / 2;
Ellipse2D hole = new Ellipse2D.Float(x, y, radius, radius);
Area area = new Area(fill);
area.subtract(new Area(hole));
g2d.setColor(getBackground());
g2d.fill(area);
g2d.setColor(Color.RED);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.fill(hole);
g2d.dispose();
g2d = (Graphics2D) g.create();
// Basically, we create an area that is subtraction of the window/rectangle
// from the whole. This leaves us with a rectangle (with a hole in it)
// that doesn't include the area where the whole is...
Rectangle win = new Rectangle(
x + (radius / 2),
y + (radius / 2), radius, (radius / 4));
area = new Area(win);
area.subtract(new Area(hole));
// Then we create a area that is a subtraction of the original rectangle
// from the one with a "hole" in it...
Area actual = new Area(win);
actual.subtract(area);
g2d.setColor(Color.BLUE);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(actual);
g2d.dispose();
}
}
}