I am wondering if there is a Standard Java Component (Icon or JLabel) that offers the abillity to show a notification count like the example below. I am not doing Android Development. I just want to show something similar in a Java desktop client application
The following code in a JLabel subclass would get the same result, but I am more interested in a standard solution. Which automatically derives the correct font size and automatically adjust to the visible space.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int w = getWidth();
Font orgFont = g.getFont();
Font deriveFont = orgFont.deriveFont(8.5f);
g2d.setPaint(Color.RED);
g2d.fillOval(w - 12, 0, 12, 12);
g2d.setPaint(Color.BLACK);
g2d.setFont(deriveFont);
g2d.drawString("99", w-10, 10);
g2d.setFont(orgFont);
}
if there is a Standard Java Component (Icon or JLabel)
No there is no standard component.
You might be able to:
Use the Compound Icon. It allows you to combine multiple icons into one.
Add a second JLabel with the notification Icon to the first JLabel. By default a JLabel doesn't have a layout manager but there is no reason you can't use one.
So for example to add the label in the top/right corner you could do:
JLabel main = new JLabel(…);
main.setLayout( new FlowLayout(FlowLayout.FlowLayout.RIGHT, 5, 5) );
JLabel notify = new JLabel(…);
main.add( notify );
Related
I'm learning java through university and I've been taught the basics of making a java program and designing GUIs. Maximizing a window after running my program makes all the JFrame components stay in place while grey fills the rest of the space. Here's an example of how it looks like:
JFrame window normally, Maximized window before "fix".
After failing to find a solution I came up with a band-aid solution which is to get the component locations and just move them with hard-coded values when the jframe is maximized. This was not an elegant solution and every jframe in my java course project increased in the number of elements on screen. Is there any piece of code to make my components move and resize automatically and dynamically?
Here's what I've tried so far:
First I obtained the positions of components through 2D points:
Point managementLoginBtnLocation, empLogLocation, logoLocation, customerBtnLocation, welcomeLblLocation, contactBtnLocation, aboutBtnLocation, mainMenuBtnLocation;
//Constructor and rest of code...
public final void getOriginalComponentLocations()
{
managementLoginBtnLocation = managementLoginBtn.getLocation();
empLogLocation = empLoginBtn.getLocation();
logoLocation = shopLogo.getLocation();
customerBtnLocation = customerBtn.getLocation();
welcomeLblLocation = welcomeLbl.getLocation();
contactBtnLocation = contactBtn.getLocation();
aboutBtnLocation = aboutBtn.getLocation();
mainMenuBtnLocation = mainMenuBtn.getLocation();
}
//This method is called within the constructor.
I implemented the ComponentListener Interface and added a component listener to my jframe. Then I made it so when the jframe's size changes, it changes the size of the jlabel used for background art. And if the label's width is greater than 800 (the default I used while designing) it moves the components and doubles their size and font size. When the jframe is minimized the label will go back to the default size so I made a method to revert the font sizes, because I found the component sizes and locations reset automatically.
public void componentResized(ComponentEvent e)
{
//Resizing the background label and setting its icon to a resized version of its current icon.
backgroundMainArt.setSize(this.getWidth() - 16, this.getHeight() - 21);
ImageIcon icon = new ImageIcon("C:\\Program Files\\OMOClothingStore\\Resources\\Main menu\\main menu background art.jpg");
Image img = icon.getImage();
Image newImage = img.getScaledInstance(backgroundMainArt.getWidth(), backgroundMainArt.getHeight(), Image.SCALE_FAST);
icon = new ImageIcon(newImage);
backgroundMainArt.setIcon(icon);
if(backgroundMainArt.getWidth() > 800) //When the size of the label is greater than default
{
//I move the components, enlarge the buttons and zoom the font size
moveComponents();
enlargeBtns();
zoomBtnsFontSize();
}
else //When the label is back to its original size
{
//I revert the font sizes as button sizes and positions reset automatically
revertBtnsFontSize();
setLogoIconAndBackgroundArtAndWelcomeLbl();
}
}
public void moveComponents()
{
moveLogo();
moveManagementLoginBtn();
moveEmployeeLoginBtn();
moveCustomerBtn();
moveWelcomeLbl();
moveContactInfoBtn();
moveAboutBtn();
moveMainMenuBtn();
}
public void moveLogo()
{
ImageIcon logoIcon = new ImageIcon("C:\\Program Files\\OMOClothingStore\\Resources\\Shared resources\\OMO Clothing Store logo.png");
Image logoImg = logoIcon.getImage();
Image newLogoImage = logoImg.getScaledInstance(250, 250, Image.SCALE_DEFAULT);
logoIcon = new ImageIcon(newLogoImage);
shopLogo.setIcon(logoIcon);
Point newLogoLocation = new Point();
newLogoLocation.x = (logoLocation.x * 2) + 200;
newLogoLocation.y = (logoLocation.y * 2) + 30;
shopLogo.setLocation(newLogoLocation);
}
//The rest of the "moveX" methods follow the same pattern as moveLogo()
public void enlargeBtns()
{
managementLoginBtn.setSize(410, 94);
empLoginBtn.setSize(410, 94);
customerBtn.setSize(410, 94);
}
public void zoomBtnsFontSize()
{
customerBtn.setFont(sizeBtn.getFont());
//sizeBtn is a JButton that has a font size of 24. I found that just creating a new Font object with bigger size here made the font way larger for some reason.
empLoginBtn.setFont(sizeBtn.getFont());
managementLoginBtn.setFont(sizeBtn.getFont());
}
public void revertBtnsFontSize()
{
empLoginBtn.setFont(new Font("Segoe UI", Font.PLAIN, 14));
managementLoginBtn.setFont(new Font("Segoe UI", Font.PLAIN, 14));
customerBtn.setFont(new Font("Segoe UI", Font.PLAIN, 14));
}
I split the moving of the components into many methods inside other methods because I found it easier to keep up with.
This worked. Here's how it looks like when running the JFrame: Maximized window after "fix". But moving on to other JFrames, they are more intricate and have many more components - extra buttons, panels with other components in them, menu bars, etc.
Is there a better approach to fixing this? Or do I just remove the ability to resize and move on?
After using custom made color:
Color bg = new Color(0f,0f,0f,0.5f);
for a JTextPane background I can see that background of JTextPane is shown with parts of background that comes from Jlabel that includes JTextPane in it.
Picture of what's happening:
So the bottom part of JTextPane background is OK, but the top one that's behind a text is having some issues.
How can I fix it? Have I made mistake by using custom color for simple transparent background for JTextPane?
Code for this part of the program:
t = new JTextPane();
SimpleAttributeSet style = new SimpleAttributeSet();
StyleConstants.setAlignment(style , StyleConstants.ALIGN_CENTER);
StyleConstants.setForeground(style , Color.white);
StyleConstants.setFontFamily(style, "Times new Roman");
StyleConstants.setFontSize(style, 20);
StyleConstants.setBold(style, true);
t.setParagraphAttributes(style,true);
t.setText(" " + text.getT1().get(0).toUpperCase());
t.setOpaque(true);
Color bg = new Color(0f,0f,0f,0.5f);
t.setBackground(bg);
t.setEditable(false);
t.setBounds(250, 400, 300, 50);
animation.add(t);
Swing doesn't support transparent backgrounds properly. Swing expects the component to be fully opaque or not based on the setOpaque(....) property.
When using transparent background you need to make sure the background of the parent container is painted first before the background of the transparent component is painted.
Check out Background With Transparency for more information on this process.
You can customize you component and do your own painting with code like:
JPanel panel = new JPanel()
{
protected void paintComponent(Graphics g)
{
g.setColor( getBackground() );
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
panel.setOpaque(false); // background of parent will be painted first
panel.setBackground( new Color(255, 0, 0, 20) );
frame.add(panel);
Or the easier approach is to use the AlphaContainer class provided in the above link.
I think I got a similar problem. You need to have at least the container of the JTextPane made opaque with no transparency. Otherwise Swing calculates very wrongly the background. You might report that as a bug I think.
i have a problem with drawing in my JFrame app. I have these two functions:
Im quite new to such graphics in Java, i was wondering if someone would be kind and help me. I need to add the line on the JLabel called areaImage. I tried using some done code i found here but none worked for me. Is my code usable with some bugs? Or is it completely bad?
Please dont just post a link with some code, Im not skilled enough to understand it and then change it so it fits my app...
This one just makes the window, adds the components:
public void game (int difficulty)
{
getContentPane().removeAll();
areaImage = new JLabel ();
areaImage.setBounds (50,100,650,500);
areaImage.setForeground(Color.WHITE);
areaImage.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Color.BLACK));
add(areaImage);
paint (100,120,500,500, null);
info = new JLabel (" Write your answer into the text field");
info.setBounds(730,180,250,50);
info.setBorder(BorderFactory.createMatteBorder(2,2,2,2,Color.BLACK));
info.setFont(new Font ("Arial", Font.PLAIN, 15));
areaImage.setForeground(Color.red);
add(info);
inputField = new JTextField("");
inputField.setBounds(810, 240, 80, 50);
add(inputField);
checkAnswer = new JButton ("Check");
checkAnswer.setBounds(750, 330, 200, 50);
checkAnswer.setContentAreaFilled(false);
checkAnswer.setOpaque(false);
checkAnswer.addActionListener(this);
checkAnswer.setFont (new Font("Arial",Font.PLAIN, 30));
add(checkAnswer);
next = new JButton ("Next");
next.setBounds(750,440,200,50);
next.setContentAreaFilled(false);
next.setOpaque(false);
next.addActionListener(this);
next.setFont (new Font("Arial",Font.PLAIN, 30));
add(next);
end= new JButton ("Exit");
end.setBounds (750,550,200,50);
end.setFont(new Font("Arial", Font.PLAIN, 30));
end.addActionListener(this);
end.setOpaque(false);
end.setContentAreaFilled(false);
add(end);
revalidate();
repaint();
}
This one is the drawing function:
private void paint (int x, int xx, int y, int yy, Graphics g)
{
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g.drawLine(x,y,xx,yy);
Line2D lin = new Line2D.Float(100, 100, 250, 260);
g2.draw(lin);
}
Please dont just post a link with some code, Im not skilled enough to understand it
Well that is how you learn. You can't expect us to debug your code and rewrite it every time you have a little problem. If you are not willing to learn by playing with working examples, then I'm not sure how we can help you.
I need to add the line on the JLabel
Then you need to override the painting code for the JLabel. You should never invoke a painting method directly because the painting will be lost the next time Swing determines the component needs to be repainted.
So start by learning how to do painting properly. There is a working example in the Swing tutorial on Custom Painting. The first example just draws a string on a panel but it will be easy enough for you do just draw a line on the label. It is a single statement you add to the paintComponent() method.
The real question is why are you trying to draw this line on a label? I'm sure there is a better solution if we understand the real requirement. You should not be hardcoding the location/size of the line since you don't know how big the label will be.
I have a little problem, I need to add a ToolTipText to JPanel. How should I do this?
I want to have a tooltip when I have mouse over the circle.
This is part of my code.
JPanel component1 = new JPanel();
JPanel component11 = new JPanel();
okno.add(component1,"align left,cell 0 0, h 75!, grow,wrap");
component1.setLayout(new MigLayout("","[][grow][grow]", "[grow]"));
component1.add((okno.add(creLab("Kraj", i, czcionka, etykietki))),"left align, cell 0 0");
component1.add(t1,"cell 1 0,grow");
//component1.add(new circle1(),"right align, cell 2 0,h 50!, w 53!, gapleft 50, wrap");
component1.add(component11," right align, cell 2 0, h 30!, gapleft 300, wrap");
component11.setLayout(new MigLayout("","[]","[]"));
component11.add(new circle1(),"cell 0 0,h 50!, w 50!, dock north");
component11.setToolTipText("<html>W polu obok wpisz kraj pochodzenia towaru</html>");
I add also code of circle1:
class circle1 extends Applet{
public void paint(Graphics g){
setForeground(Color.yellow);
g.drawOval(0, 0, 50, 50);
g.fillOval(0, 0, 50, 50);
g.setColor(Color.black);
g.drawString("Jak", 14, 14);
g.drawString("wpisac", 3, 28);
g.setColor(Color.red);
g.drawString("kraj?", 14, 42);
//g.drawString(arg0, arg1, arg2)
}
}
Take a look at JComponent#getToolTipText(MouseEvent)
This will allow you to determine what text to return based on the location of the mouse.
It's difficult to determine for your code snippet, exactly where the circle is been drawen, but I would avoid drawing directly to the surface of the applet, but instead use a custom component (like a JPanel) instead (overriding its paintComponent method). This I would then either add to the applet or to the control panel.
This way your going to avoid issues with the mouse events been consumed
I would also take a look at Ellipse2D, which can be used to determine if the ellipse contains a given point
The first thing is to identify when the mouse is inside the circle. To do that you could verify the mouse position on a mouseMotionlister according to the circle area
http://www.java2s.com/Code/JavaAPI/javax.swing/JPaneladdMouseMotionListenerMouseMotionListenerlis.htm
Once you identify this situation you could proceed to change the tooltip
See Playing With Shapes. You can create a JLabel with a ShapeIcon. Then you just use the setToolTipText() method of the JLabel. You can then add the label to the panel like any other component.
Now that you can use a component to represent a Shape there is no need to do custom painting. Just create a panel add add components to the panel. You can also create JLabels for all your text strings.
Don't do custom painting, unless you have a good reason to do so.
Is there a friendlier way to get an instance of FontMetrics than
FontMetrics fm = Graphics.getFontMetrics(Font);
I hate this way because of the following example:
If you want to create in a game a menu and you want all the menuitems in the center of the screen you need fontmetrics. But, mostly, menuitems are clickable. So I create an array of Rectangles and all the rectangles fits around the items, so when the mouse is pressed, I can simply use
for (int i = 0; i < rects.length; i++)
if (rects[i].contains(mouseX, mouseY)) { ... }
But to create the rects I also need FontMetrics for their coordinates. So this mean that I have to construct all my rectangles in the paint-method of my menu.
So I want a way to get the FontMetrics so I can construct the Rectangles in a method called by the constructor.
For me the easiest way was to:
Font font = new Font("Helvetica",Font.PLAIN,12);
Canvas c = new Canvas();
FontMetrics fm = c.getFontMetrics(font);
Benefits:
If you call c.getGraphics() it will return null (thus there is no graphics object)
This (canvas) will also work in headless mode.
Now you can easily get height and width...
The really correct answer is to use Toolkit.
Font font = new Font("Courier New", Font.PLAIN, 14);
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
Once the background component, i.e. whatever is behind your menu, has been rendered, it has a Graphics object that you can use to get the metrics for a given font, just once.
You certainly don't want to be doing this in the paint method, which should be as lightweight as possible. I'd hang this code on a listener that gets called when the component is first rendered. It can store the resulting FontMetrics object somewhere where you can later access it, either in a paint method for drawing those menu item boxes.
Rather than determining the measurements of your menu graphics at the last moment, i.e. when painting, it might be a good idea instead to create some components to represent your menu. You can place those components on the Glass Pane more info here so they'll float above everything else, and you'll have the added bonus that those components are all capable of accepting mouse clicks and firing listener events on them, and since they only capture events on their own geometry you don't even have to figure out which part of menu was hit by the click, if at all.
Another advantage of using components here is that you may entirely get around the requirement for fiddling with font metrics. There are ready-made menu items, or you could just use JLabels, and you can specify their alignment, you can use a LayoutManager to size the boxes to the width of the biggest label, and so forth.
Assuming the menu text is fixed, you could pre-draw the text to a BufferedImage with alpha transparency and make your calculations then. Then, when you need the menu text, just draw the image.
You'll still have to do some offset calculations to centre the image (assuming the panel size can change), but these should be relatively lightweight.
I think this is a good solution
private static HashMap<Font, FontMetrics> fontmetrics = new HashMap<Font, FontMetrics>();
public static FontMetrics getFontMetrics(Font font)
{
if (fontmetrics.containsKey(font))
{
return fontmetrics.get(font);
}
FontMetrics fm = createFontMetrics(font);
fontmetrics.put(font, fm);
return fm;
}
private static FontMetrics createFontMetrics(Font font)
{
BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics g = bi.getGraphics();
FontMetrics fm = g.getFontMetrics(font);
g.dispose();
bi = null;
return fm;
}
Adding to what Lonzak said, how about this:
public static FontMetrics getFontMetrics(Font font){
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration config = gd.getDefaultConfiguration();
Canvas c = new Canvas(config);
return c.getFontMetrics(font);
}
You could store the 'config' variable as a static variable so it is constructed once in some utility font class that contains other font related information for your game/development environment. I guess you could also do this with the canvas variable.
Updated recommendation. FontMetrics is deprecated. Use LineMetrics instead.
String text = "some string";
FontRenderContext frc = new FontRenderContext(font.getTransform(), true, true);
LineMetrics lm = font.getLineMetrics(text, frc);
However, some methods such as SwingUtilities.computeStringWidth require a FontMetrics instance. Another option is to compute the bounds of the String.
Rectangle2D bounds = font.getStringBounds(text, frc);
Then the width and height may be obtained from the bounds.