relatively straight-forward, how can I set the background color of a JMenuBar?
ive tried:
MenuBar m = new MenuBar() {
void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setBackground(Color.yellow);
g2.fillRect(0, 0, getWidth(), getHeight());
}
but nothin'
Well, to start with, what you've shown is not a JMenuBar, it's MenuBar, there's a significant difference. Try using a JMenuBarand use setBackground to change the background color
Updated from feedback from Vulcan
Okay, in the cases where setBackground doesn't work, this will ;)
public class MyMenuBar extends JMenuBar {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
}
}
With MadProgrammer's approach you will get menubar background painted twice - once by the UI (it could be gradient on Windows for example, which takes some time to paint) and once by your code in paintComponent method (atop of the old background).
Better replace menubar UI by your own one based on BasicMenuBarUI:
menuBar.setUI ( new BasicMenuBarUI ()
{
public void paint ( Graphics g, JComponent c )
{
g.setColor ( Color.RED );
g.fillRect ( 0, 0, c.getWidth (), c.getHeight () );
}
} );
You can also set that UI globally for all menubars so that you don't need to use your specific component each time you create menubar:
UIManager.put ( "MenuBarUI", MyMenuBarUI.class.getCanonicalName () );
MyMenuBarUI class here is your specific UI for all menubars.
Related
I have experimented with two different methods of drawing the same shape, the first image is drawn by overriding JPanel's paintComponent(Graphics g) method and using g.drawOval(..) etc,
The second image is drawn by creating a buffered image and drawing on it by using buffered image's graphics. How can I achieve the same rendering quality on both approaches? I have tried using many different rendering hints but none of them gave the same quality. I also tried sharpening by using Kernel and filtering, still couldn't.
private void createImage() {
image = new BufferedImage(IMG_SIZE, IMG_SIZE, BufferedImage.TYPE_INT_ARGB);
Graphics2D gr = image.createGraphics();
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
gr.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
//something along the way
gr.drawOval(.....);
gr.drawLine(.....);
gr.drawOval(.....);
panel.repaint();
gr.dispose();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(backgroundColor);
if (USE_BUFFERED_IMAGE) {
g.drawImage(image, startX, startY, null);
} else {
//something along the way
g.drawOval(.....);
g.drawLine(.....);
g.drawOval(.....);
}
}
Drawing using JPanel paintComponent graphics
Drawing using Buffered Image graphics then it is drawn on Jpanel via drawimage
EDIT
I found my solution by getting almost every setting of panel graphics and applying them to buffered image graphics. Not by using only using the same rendering hints or "minimal reproducible examples" approaches. Here, the importing thing is that the panel's graphic scales everything by 1.25 and then scales down to the original before showing it on the panel.
Here is an example, -this is not exactly how my code is, this is just an example to give you an idea-
private void createImages(Paint paint, RenderingHints hints,
AffineTransform transform, Stroke stroke,
Composite composite, GraphicsConfiguration config ){
image = config.createCompatibleImage(IMG_SIZE, IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
Graphics2D gr = image.createGraphics();
// same options
gr.setPaint(paint);
gr.setRenderingHints(hints);
gr.setTransform(transform);
gr.setStroke(stroke);
gr.setComposite(composite);
//something along the way
gr.drawOval(.....);
gr.drawLine(.....);
gr.drawOval(.....);
panel.repaint();
gr.dispose();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(backgroundColor);
if (USE_BUFFERED_IMAGE) {
Graphics2D g2 = (Graphics2D)g;
createImages(g2.getPaint(), g2.getRenderingHints(),g2.getTransform(),
g2.getStroke(),g2.getComposite(), g2.getDeviceConfiguration());
//scaling down is important because your drawings get scaled to 1.25
// by panels graphics' transformation
g.drawImage(image, startX, startY,(int)(IMG_SIZE*0.8),(int)(IMG_SIZE*0.8), null);
} else {
//something along the way
g.drawOval(.....);
g.drawLine(.....);
g.drawOval(.....);
}
}
I found my solution by getting almost every setting of panel graphics and applying them to buffered image graphics. Here, the importing thing is that the panel's graphic scales everything by 1.25 and then scales down to the original before showing it on the panel.
Here is an example, -this is not exactly how my code is, this is just an example to give you an idea-
private void createImages(Paint paint, RenderingHints hints,
AffineTransform transform, Stroke stroke,
Composite composite, GraphicsConfiguration config ){
image = config.createCompatibleImage(IMG_SIZE, IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
Graphics2D gr = image.createGraphics();
// same options
gr.setPaint(paint);
gr.setRenderingHints(hints);
gr.setTransform(transform);
gr.setStroke(stroke);
gr.setComposite(composite);
//something along the way
gr.drawOval(.....);
gr.drawLine(.....);
gr.drawOval(.....);
panel.repaint();
gr.dispose();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(backgroundColor);
if (USE_BUFFERED_IMAGE) {
Graphics2D g2 = (Graphics2D)g;
createImages(g2.getPaint(), g2.getRenderingHints(),g2.getTransform(),
g2.getStroke(),g2.getComposite(), g2.getDeviceConfiguration());
//scaling down is important because your drawings get scaled to 1.25
// by panels graphics' transformation
g.drawImage(image, startX, startY,(int)(IMG_SIZE*0.8),(int)(IMG_SIZE*0.8), null);
} else {
//something along the way
g.drawOval(.....);
g.drawLine(.....);
g.drawOval(.....);
}
}
I'm trying to fill each circle with a colour. The paintComponent method is supposed to create the general outline of a traffic light.
Then the other methods are supposed to fill each circle with a different color, depending on what color the traffic light is going to be. I get the error, that it cant find symbol( e.g
TrafficLight.java:56: error: cannot find symbol
g2.fill(circle3);
I didnt think I had to pass the circle variables into the other methods, if they were in the same class. I tried adding the circle variables and then i get identifier expected. Pretty sure there's something about the passing of variables that im not getting, but any help would be appreciated
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class TrafficLight extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2= (Graphics2D) g;
Rectangle box= new Rectangle(100,800,200,700);
g2.setPaint(Color.BLACK);
g2.fill(box);
g2.draw(box);
Ellipse2D.Double circle1=new Ellipse2D.Double(200,200,200,200);
g2.draw(circle1);
Ellipse2D.Double circle2=new Ellipse2D.Double(200,200,200,400);
g2.draw(circle2);
Ellipse2D.Double circle3=new Ellipse2D.Double(200,200,200,600);
g2.draw(circle3);
}
public void drawRed(Graphics g)
{
Graphics2D g2=(Grpahics2D) g;
g2.setPaint(Color.RED);
g2.fill(circle1);
g2.setPaint(Color.BLACK);
g2.fill(circle2);
g2.fill(circle3);
}
public void drawGreen(Graphics g)
{
Graphics2D g2= (Graphics2D) g;
g2.setPaint(Color.GREEN);
g2.fill(circle3);
g2.setPaint(Color.BLACK);
g2.fill(circle2);
g2.fill(circle1);
}
public void drawYellow(Graphics g)
{
Graphics2D g2= (Graphics2D) g;
g2.setPaint(Color.YELLOW);
g2.fill(circle2);
g2.setPaint(Color.BLACK);
g2.fill(circle1);
g2.fill(circle3);
}
}
The entire code is missing.
But the general solution is to have the paintComponent method do all.
So have a state red/green/yellow.
public class TrafficLight extends JComponent {
public enum Light { RED, GREEN, YELLOW, BLACK }
private Light light = Light.BLACK;
public void setLight(Light light) {
this.light = light;
repaint(50L); // Repaint almost immediately.
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
...
// Draw red bulb
g2.setPaint(light == Light.RED ? Color.RED : Color.BLACK);
...
// Draw yellow bulb
g2.setPaint(light == Light.YELLOW ? Color.YELLOW : Color.BLACK);
...
// Draw green bulb
g2.setPaint(light == Light.GREEN ? Color.GREEN : Color.BLACK);
Switching the light then alters the state and causes a repaint. There are some repaint methods.
Instead of the enum one might use Color light;, but that is not so clear.
Also consider an enum value BLACK, for semaphores without electricity.
To handle automatic timed switching, one might use a swing Timer.
I'm trying to figure out if the repaint method does something that we can't do ourselves.
I mean,how are these two versions different?
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
g2.draw(r);
}
}
and
public class Component extends JComponent {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle r = new Rectangle(0,0,20,10);
g2.draw(r);
r.translate(5,5);
repaint();
}
}
The 2nd version can result in a very risky and poor animation since it can result in repaints being called repeatedly, and is something that should never be done. If you need simple animation in a Swing GUI, use a Swing Timer to drive the animation.
i.e.,
public class MyComponent extends JComponent {
private Rectangle r = new Rectangle(0,0,20,10);
public MyComponent() {
int timerDelay = 100;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
r.translate(5, 5);
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(r);
}
}
The use of repaint() is to suggest to the JVM that the component needs to be painted, but it should never be called in a semi-recursive fashion within the paint or paintComponent method. An example of its use can be seen above. Note that you don't want to call the painting methods -- paint or paintComponent directly yourselves except under very unusual circumstances.
Also avoid calling a class Componenet since that name clashes with a key core Java class.
I hope you can help me concerning a problem in my drawString.
I do some drawing within a Factory class and return a BufferedImage with a String on it.
public class Factory {
public BufferedImage create() {
TempPanel tempPanel = new TempPanel(new Dimension(100, 100));
return tempPanel.createImage();
}
private class TempPanel extends JPanel {
public TempPanel(Dimension d) {
this.setVisible(true);
this.setSize(d);
}
public BufferedImage createImage() {
BufferedImage bi = new BufferedImage(this.getSize().getWidth(), this.getSize().getHeight(), BufferedImage.TRANSLUCENT);
this.paintComponent(bi.createGraphics());
return bi;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponents(g);
// General setup
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Transparent Background
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(0, 0, graphicalObject.width, graphicalObject.height);
g2.setComposite(AlphaComposite.Src);
// Different settings for a certain graphical object
g2.setFont(new Font("TimesRoman", Font.PLAIN, 12);
// Actual drawing
g2.drawString("Test", 0, 0);
}
}
}
And then I also have a JPanel in which this image should be drawn:
public class Label extends JPanel {
// ...
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Factory factory = new Factory();
BufferedImage img = factory.create();
g2.drawImage(img, 0, 0, null);
}
}
The problem ist that the text only appears if i minimize and restore the window, but as soon as I start moving the mouse within the window, the text disappears again.
When I call drawRect() instead of drawString(), everything works fine?!
Can someone tell me why?
Thanks!
There are a number of things that "weird" me out...
Firstly, you're creating a new Graphics context, but you're not disposing of it. This could be leaving resources open and may actually prevent the contents from been rendered on some OSs...
So instead of...
BufferedImage bi = new BufferedImage(this.getSize().getWidth(), this.getSize().getHeight(), BufferedImage.TRANSLUCENT);
this.paintComponent(bi.createGraphics());
You should be doing something more like...
BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TRANSLUCENT);
Graphics2D g2d = bi.createGraphics();
this.paintComponent(g2d);
g2d.dispose();
Secondly, text is not render the same way that most other elements are, that is, it doesn't start at x/y and render down/right, in most cases it starts at x/y and renders, slightly, up/right. The actual coordinate is based on the Fonts FontMetrics getAscent value...
So, instead of...
g2.setFont(new Font("TimesRoman", Font.PLAIN, 12));
// Actual drawing
g2.drawString("Test", 0, 0);
You should be doing something more like...
g2.setFont(new Font("TimesRoman", Font.PLAIN, 12));
FontMetrics fm = g2.getFontMetrics();
// Actual drawing
g2.drawString("Test", 0, fm.getAscent());
See Working with Text APIs or more details...
Thirdly, JComponent and by extension, anything that extends from it, is an ImageObsever, this is important, as not everything that is painted is ready for display immediately, sometimes it needs further processing, rather than having to deal with this annoyance yourself, you can delegate the responsibility, so instead of...
g2.drawImage(img, 0, 0, null);
You should be doing something more like...
g2.drawImage(img, 0, 0, this);
This allows the component to listen to the underlying image and respond to any changes that might happen to the image data and update itself.
Forthly...
private class TempPanel extends JPanel {
public TempPanel(Dimension d) {
this.setVisible(true);
this.setSize(d);
}
public BufferedImage createImage() {
BufferedImage bi = new BufferedImage(this.getSize().getWidth(), this.getSize().getHeight(), BufferedImage.TRANSLUCENT);
this.paintComponent(bi.createGraphics());
return bi;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponents(g);
// General setup
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Transparent Background
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(0, 0, graphicalObject.width, graphicalObject.height);
g2.setComposite(AlphaComposite.Src);
// Different settings for a certain graphical object
g2.setFont(new Font("TimesRoman", Font.PLAIN, 12);
// Actual drawing
g2.drawString("Test", 0, 0);
}
}
Is freaking me out...Swing components are meant to be attached to a native peer onto which they are rendered, they are also optimised not to paint when they aren't displayable, this could be throwing off your rendering...
Infact, you could achieve the same thing using something like...
public static class Factory {
private static Renderer renderer;
public BufferedImage create() {
if (renderer == null) {
renderer = new Renderer(new Dimension(100, 100));
}
return renderer.createImage();
}
private class Renderer {
private Dimension size;
public Renderer(Dimension size) {
this.size = size;
}
public BufferedImage createImage() {
BufferedImage bi = new BufferedImage(size.width, size.height, BufferedImage.TRANSLUCENT);
Graphics2D g2 = bi.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// Transparent Background
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(0, 0, graphicalObject.width, graphicalObject.height);
g2.setComposite(AlphaComposite.Src);
// Different settings for a certain graphical object
g2.setFont(new Font("TimesRoman", Font.PLAIN, 12));
FontMetrics fm = g2.getFontMetrics();
// Actual drawing
g2.drawString("Test", 0, fm.getAscent());
g2.dispose();
return bi;
}
}
}
Which would divorce the actual painting from anything else and provide you with control over it...
In my paintComponent, I have drawRect, which draws a single rectangle. However, I want to make the outline of the rectangle thicker but I don't know how. So I thought of making another rectangle inside the existing one. I tried putting another drawRect but the rectangle isn't in the center.
Thanks for those who'll help!
g2d.setStroke(new BasicStroke(6));
The argument passed to the paintComponent(Graphics) method of a Swing component should actually be a Graphics2D instance. It can be cast to one.
See this example in which 3 strokes are layered.
import javax.swing.*;
import java.awt.*;
class StrokeIt {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
StrokePanel sp = new StrokePanel();
sp.setPreferredSize(new Dimension(400,100));
sp.setBackground(Color.BLUE);
JOptionPane.showMessageDialog(null, sp);
}
});
}
}
class StrokePanel extends JPanel {
int pad = 12;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(10));
g2d.drawRect( 0+pad, 0+pad,
getWidth()-(2*pad), getHeight()-(2*pad) );
g2d.setColor(Color.YELLOW);
g2d.setStroke(new BasicStroke(6));
g2d.drawRect( 0+pad, 0+pad,
getWidth()-(2*pad), getHeight()-(2*pad) );
g2d.setColor(Color.ORANGE);
g2d.setStroke(new BasicStroke(2));
g2d.drawRect( 0+pad, 0+pad,
getWidth()-(2*pad), getHeight()-(2*pad) );
}
}