I've created a custom border class where I fill a rectangle to act as a background for a component. Note that this border will have a more complex shape in the future, not just a simple rectangle.
When I add my border to a component, the text of the component will appear behind the border and make the text unreadable. (The result is depicted in the image below.)
Is there a way to draw the border below the text?
My border class:
public class CustomBorder extends AbstractBorder {
private static final long serialVersionUID = 1L;
#Override
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(125, 125, 125, 255));
g2d.fillRect(x - 10, y - 10, width + 20, height + 20);
}
#Override
public Insets getBorderInsets(Component c) {
return super.getBorderInsets(c);
}
#Override
public Insets getBorderInsets(Component c, Insets insets) {
return super.getBorderInsets(c, insets);
}
#Override
public boolean isBorderOpaque() {
return super.isBorderOpaque();
}
}
Main:
public static void main(String[] args) {
JLabel label = new JLabel("JLabel text");
label.setBorder(new CompoundBorder(new EmptyBorder(50, 20, 20, 20), new CustomBorder()));
JFrame frame = new JFrame("");
frame.setLayout(new FlowLayout());
frame.setSize(200, 200);
frame.add(label);
frame.setVisible(true);
}
Edit: I should also note that I will be using this border to a chat program, which will be using bubble-shaped messages, so a colored square using setBackground() is a no-no.
See A Closer Look at the Paint Mechanism which explains how the painting is done. The border is painted after the text of the label is painted.
What exactly are you trying to do? Your border painting code doesn't make sense. You are trying to fill a rectangle equal to the width/height of the component + 20 pixels, which means you are trying to paint an area larger than the component.
If you are just trying to paint a background on a label then you can use:
label.setOpaque( true );
label.setBackground(...);
Edit: The code in this answer that was linked in the comment section below solved the problem.
You could always use g2d.drawString().
However, if that is not to be utilised for some reason, you could just do:
JLabel l = new JLabel("foo");
l.setBackground(Color.GRAY);
l.setOpaque(true);
Related
I have added webcam to my software using com.github.sarxos.webcam. It has a JPanel named WebcamPanel and has predefined webcam sizes while I need my custom size of pictures. I managed to crop the images taken from webcam at 640 x 480. I want to put a red rectangle over the WebcamPanel to show that this part of the image will be saved.
public class CardPanel {
Dimension panelDim = new Dimension(640, 480);
public Cardpanel(){
//....Button Defined earlier
btnTakePhoto.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
webcameFrame();
}
});
}
private void webcamFrame(){
imageFrame = new JFrame("Photo Capture");
// Did some calculations to put window at center
imageFrame.setBounds(screenSize.width / 2 - frameWidth / 2, screenSize.height / 2 - frameHeight / 2, frameWidth,
frameHeight);
imageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
imageFrame.setContentPane(contentPane);
JPanel webcamWindow = new JPanel();
RedHighlighter redHighlighter = new RedHighlighter();
Webcam webcam = Webcam.getDefault();
webcam.setViewSize(WebcamResolution.VGA.getSize());
webcamPanel = new WebcamPanel(webcam);
webcamPanel.setFillArea(true);
webcamPanel.setMirrored(false);
webcamPanel.setPreferredSize(panelDim);
webcamWindow.add(webcamPanel);
webcamWindow.add(redHighlighter);
hBox.add(webcamWindow);
}
// Sub Class just for drawing the rectangle
public class RedHighlighter extends JPanel{
public RedHighlighter() {
// If you delete the following line, nothing will appear
setPreferredSize(new Dimension(400, 400));
}
#Override
public void paint(Graphics g) {
g.setColor(Color.RED);
g.drawRect(100, 100, 200, 200);
}
}
}
I used JLayeredPanes but no matter what you do it will cover whole size and will show only one item at a time.
Overriding paint method helped me draw the rectangle but it's on side and not on top.
As you can see the rectangle has pushed WebcamPanel towards left. I want webcamPanel to remain in it's position while the rectangle on top of it at center. Please suggest an efficient approach to this problem. Thanks!
The one JPanel is being pushed over due to the layout managers that you are using. If you want one JPanel to overly another, you'll want to consider using a JLayeredPane, with the video images in the lower level, perhaps the JLayeredPane.DEFAULT layer, and the drawing JPanel above it.
Other options and issues:
You could potentially draw in the same JPanel that the image is being displayed in by displaying the image in a paintComponent method as well as the drawing (in lines of code after the image is displayed.
Look into use of a JLayer as a way of adding a drawing "decoration" over your image.
Always override paintComponent, not paint
Always call the super's painting method within your override.
It worked!
public class MyWebcamPanel extends WebcamPanel {
/**
*
*/
private static final long serialVersionUID = 2808353446021354508L;
public MyWebcamPanel(Webcam webcam) {
super(webcam);
}
#Override
protected void paintComponent(Graphics g) {
int x = 180;
int y = 87;
super.paintComponent(g);
g.setColor(Color.RED);
g.drawRect(x, y, 640-2*x, 480-2*y);
}
}
I put a JTextArea in a JPanel. This JPanel has a picture on the background, and the JTextArea is translucent (translucid red) to show the background through. I don't want the user to be able to edit or select the text, I want it to act just as a JLabel (but with multiple lines and easy to word wrap and adjust to screen resize).
I tried all these options:
text.setEditable(false);
text.setFocusable(false);
text.setEnabled(false);
text.setHighlighter(null);
but still some change of color happens as the user drags the mouse over the JTextArea. Anyone knows what is going on?
You can't simply set the background color of a component to "transparent" and expect Swing to deal with it. You need to flag the component as transparent (setOpaque(false)), only then will Swing's repaint manager know that it has to update the components under it.
This then leads you to the problem of how to paint the background (as Swing only has the concept of fully opaque or fully transparent).
To do this, you need to supply your own paint routines (override paintComponent, fill the background, update the component)...this is essentially what Rob Camick's solution is doing, it just provides a nice wrapper component for you...
Below is an example of using a JLabel using text wrapped in HTML and a JTextArea, both updated to support "translucency"...
Using a JLabel
Using a JTextArea
Now, it would be a lot easier to achieve using Rob's wrapper class, but this provides you with the idea of what is going wrong and what you would need to do to fix it.
public class MultiLineLabel {
public static void main(String[] args) {
new MultiLineLabel();
}
public MultiLineLabel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BackgroundPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TransclucentLabel extends JLabel {
public TransclucentLabel(String text) {
super(text);
setVerticalAlignment(TOP);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - (insets.left + insets.right);
int height = getHeight() - (insets.top + insets.bottom);
g2d.setColor(new Color(255, 0, 0, 128));
g2d.fillRect(x, y, width, height);
super.paintComponent(g);
}
}
public class TransclucentTextArea extends JTextArea {
public TransclucentTextArea(String text) {
super(text);
setOpaque(false);
setLineWrap(true);
setWrapStyleWord(true);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - (insets.left + insets.right);
int height = getHeight() - (insets.top + insets.bottom);
g2d.setColor(new Color(255, 0, 0, 128));
g2d.fillRect(x, y, width, height);
super.paintComponent(g);
}
}
public class BackgroundPane extends JPanel {
private BufferedImage background;
public BackgroundPane() {
setLayout(new BorderLayout());
// addLabel();
addTextArea();
setBorder(new EmptyBorder(24, 24, 24, 24));
try {
background = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
protected void addTextArea() {
StringBuilder sb = new StringBuilder(128);
sb.append("I put a JTextArea in a JPanel. This JPanel has a picture on the background, and the JTextArea is translucent (translucid red) to show the background through. I don't want the user to be able to edit or select the text, I want it to act just as a JLabel (but with multiple lines and easy to word wrap and adjust to screen resize).\n\n");
sb.append("I tried all these options:\n\n");
sb.append("text.setEditable(false);\n");
sb.append("text.setFocusable(false);\n");
sb.append("text.setEnabled(false);\n");
sb.append("text.setHighlighter(null);\n\n");
sb.append("but still some change of color happens as the user drags the mouse over the JTextArea. Anyone knows what is going on?\n");
add(new TransclucentTextArea(sb.toString()));
}
protected void addLabel() {
StringBuilder sb = new StringBuilder(128);
sb.append("<html>");
sb.append("<p>I put a JTextArea in a JPanel. This JPanel has a picture on the background, and the JTextArea is translucent (translucid red) to show the background through. I don't want the user to be able to edit or select the text, I want it to act just as a JLabel (but with multiple lines and easy to word wrap and adjust to screen resize).</p><br>");
sb.append("<p>I tried all these options:</p><br>");
sb.append("<p>text.setEditable(false);<br>");
sb.append("text.setFocusable(false);<br>");
sb.append("text.setEnabled(false);<br>");
sb.append("text.setHighlighter(null);</p><br>");
sb.append("<p>but still some change of color happens as the user drags the mouse over the JTextArea. Anyone knows what is going on?</p>");
add(new TransclucentLabel(sb.toString()));
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g.drawImage(background, x, y, this);
}
}
}
}
The idea of the program is that I have some buttons and an icon SOMEWHERE on the frame. I want the buttons to change the color. I'm only worried about making all the elements show up right now. If I comment out lines 11-13, I see "hello," printed out, with a red circle on top of it. Otherwise, I just have the button "red" without "hello" or my red circle. So here's my code:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class ButtonTester
{
public static void main (String[] args)
{
JFrame frame = new ButtonFrame();
frame.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton redButton = new JButton("Red");
frame.add(redButton);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class ButtonFrame extends JFrame
{
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
public ButtonFrame()
{
setTitle("Hello");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
ButtonPanel panel = new ButtonPanel();
add(panel);
}
}
class ButtonPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hello !", 100, 100);
Icon ico = new ColorIcon(32);
ico.paintIcon(null, g, 75, 75);
}
}
I'm 90% sure the problem is lines 11-13, but I'm not sure what to change to make everything visible.
Your problem is that your ButtonPanel's size is 0. Have it override getPreferredSize() and you will see what I mean:
class ButtonPanel extends JPanel {
private static final int PREF_W = 150;
private static final int PREF_H = PREF_W;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hello !", 100, 100);
// !! Icon ico = new ColorIcon(32);
// Icon ico = new ImageIcon();
// ico.paintIcon(null, g, 75, 75);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Also as an unrelated aside, why are you creating an Icon inside of the paintComponent method? This doesn't make sense to me and would only serve to needlessly slow your graphics down.
Edit
You state:
Ok, I see the difference after overriding getPreferredSize() But what would be the "better" or "correct" way to create the icon? I'm just trying to follow the directions for an exercise out of a Java textbook: Exercise 4.14. Write a program that shows a frame with three buttons labeled "Red", "Green", and "Blue", and a label containing an icon showing a circle that is initially red. As the user clicks the buttons, the fill color of the circle should change. When you change the color, you need to invoke the repaint method on the label. The call to repaint ensures that the paintIcon method is called so that the icon can be repainted with the new color.
You need to think on this a different way. Myself I'd create three ImageIcons one for a blue circle, one for red, and one for green. I'd then display the ImageIcon in a JLabel on my JFrame. I'd change the color by simply swapping the label's icons via its setIcon(...) method. I wouldn't worry about futzing with paintComponent(...) but rather would try to solve this in as simple a fashion as possible.
I put a JTextArea in a JPanel. This JPanel has a picture on the background, and the JTextArea is translucent (translucid red) to show the background through. I don't want the user to be able to edit or select the text, I want it to act just as a JLabel (but with multiple lines and easy to word wrap and adjust to screen resize).
I tried all these options:
text.setEditable(false);
text.setFocusable(false);
text.setEnabled(false);
text.setHighlighter(null);
but still some change of color happens as the user drags the mouse over the JTextArea. Anyone knows what is going on?
You can't simply set the background color of a component to "transparent" and expect Swing to deal with it. You need to flag the component as transparent (setOpaque(false)), only then will Swing's repaint manager know that it has to update the components under it.
This then leads you to the problem of how to paint the background (as Swing only has the concept of fully opaque or fully transparent).
To do this, you need to supply your own paint routines (override paintComponent, fill the background, update the component)...this is essentially what Rob Camick's solution is doing, it just provides a nice wrapper component for you...
Below is an example of using a JLabel using text wrapped in HTML and a JTextArea, both updated to support "translucency"...
Using a JLabel
Using a JTextArea
Now, it would be a lot easier to achieve using Rob's wrapper class, but this provides you with the idea of what is going wrong and what you would need to do to fix it.
public class MultiLineLabel {
public static void main(String[] args) {
new MultiLineLabel();
}
public MultiLineLabel() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BackgroundPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TransclucentLabel extends JLabel {
public TransclucentLabel(String text) {
super(text);
setVerticalAlignment(TOP);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - (insets.left + insets.right);
int height = getHeight() - (insets.top + insets.bottom);
g2d.setColor(new Color(255, 0, 0, 128));
g2d.fillRect(x, y, width, height);
super.paintComponent(g);
}
}
public class TransclucentTextArea extends JTextArea {
public TransclucentTextArea(String text) {
super(text);
setOpaque(false);
setLineWrap(true);
setWrapStyleWord(true);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - (insets.left + insets.right);
int height = getHeight() - (insets.top + insets.bottom);
g2d.setColor(new Color(255, 0, 0, 128));
g2d.fillRect(x, y, width, height);
super.paintComponent(g);
}
}
public class BackgroundPane extends JPanel {
private BufferedImage background;
public BackgroundPane() {
setLayout(new BorderLayout());
// addLabel();
addTextArea();
setBorder(new EmptyBorder(24, 24, 24, 24));
try {
background = ImageIO.read(new File("/path/to/your/image"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
protected void addTextArea() {
StringBuilder sb = new StringBuilder(128);
sb.append("I put a JTextArea in a JPanel. This JPanel has a picture on the background, and the JTextArea is translucent (translucid red) to show the background through. I don't want the user to be able to edit or select the text, I want it to act just as a JLabel (but with multiple lines and easy to word wrap and adjust to screen resize).\n\n");
sb.append("I tried all these options:\n\n");
sb.append("text.setEditable(false);\n");
sb.append("text.setFocusable(false);\n");
sb.append("text.setEnabled(false);\n");
sb.append("text.setHighlighter(null);\n\n");
sb.append("but still some change of color happens as the user drags the mouse over the JTextArea. Anyone knows what is going on?\n");
add(new TransclucentTextArea(sb.toString()));
}
protected void addLabel() {
StringBuilder sb = new StringBuilder(128);
sb.append("<html>");
sb.append("<p>I put a JTextArea in a JPanel. This JPanel has a picture on the background, and the JTextArea is translucent (translucid red) to show the background through. I don't want the user to be able to edit or select the text, I want it to act just as a JLabel (but with multiple lines and easy to word wrap and adjust to screen resize).</p><br>");
sb.append("<p>I tried all these options:</p><br>");
sb.append("<p>text.setEditable(false);<br>");
sb.append("text.setFocusable(false);<br>");
sb.append("text.setEnabled(false);<br>");
sb.append("text.setHighlighter(null);</p><br>");
sb.append("<p>but still some change of color happens as the user drags the mouse over the JTextArea. Anyone knows what is going on?</p>");
add(new TransclucentLabel(sb.toString()));
}
#Override
public Dimension getPreferredSize() {
return background == null ? super.getPreferredSize() : new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background != null) {
int x = (getWidth() - background.getWidth()) / 2;
int y = (getHeight() - background.getHeight()) / 2;
g.drawImage(background, x, y, this);
}
}
}
}
Here's my canvas class extending JPanel:
package start;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Board extends JPanel
{
private static final long serialVersionUID = 1L;
public Board() {}
public void paintComponent(Graphics g)
{
int width = getWidth();
int height = getHeight();
this.setBackground(Color.green);
g.setColor(Color.black);
g.drawOval(0, 0, width, height);
}
}
Here's the method where I'm calling it:
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Hello");
frame.setPreferredSize(new Dimension(700, 700));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Board b = new Board();
frame.getContentPane().add(b);
frame.pack();
frame.setVisible(true);
}
But this shows the oval on the default colour. I also tried without the this., and then tried setting the colour of b, and setting the colour inside the constructor, but none of these worked. What's wrong?
EDIT: Sorry for not making thing clear, my goal was to display a thin black oval on a green background.
In the paintComponent method you do not have to use setBackground to change the colour of the JPanel. That should be done outside of paintComponent. paintComponent will probably use the colour of the background before you change it.
There are a number of things you can try. One, is to set the colour in the constructor and then call the super class' paintComponent first like this:
public Board() {
this.setBackground(Color.GREEN);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
g.setColor(Color.BLACK);
g.drawOval(0, 0, width, height);
}
Also note the color constants are all upper case. i.e. BLACK or GREEN.
If you want to change the background colour dynamically then you can do so in the event handler such as mouseEntered or actionPerformed etc.
While the code does not exactly make clear what's your intent i try to fix some issues:
If you want a green background, do as #vincent told you. YOu should see a black oval in green background. The "super.paintComponent" will fill its area with the components background automatically if the panel is opaque.
If you want a green oval on white background, maybe with black border
public void paintComponent(Graphics g)
{
int width = getWidth();
int height = getHeight();
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillOval(0, 0, width, height);
g.setColor(Color.BLACK);
g.drawOval(0, 0, width, height);
}
EDIT
i forgot super