I'm working on a custom menu for a game and a problem occured to me by trying to draw the components.
The idea is, to have either a picture or a video running in the background with a panel on top of it containing some custom menu-items.
The panel which contains the menu-items shall be semi-transparent and thats the problem I guess.
Since it's a bit hard to explain I've build an example wich demonstrates the issue. Redrawing the menu-item on mouseover works without any problems but it appears that the redraw also kinda affects the parts of the panel which should not be drawn since the shape of the actual menu-item doesn't fit the panels rectangle shape. This results in my menu-item having ugly dark edges which should not be there (top-right and bottom-left).
Hope you guys know a solution for that cause I start getting really annoyed. I tried everything I found here or somewhere else in the web, but it appears all the other problems only had to deal with redrawing one shape which does not change.
import java.awt.AlphaComposite;
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.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class TransparentDrawTest extends JPanel implements MouseListener {
private final static Dimension SIZE = new Dimension(400, 50);
private boolean isSelected;
private boolean isClicked;
public static void main(String[] args) {
JFrame testFrame = new JFrame();
testFrame.setSize(500, 100);
testFrame.setResizable(false);
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testFrame.setLocationRelativeTo(null);
testFrame.setBackground(Color.WHITE);
JPanel semiTransPanel = new JPanel();
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.6f));
semiTransPanel.setOpaque(true);
semiTransPanel.add(new TransparentDrawTest());
testFrame.add(semiTransPanel);
testFrame.setVisible(true);
}
public TransparentDrawTest() {
this.addMouseListener(this);
this.setSize(SIZE);
this.setMinimumSize(SIZE);
this.setMaximumSize(SIZE);
this.setPreferredSize(SIZE);
this.setOpaque(false);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
g2d.fillRect(0, 0, 400, 50);
int[] pointsX;
int[] pointsY;
if (this.isSelected) {
pointsX = new int[] { 2, 348, 398, 48 };
pointsY = new int[] { 2, 2, 48, 48 };
}
else {
pointsX = new int[] { 2, 298, 348, 48 };
pointsY = new int[] { 2, 2, 48, 48 };
}
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(5));
g2d.setColor(Color.WHITE);
g2d.drawPolygon(pointsX, pointsY, 4);
if (this.isClicked)
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
else
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g.fillPolygon(pointsX, pointsY, 4);
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
this.isSelected = true;
this.repaint();
}
#Override
public void mouseExited(MouseEvent e) {
this.isSelected = false;
this.repaint();
}
#Override
public void mousePressed(MouseEvent e) {
this.isClicked = true;
this.repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
this.isClicked = false;
this.repaint();
}
}
There are at least two things I can see which will give you trouble...
Firstly, you are changing the alpha composite of the Graphics context, but you are not resting it....
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
The Graphics context is a shared resource, all components being painted in the current paint cycle will be given the same instance of Graphics, meaning that any changes you make to it will be passed to the other components being painted.
Instead, you should create a temporary copy of the Graphics context and use that instead...
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC));
//...
// If you create it, you must dispose of it...
g2d.dispose();
Second of all, you are setting the background color of a opaque component to a semi transparent value...
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f, 0.6f));
The problem is, Swing only treats components and opaque or transparent. By doing this, Swing won't know that it should also update the components below this one. Basically it will think that the component is opaque (because it is) and will not update the components below...
Basically, you will need to do something simular to what you've done with TransparentDrawTest panel, and fake it, for example...
JPanel semiTransPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
};
semiTransPanel.setBackground(new Color(0.0f, 0.0f, 0.0f));
semiTransPanel.setOpaque(false);
I also can't figure out why you are doing this...
g2d.fillRect(0, 0, 400, 50);
In your TransparentDrawTest pane...
You should be calling super.paintComponent(g) first, this is actually what this method does anyway, it clears the Graphics context for painting...amongst other things ;)
Updated with example code...
This is the example code I've been using to test you code with...
Ps: I changed the background color of the semi transparent panel so I can see the difference...
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TransparentDrawTest extends JPanel implements MouseListener {
private final static Dimension SIZE = new Dimension(400, 50);
private boolean isSelected;
private boolean isClicked;
public static void main(String[] args) {
JFrame testFrame = new JFrame();
testFrame.setSize(500, 100);
testFrame.setResizable(false);
testFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testFrame.setLocationRelativeTo(null);
testFrame.setBackground(Color.WHITE);
JPanel semiTransPanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getBackground());
g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
};
semiTransPanel.setOpaque(false);
semiTransPanel.setBackground(Color.RED);
semiTransPanel.add(new TransparentDrawTest());
testFrame.add(semiTransPanel);
testFrame.setVisible(true);
}
public TransparentDrawTest() {
this.addMouseListener(this);
this.setSize(SIZE);
this.setMinimumSize(SIZE);
this.setMaximumSize(SIZE);
this.setPreferredSize(SIZE);
this.setOpaque(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int[] pointsX;
int[] pointsY;
if (this.isSelected) {
pointsX = new int[]{2, 348, 398, 48};
pointsY = new int[]{2, 2, 48, 48};
} else {
pointsX = new int[]{2, 298, 348, 48};
pointsY = new int[]{2, 2, 48, 48};
}
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(5));
g2d.setColor(Color.WHITE);
Composite comp = g2d.getComposite();
if (this.isClicked) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
} else {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
}
g2d.fillPolygon(pointsX, pointsY, 4);
g2d.setComposite(comp);
g2d.drawPolygon(pointsX, pointsY, 4);
g2d.dispose();
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
this.isSelected = true;
this.repaint();
}
#Override
public void mouseExited(MouseEvent e) {
this.isSelected = false;
this.repaint();
}
#Override
public void mousePressed(MouseEvent e) {
this.isClicked = true;
this.repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
this.isClicked = false;
this.repaint();
}
}
See Backgrounds With Transparency for the probable problem and a simple solution:
//testFrame.add(semiTransPanel);
testFrame.add( new AlphaContainer(semiTransPanel) );
Related
I'm writing paint on screen program using Java Swing. It working on ubuntu linux. But windows shows black screen instead of transparent panel. I included similar example code. What is wrong in my code?
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class Example {
public static final Color COLOR_TRANSPARENT = new Color(0,0,0,0);
public Example() {
Canvas drawArea = new Canvas();
drawArea.setBackground(COLOR_TRANSPARENT);
drawArea.setOpaque(true);
JWindow drawingFrame = new JWindow();
drawingFrame.setBackground(COLOR_TRANSPARENT);
drawingFrame.setContentPane(drawArea);
drawingFrame.pack();
drawingFrame.setSize(640, 460);
drawingFrame.setVisible(true);
drawingFrame.setLocationRelativeTo(null);
drawingFrame.setAlwaysOnTop(true);
}
public static void main(String[] args){
SwingUtilities.invokeLater(Example::new);
}
class Canvas extends JPanel{
private Image image;
private Graphics2D g2;
public Canvas() {
super();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
int x = e.getX();
int y = e.getY();
g2.setPaint(Color.RED);
g2.fillOval(x-10, y-10, 20, 20);
repaint(x-10, y-10, 20, 20);
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image == null){
image = createImage(getWidth(), getHeight());
g2 = (Graphics2D) image.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setBackground(COLOR_TRANSPARENT);
clear();
}
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0,0, null);
}
public void clear(){
System.out.println("clearing canvas ");
g2.setComposite(AlphaComposite.Clear);
g2.setBackground(COLOR_TRANSPARENT);
g2.setColor(COLOR_TRANSPARENT);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.clearRect(0, 0, getWidth(), getHeight());
g2.setPaint(Color.RED);
g2.setComposite(AlphaComposite.SrcOver);
repaint();
}
}
}
Here is screenshot what I wanted.
Example code updated. Now code should work without any other additional code.
For windows I made a couple of changes:
image = createImage(getWidth(), getHeight());
image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
I used a BufferedImage to you can set the alpha values of the image to be transparent.
//public static final Color COLOR_TRANSPARENT = new Color(0,0,0,0);
public static final Color COLOR_TRANSPARENT = new Color(0,0,0,1);
I made the alpha value non-zero, because a value of zero means the Java application won't receive the MouseEvent because it is passed to the application under the window.
I want to write a multiline text (3-4 lines is ok) on the bottom right corner of a JDesktopPane, how can I do this?
The text is not fixed, it can change every time i start the swing application but once the application is started it remains the same, I don't need to update from the application.
My first thought was to create an image, put it as background of the JDesktopPane and then write on it, but it doesn't seem a simple solution.
Thanks for the help.
Combining the examples seen here and here, the print() method in the variation below illustrates using FontMetrics to right justify multiple lines of text in the bottom, right corner of a JDesktopPane.
import java.awt.*;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
/** #see http://stackoverflow.com/a/45055215/230513 */
public class JDPTest extends JDesktopPane {
private MyFrame one = new MyFrame("One", 100, 100);
public JDPTest() {
this.setPreferredSize(new Dimension(640, 480));
this.add(one);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.lightGray);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.BLACK);
g2d.setFont(new Font(Font.SERIF, Font.BOLD, 16));
print(g2d, 3, "Hello, world!");
print(g2d, 2, "This is a test.");
print(g2d, 1, "This is another test.");
print(g2d, 0, "This is still another test.");
}
private void print(Graphics2D g2d, int line, String s) {
FontMetrics fm = g2d.getFontMetrics();
int x = this.getWidth() - fm.stringWidth(s) - 5;
int y = this.getHeight() - fm.getDescent()
- line * (fm.getHeight() + fm.getLeading());
g2d.drawString(s, x, y);
}
private final class MyFrame extends JInternalFrame {
MyFrame(String name, int x, int y) {
super(name, true, true, true, true);
this.setSize(320, 240);
this.setLocation(x, y);
this.setVisible(true);
}
}
private void display() {
JFrame f = new JFrame("JDPTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new JDPTest().display();
}
});
}
}
Use this example class.
The background if set will be scaled.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.swing.JDesktopPane;
public class MyDesktopPane extends JDesktopPane {
Image img;
public MyDesktopPane() {
super();
}
#Override
public void paintComponent(Graphics g) {
int width = this.getWidth();
int height = this.getHeight();
int infoWidth = 150;
int infoHeight = 100;
super.paintComponent(g);
// alpha
final Float alpha = new Float(0.9);
final Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(makeComposite(alpha.floatValue()));
// draw bacground image is set
if (img != null) {
g.drawImage(img, 0, 0, width, height, this);
}
//draw 3 line text in red reound rectangle
g.setColor(Color.RED);
g.fillRoundRect(width - infoWidth, height - infoHeight, infoWidth, infoHeight, 5, 5);
g.setColor(Color.BLACK);
g.drawString("Line 1", width - infoWidth + 5, height - infoHeight + 20);
g.drawString("Line 2", width - infoWidth + 5, height - infoHeight + 40);
g.drawString("Line 3", width - infoWidth + 5, height - infoHeight + 60);
}
public void setBackGroundImage(String path) {
try {
boolean file = new File(path).isFile();
if (file) {
img = javax.imageio.ImageIO.read(new FileInputStream(path));
}
} catch (IOException e) {
e.printStackTrace();
}
this.repaint();
}
private AlphaComposite makeComposite(final float alpha) {
final int type = AlphaComposite.SRC_OVER;
return (AlphaComposite.getInstance(type, alpha));
}
}
I have created a JPanel and add it on a JFrame. JPanel has TitledBorder, but when I use the method translate(), e.g.,
g2.translate(getWidth() / 2, getHeight() / 2);
the whole component, including the border is translated. The effect is presented on Figure 1.
The desired result is depicted on the figure below.
Code is pasted below.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
class GPanel extends JPanel {
private void doDrawing(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.translate(getWidth() / 2, getHeight() / 2);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
class Main extends JFrame {
public Main() {
}
public static void main(String[] args) {
Main ex = new Main();
ex.setSize(new Dimension(400, 400));
GPanel panel = new GPanel();
panel.setBorder(BorderFactory.createTitledBorder("Title"));
ex.add(panel);
ex.setLocationRelativeTo(null);
ex.setVisible(true);
}
}
You have invoked setBorder() on an instance of GPanel and then modified the graphics context's transform in the latter's implementation of paintComponent(). The border doesn't know about this, so the result shown is entirely expected. Instead, follow the parent JComponent implementation advice for setBorder(): "put the component in a JPanel and set the border on the JPanel." A related example is shown here.
In the variation below,
The enclosing bPanel now has the Border, and the enclosed gPanel can safely manipulate the graphics context.
Before translate(), the red dot is centered on the origin; after translate(), the blue dot is centered on the origin, but the origin has moved.
Don't use setPreferredSize() when you really mean to override getPreferredSize().
Don't extend JFrame needlessly.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
class GTest {
public static void main(String[] args) {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GPanel gPanel = new GPanel();
JPanel bPanel = new JPanel();
bPanel.setBorder(BorderFactory.createTitledBorder("Title"));
bPanel.add(gPanel);
f.add(bPanel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class GPanel extends JPanel {
private static final int N = 16;
private void doDrawing(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.red);
g.fillOval(-N, -N, 2 * N, 2 * N);
g2.translate(getWidth() / 2, getHeight() / 2);
g2.setPaint(Color.blue);
g.fillOval(-N, -N, 2 * N, 2 * N);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(256, 256);
}
}
}
You might need to restore the move made by executing g2.translate(x, y); with g2.translate(-x, -y);:
Graphics2D g2 = (Graphics2D) g;
double x = getWidth() / 2d;
double y = getHeight() / 2d;
g2.translate(x, y);
g2.setPaint(Color.BLUE);
g2.fill(s);
g2.translate(-x, -y);
Another common way is to use a new Graphics object which is a copy of GPanel's Graphics object:
Graphics#create(), Graphics#dispose()
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(getWidth() / 2, getHeight() / 2);
g2.setPaint(Color.BLUE);
g2.fill(s);
g2.dispose();
Main2.java
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
class GPanel extends JPanel {
private final Rectangle s = new Rectangle(0, 0, 16, 16);
private void doDrawing(Graphics g) {
g.setColor(Color.RED);
g.fillRect(s.x, s.y, s.width, s.height);
// Graphics2D g2 = (Graphics2D) g;
// double x = getWidth() / 2d;
// double y = getHeight() / 2d;
// g2.translate(x, y);
// g2.setPaint(Color.BLUE);
// g2.fill(s);
// g2.translate(-x, -y);
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(getWidth() / 2, getHeight() / 2);
g2.setPaint(Color.BLUE);
g2.fill(s);
g2.dispose();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public class Main2 extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
GPanel panel = new GPanel();
panel.setBorder(BorderFactory.createTitledBorder("Title"));
JFrame ex = new JFrame();
ex.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
ex.getContentPane().add(panel);
ex.setSize(400, 400);
ex.setLocationRelativeTo(null);
ex.setVisible(true);
});
}
}
try this code:
private void doDrawing(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.translate(0, 0);
}
I'm trying to make a paint program, but I'm having trouble with drawing lines when dragging the mouse. It appears that the paint keeps on refreshing and so it only draws the current position of my mouse.
I a bit new to this, so how would I be able to get all the lines to show on the JPanel when dragging the mouse? Thanks, and here is what I have:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
public class DrawingPanel extends JPanel{
Point start;
Point end;
static Color c = Color.black;
DrawingPanel(){
addMouseMotionListener(new ml());
addMouseListener(new ml());
}
public class ml implements MouseMotionListener, MouseListener{
public void mouseMoved(MouseEvent ev){}
public void mousePressed(MouseEvent e){
end = e.getPoint();
}
public void mouseDragged(MouseEvent e){
start = end;
end=e.getPoint();
repaint();
}
public void mouseReleased(MouseEvent e){
start=null;
end=null;
}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(c);
if(start!=null){
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawLine(start.x, start.y, end.x, end.y);
}
}
}
There are multiple ways to solve your problem. #MattiasF and #MadProgrammer are correct: Swing is doing what it should be doing. Your paintComponent method is supposed to repaint the entire scene, not add onto the previous one.
The solutions suggested so far result in an application that is doing vector graphics: you are remembering the original drawing operations, and on every paint, you are executing every one of them (Java2D optimises some of it, because it will not really redraw the areas that are not currently visible on the screen, but it also takes time to figure out which areas are visible and which ones not)
The advantages are that you can scale the drawing operations perfectly if you need a bigger or smaller image. The disadvantages are that it may be slower once you have stored many drawing operations, and that you cannot (easily) do bitmap manipulations.
Another approach to this is the bitmap approach. You build up a bitmap of your drawing so far in memory, and in the paintComponent method you draw the bitmap onto the screen.
The advantage is that it's usually faster. It also allows bitmap operations, and it is also often easier to program against this model as you can just draw when you need it, rather than build op a list of drawing operations in memory. The disadvantages are that it uses more memory (until you have many drawing operations) and that you cannot perfectly scale your image up and down anymore.
To make your example work with a bitmap kept in memory, add the fields image and imageGraphics into your class, and replace your mouse listener ml as well as the paintComponent method with the code below:
private BufferedImage image = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
private Graphics2D imageGraphics = image.createGraphics();
public class ml extends MouseAdapter implements MouseMotionListener, MouseListener {
public void mousePressed(MouseEvent e) {
end = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
start = end;
end = e.getPoint();
imageGraphics.setColor(c);
imageGraphics.setStroke(new BasicStroke(5));
imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
imageGraphics.drawLine(start.x, start.y, end.x, end.y);
repaint();
}
public void mouseReleased(MouseEvent e) {
start = null;
end = null;
}
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, null, 0, 0);
}
You will immediately see the problem with scaling. The bitmap buffer is 500x500 pixels, and anything outside it will not be drawn. This is basically the same way that Microsoft paint works: you need to know the canvas size before you start drawing.
You have done almost everything right. The reason for your program only showing the current mouse position is because you don't save the starting position on mousePressed. Try replacing:
public void mousePressed(MouseEvent e){
end = e.getPoint();
}
with:
public void mousePressed(MouseEvent e){
start = e.getPoint();
}
And also:
public void mouseDragged(MouseEvent e){
start = end;
end=e.getPoint();
repaint();
}
with:
public void mouseDragged(MouseEvent e){
end = e.getPoint();
repaint();
}
That will get it working to draw one line. If you want more lines you can just add each finished line to a list in mouseReleased. Try adding this to your DrawingPanel class:
private ArrayList<Point> points = new ArrayList<Point>();
also replace this:
public void mouseReleased(MouseEvent e){
start = null;
end = null;
}
with:
public void mouseReleased(MouseEvent e){
points.add(start);
points.add(end);
start = null;
end = null;
}
And also replace:
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(c);
if(start!=null){
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(5));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawLine(start.x, start.y, end.x, end.y);
}
}
with:
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(c);
g2.setStroke(new BasicStroke(5));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//Draw all previous lines
for (int i = 0; i < points.size(); i+=2) {
g2.drawLine(points.get(i).x, points.get(i).y, points.get(i+1).x, points.get(i+1).y);
}
//Draw the current line if there is one
if(start != null && end != null){
g2.drawLine(start.x, start.y, end.x, end.y);
}
}
Now we save each finished line in the list, each even index is the start of a line and each odd is the end of a line. If you want to be able to draw "curves" that looks exactly like you moved the mouse you have to change this code a bit, but at least this should give you something to work with. Just submit an additional question if you need further help.
It appears that the paint keeps on refreshing
Yes, that's how painting works, take a look at Painting in AWT and Swing for more details
As a possible solution, you could add each new Point a List and simply draw a line between each point within the paintComponent method by iterating over the List.
You could also create a Shape and draw the lines within in and paint this shape within the paintComponent method.
Take a look at 2D Graphics for more details
Example for drawing single lines
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawLine {
public static void main(String[] args) {
new DrawLine();
}
public DrawLine() {
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 Point startPoint, endPoint;
private List<Point[]> lines;
public TestPane() {
lines = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
endPoint = e.getPoint();
Point[] points = new Point[]{startPoint, endPoint};
lines.add(points);
startPoint = null;
endPoint = null;
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
endPoint = e.getPoint();
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
for (Point[] p : lines) {
g2d.drawLine(p[0].x, p[0].y, p[1].x, p[1].y);
}
if (startPoint != null && endPoint != null) {
g2d.setColor(Color.RED);
g2d.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
g2d.dispose();
}
}
}
Example for drawing multiple, connected lines
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawLine {
public static void main(String[] args) {
new DrawLine();
}
public DrawLine() {
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 List<Point> points;
private Point mousePoint;
public TestPane() {
points = new ArrayList<>(25);
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
Point startPoint = null;
for (Point p : points) {
if (startPoint == null) {
startPoint = p;
} else {
g2d.drawLine(startPoint.x, startPoint.y, p.x, p.y);
startPoint = p;
}
}
if (startPoint != null) {
g2d.setColor(Color.RED);
g2d.drawLine(startPoint.x, startPoint.y, mousePoint.x, mousePoint.y);
}
g2d.dispose();
}
}
}
I'm trying to add an imagepanel which extends JPanel to another JPanel. Which is not working well for me. The paint function of image panel don't get invoked inside Jpanel but works fine in JFrame. Any ideas or help will be appreciated.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
class ImagePanel extends JPanel
{
int g_h=10,g_w=10;
int width=50,height=50;
int cornerradius;
Image castle;
Dimension size;
protected int x1,y1;
Color c1=new Color(255, 0, 0);
Rectangle rec;
boolean b=false;
boolean imboo=false;
boolean roundb= false;
Graphics g= this.getGraphics();
protected int strokeSize = 1;
protected Color shadowColor = Color.BLACK;
boolean shadowed = false;
public ImagePanel()
{
//super();
setOpaque(false);
setLayout(null);
System.out.println("it executed");
}
public ImagePanel(int x, int y)
{
setSize(x, y);
}
public void setSize(int x,int y){
width=x;
height=y;
}
public int getheight(){
return height;
}
public int getwidth(){
return width;
}
public void setImagePanelBounds(
int x, int y, int width, int height){
x1=x;
y1=y;
this.width= width;
this.height= height;
System.out.println("6it executed");
}
public void setroundcorners(boolean b, int i){
roundb=b;
cornerradius=i;
System.out.println("5it executed");
}
public void setImage(String s){
imboo=true;
size = new Dimension();
castle = new ImageIcon(s).getImage();
size.width = castle.getWidth(null);
size.height = castle.getHeight(null);
setPreferredSize(size);
System.out.println("4it executed");
}
public void paint(Graphics gh){
System.out.println("it executed p");
{int x=this.getWidth();
int j=20,a=20;
Graphics2D g2= (Graphics2D)gh.create();
{
g2.setColor(Color.WHITE);
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setComposite(AlphaComposite.SrcAtop);
rec= new Rectangle(x1, y1, width, height);
//Start of If-else
if(roundb){
g2.setClip(new RoundRectangle2D.Float(
(int)rec.getX(),(int)rec.getY(),
(int)rec.getWidth(),(int)rec.getHeight(),
cornerradius, cornerradius));
System.out.println("it executed");
}
// End of If-Else
// Image condition Starts
if (imboo){
g2.drawImage(castle, (int)rec.getX(),
(int)rec.getY(), (int)rec.getWidth(),
(int)rec.getHeight(), null);
//g.drawImage(castle, (int)rec.getX(),(int)rec.getY(), null);
}
// Image condition Ends
g2.setColor(Color.BLUE);
}
}
}
public static void main(String[]args)
{
ImagePanel t1=new ImagePanel();
JPanel jp1= new JPanel();
jp1.add(t1);
jp1.setLayout(null);
jp1.setBounds(0, 0, 600, 600);
JFrame jf1= new JFrame("Testing");
t1.setImage("icons/1.png");
//t1.setImage("1.jpg");
t1.setLayout(null);
t1.setroundcorners(true, 10);
//t1.setShadow(true);
t1.add(new JLabel("niak"));
//t1.setShadowDimensions(18, 18, 305, 305, 12);
t1.setImagePanelBounds(20, 20, 100, 100);
// jf1.add(t1);
jf1.setSize(600, 600);
jf1.setDefaultCloseOperation(jf1.EXIT_ON_CLOSE);
jf1.setVisible(true);
//jf1.revalidate();
jf1.setLayout(null);
}
}
Let's start with...
Don't use getGraphics. This is not how to perform custom painting. getGraphics may return null and is, at best, a snapshot, which will be discard when the next paint cycle occurs.
JPanel already has getWidth, getHeight and setSize methods, you should never have a need to overridden them. Instead, you should override getPreferredSize and return a size hint back that the parent layout manager can use.
If you create a Graphics context, you should dispose of it. In your paint method, you use gh.create, this is consuming resources and under some systems, until the Graphics context is disposed, it may not actually paint anything.
Don't override paint, instead use paintComponent
DO NOT modify the clip rectangle. Seriously, this is going to cause you more issues then you can imagine.
Don't use null layout managers without EXTREMELY good reason.
JPanel has a setBounds method, while, under normal circumstances, you shouldn't need to use it, since you've thrown away the layout manager, you should use it.
Basically, you've discard all the inner workings of the JPanel that enable the paint system to know that it should actually paint your panel
Updated with example
As an example...
Instead of using the clip, I use a masking technique, and mask the shape I want over the source image. I also buffer the result, which should make it more memory conservative as well as render faster
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.Insets;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class ImagePaneExample {
public static void main(String[] args) {
new ImagePaneExample();
}
public ImagePaneExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
try {
BufferedImage img = ImageIO.read(new File("C:\\hold\\thumbnails\\2005-09-29-3957.jpeg"));
ImagePane imgPane = new ImagePane();
imgPane.setImage(img);
imgPane.setRounded(true);
imgPane.setBorder(new EmptyBorder(20, 20, 20, 20));
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(imgPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (Exception exp) {
exp.printStackTrace();
}
}
});
}
public class ImagePane extends JPanel {
private BufferedImage img;
private BufferedImage renderImg;
private boolean rounded;
public ImagePane() {
}
public void setRounded(boolean value) {
if (value != rounded) {
rounded = value;
renderImg = null;
firePropertyChange("rounded", !rounded, rounded);
repaint();
}
}
public boolean isRounded() {
return rounded;
}
public void setImage(BufferedImage value) {
if (value != img) {
BufferedImage old = img;
img = value;
renderImg = null;
firePropertyChange("image", old, img);
repaint();
}
}
public BufferedImage getImage() {
return img;
}
#Override
public Dimension getPreferredSize() {
Dimension size = img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
Insets insets = getInsets();
size.width += (insets.left + insets.right);
size.height += (insets.top + insets.bottom);
return size;
}
protected void applyQualityRenderHints(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
}
protected BufferedImage getImageToRender() {
if (renderImg == null) {
BufferedImage source = getImage();
if (source != null) {
if (isRounded()) {
BufferedImage mask = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
applyQualityRenderHints(g2d);
g2d.setBackground(new Color(255, 255, 255, 0));
g2d.clearRect(0, 0, mask.getWidth(), mask.getHeight());
g2d.setBackground(new Color(255, 255, 255, 255));
g2d.fillRoundRect(0, 0, mask.getWidth(), mask.getHeight(), 40, 40);
g2d.dispose();
BufferedImage comp = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_ARGB);
g2d = comp.createGraphics();
applyQualityRenderHints(g2d);
g2d.setBackground(new Color(255, 255, 255, 0));
g2d.clearRect(0, 0, source.getWidth(), source.getHeight());
g2d.drawImage(source, 0, 0, this);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
g2d.drawImage(mask, 0, 0, this);
g2d.dispose();
renderImg = comp;
} else {
renderImg = source;
}
}
}
return renderImg;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage img = getImageToRender();
System.out.println(img);
if (img != null) {
Insets insets = getInsets();
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
int x = ((width - img.getWidth()) / 2);
int y = ((height - img.getHeight()) / 2);
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}
}
}
I'd recommend some more reading through Creating a UI with Swing, in particular the section on layout managers, as well as Performing Custom Painting and Painting in AWT and Swing
All you have to do is to set the Layout of your panel jp1 to BorderLayout and add the image panel t1 to BorderLayout.CENTER, like this:
JPanel jp1= new JPanel(new BorderLayout());
jp1.add(t1, BorderLayout.CENTER);