I need to implement font size switching in my app. But when I increase font's size RadioButtons remain same size and on small screen with high resolution my customer just can't hit it easily. Is there a way to resize RadioButton's round thing programmatically without diging into L&F and redrawing Icons manually (it's complicated since app targets multiple platforms with different UIs and each of them must have 7 icons).
Perfect solution could look like this:
Extraction of native UI icon.
Resizing it
Setting resized icon as component's icon.
How to implement step 1? Is it possible?
EDIT: this is what i tried so far
public class IconImageSaver extends JFrame{
public IconImageSaver() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(0,0,100,100);
setVisible(true);
JRadioButton rad1 = new JRadioButton();
rad1.setBounds(10,10,40,40);
add(rad1);
Icon icon = UIManager.getIcon("RadioButton.icon");//(1) trying to get icon
int w = icon.getIconWidth(),h = icon.getIconHeight();
Image i = rad1.createImage(w, h);
Image i2 = rad1.createImage(w,h);
Graphics g = i.getGraphics();
Graphics g2 = i2.getGraphics();
g.setColor(Color.CYAN);
g.fillRect(0, 0, w, h);
rad1.setIcon(new ImageIcon(i));//setting icons
g2.setColor(Color.RED);
g2.fillRect(0, 0, w, h);
rad1.setPressedIcon(new ImageIcon(i2));//setting icons
}
public static void main(String[] args) {
new IconImageSaver();
}
}
At position (1) i'm trying to get icon image, but it returns only background color.
Can't understand why.
Setting icons for various states works as intended.
Some L&Fs (e.g. Nimbus, Aqua) support a large JComponent.sizeVariant, as discussed in Resizing a Component and Using Client Properties.
Addendum: I must use pure native L&F.
The rendering of a JRadioButton is determined by its associated ButtonUI delegate. The internals of delegates supplied by the native L&F are generally inaccessible and rely on host platform APIs. You have to use the available feature(s) of the user's chosen L&F or supply your own. If you can explain more about the underlying problem, it may help to suggest better alternatives.
Addendum: Absent developing a complete L&F, it may be possible to work with the radio button's parent, JToggleButton. Such buttons work well in a ButtonGroup, as shown here, and they can be decorated arbitrarily, as outlined here.
is very L&F sensitive, by default you can
use proper L&F (only Nimbus has implemented auto_whatever) but we talking about Custom L&F
to override keys in UIManager, but these keys can, could (be presented or with value) or missing in compare with another L&F
create own (J)Component, to overide important methods and
a) put to the UIManger (one def. valid for whole JVM instace)
b) add to the selected, desired or part of (J)Components, e.i. .... in the visible GUI
notice for (I need to implement font size switching in my app) there is very important to test if is required to change (we'll talking about) Font or FontUIResources, part of implemented methods for part of (J)Components to pretty ignore Font and required FontUIResources, now not sure if vice versa too
IMPORTANT NOTE: This was only tested with the default 'Metal' look and feel. I do not guarantee that this will work for any other look and feel. Also I am not entirely sure how it works because it is admittedly a bit of a hack.
I was able to solve this a little bit differently.
I Was scaling my font globally using the UIManager defaults and so I wanted my radio buttons to scale with the font.
I found I could do this by extracting the Icon for the radio button from the UIManager, buffering them, re-sizing them and then deriving a new icon from the graphics of the buffered icons.
I ended up with this function:
public static void scaleRadioButtonIcon(JRadioButton rb){
boolean previousState = rb.isSelected();
rb.setSelected(false);
FontMetrics boxFontMetrics = rb.getFontMetrics(rb.getFont());
Icon radioIcon = UIManager.getIcon("RadioButton.icon");
BufferedImage radioImage = new BufferedImage(
radioIcon.getIconWidth(), radioIcon.getIconHeight(),BufferedImage.TYPE_INT_ARGB
);
Graphics graphics = radioImage.createGraphics();
try{
radioIcon.paintIcon(rb, graphics, 0, 0);
}finally{
graphics.dispose();
}
ImageIcon newRadioImage = new ImageIcon(radioImage);
Image finalRadioImage = newRadioImage.getImage().getScaledInstance(
boxFontMetrics.getHeight(), boxFontMetrics.getHeight(), Image.SCALE_SMOOTH
);
rb.setSelected(true);
Icon selectedRadioIcon = UIManager.getIcon("RadioButton.icon");
BufferedImage selectedRadioImage = new BufferedImage(
selectedRadioIcon.getIconWidth(), selectedRadioIcon.getIconHeight(),BufferedImage.TYPE_INT_ARGB
);
Graphics selectedGraphics = selectedRadioImage.createGraphics();
try{
selectedRadioIcon.paintIcon(rb, selectedGraphics, 0, 0);
}finally{
selectedGraphics.dispose();
}
ImageIcon newSelectedRadioImage = new ImageIcon(selectedRadioImage);
Image selectedFinalRadioImage = newSelectedRadioImage.getImage().getScaledInstance(
boxFontMetrics.getHeight(), boxFontMetrics.getHeight(), Image.SCALE_SMOOTH
);
rb.setSelected(previousState);
rb.setIcon(new ImageIcon(finalRadioImage));
rb.setSelectedIcon(new ImageIcon(selectedFinalRadioImage));
}
What it does is get the size of the font from the radiobuttons's font metrics. Using those metrics, it derives a new icon based on the icon found in the 'Look and Feel' and sizing it to the font's height.
One thing that I am not able to explain is how the icon for the radiobutton coming out of the UIManager changes to the 'selected' icon when I am accessing the same property to get both icons.
I start by saving the state of the control so I can restore it at the end. This is done because in order for the icons to be set properly, the state needs to be unchecked when you first request the icon from the UIManager and then it will need to be checked when you request the icon the second time to get the 'selected' icon.
Again, I am not entirely sure how the UIManager works or why the icon changes when we call the same property just by setting the 'selected' value of a single radiobutton, but that is what is required in order to get both the necessary icons.
If you did not want to use the font to size the controls, you could easily just pass in the height and width as parameters and use them instead of the font's height when setting the buffered image size.
I might mention that this same methodology works with checkboxes
Related
I have JFrame with a JTextArea inside of it.
Font font = new Font("monospaced", Font.PLAIN, 14);
textarea.setFont(font);
Since the font is monospaced, all characters are the same width and height.
I'd like to know what this width and height is in pixels.
For this, I could use font.getStringBounds but I have no Graphics context to pass to it. frame.getGraphics() returns null.
How can I find the size of a character? Can it be done without a Graphics instance? I don't want an instance of it anyway. I just want to know how big my characters are.
You can use JFrame#getFontMetrics since one of JFrame's Superclass is Component.
If this does not work, you can also use BufferedImage to get a Graphics object:
BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
You can use the image object to get an instance of Graphics.
FYI, I am using a JFrame/JTextarea to render a text based game, so I'll use this info for scaling the text and getting the dimensions of the window in units of characters
It's probably not the best approach, it would be better to simply use JTextArea#setColumns and JTextArea#setRows which will use the font based information to make determinations about it's preferred size automatically
You can then make use of the LayoutManager APIs and simply call pack on the JFrame which will pack the window around the contents, based on it's preferred size
This will also affect the preferred size of JScrollPane
We've a Java application with a trayicon (SystemTray) and a popup menu (PopupMenu) that worked nicely on all platforms.
On a new Surface 4 Pro we've a problem as the size of the menu is amazingly small. Looks as not noticing it's a 'retina'/high definition display.
Is there an easy way to fix this ?
Looks like you may have to resort to setting the menu UI.
See : https://docs.oracle.com/javase/8/docs/api/javax/swing/JPopupMenu.html#setUI-javax.swing.plaf.PopupMenuUI-
Of course that means you need some way to determine that you're running on the Surface Pro in the first place.
You could try figuring out the current font size and dimensions used by the menu UI and compare that with the screen dimensions from Toolkit.getDefaultToolkit().getScreenSize()
As a general solution, which is not tied up to specific devices, you could detect screen resolution in dots-per-inch via:
java.awt.Toolkit.getDefaultToolkit().getScreenResolution()
This will return screen resolution in DPI (dots-per-inch). The bigger this value - the smaller the textual fonts will get for a specific font size.
Use the DPI to set an acceptable (or maximal) DPI settings and calculate the adjustment ratio.
You can adjust either by replacing the UI delegates fonts (for an application-wide effect)
OR, if the issue is specific for the system-tray, by deriving a larger font only for SystemTray's MenuItems:
java.awt.Font defaultFont = java.awt.Font.decode(null); // default font
float adjustmentRatio = 1.0f; // Calculate this based on your metrics
float newFontSize = defaultFont.getSize() * adjustmentRatio ;
java.awt.Font derivedFont = defaultFont.deriveFont(newFontSize);
// PopupMenu with the adjusted font size:
MenuItem item = new MenuItem("Menu Item");
item.setFont(derivedFont);
popupMenu.add(item);
I have a setup that usually works, but I'm finding it a bit of a pain at the moment.
I have a JDialog (formerly a JFrame, but I've changed the UI flow recently to remove redundant JFrames) set to BorderLayout that contains three main JPanels, header, content and footer.
The header JPanel has a background image that is loading fine. However, I'm calling all manner of setMinimumSize / setPreferredSize / etc (I even tried setSize and setBounds out of desperation) and the JPanel isn't being sized correctly. The layout for that component is BoxLayout, and the children sit comfortably within it, but that shouldn't affect what I'm trying to do with the header panel itself.
The only thing that works is setting a size on the parent JDialog. But I don't want to do that / from what I've read it seems like bad design. I'd also have to maths out the potential width / height of all the children, add the margins, etc.
Advice I am not looking for: "use a different LayoutManager."
I want to understand if there's a reason for the child components not being respected.
I can provide code, but isolating a small, runnable segment is difficult given the amount of interlocked systems I'm juggling. Here is the relevant snippet to set the size of the JPanel. The image dimensions are 480 * 96.
public JPanelThemed(BufferedImage image) {
super();
this.backgroundImage = image;
setAllSimilarConstraints(new Dimension(image.getWidth(), image.getHeight()));
setOpaque(false);
}
// for use with BoxLayout as it requires explicit bounds to be set
public void setAllSimilarConstraints(Dimension dim) {
setPreferredSize(dim);
setMinimumSize(dim);
setMaximumSize(dim);
}
I would implement it a bit another way - to ensure that preferred/min/max/Size can not be changed from elsewhere:
public JPanelThemed(BufferedImage image) {
this.backgroundImage = image;
setOpaque(false);
}
public Dimension getPreferredSize() {
return new Dimension(this.backgroundImage.getWidth(), this.backgroundImage.getHeight());
}
If I don't define dimensions, how do I ensure the whole background I'm painting is displayed?
You could use a JLabel to display the ImageIcon. The size of the label will be the size of the image. You then set the layout manager of the label so you can add components to it.
Edit:
do you know why it's commonly-recommended to subclass JPanel when painting backgrounds in Java
When you use a JPanel the size of the panel is based on the components added to the panel and the layout manager you are using. Your custom painting code then needs to paint the image the way your want. That is you can paint the image from (0, 0), or you can center the image on the panel, you can scale the image to make it fit the pane, but the image in no way controls the size of the panel, unless you override the getPreferredSize() method of the panel to use custom code to base the size of the larger of the components or the image. So you are in full control.
When you use the JLabel approach suggested here, the size of the label is always the size of the image. Then components will be positioned based on the layout manager but can be truncated if the image is smaller than the space required by the components.
So based on your requirement that you want to make sure the entire image is displayed, I suggested the JLabel approach, since you don't need to write any custom code. I find this a simple approach when using popup non-resizable dialogs to display a background image and a few components and buttons to close the dialog.
You can also check out Background Panel which provides these common painting features in a reusable class.
I'm writign a POS system(point of sale) for fun. A typical feature of a POS system is buttons that have different background colors for different products. But I don't mean the background behind the button's image, i mean like in this random picture I got off google images:
Edit: Note I changed the look and feel to the system one
I need to do something like THAT with my buttons. I know a few ways this COULD be possible:
Creating a look and feel for my buttons?
Completely overriding the paintComponenet method in my JButtons(But that's a pretty pathetic way to do it... and the border + text isn't drawn when I override that... obviously a bad idea)
Using button.setContentAreaFilled(false);, and putting a JPanel of the same size as the button behind the button.
I don't really know how to create my own look and feel, and it sounds like a pain, especially just for 1 button, and I heard something about breaking the look and feel which scared me away from that idea. The 3rd way sounds plausible, and not extremely difficult, but I what is the very best way to do what I am trying to do?
Right now my code for creating butons is this:
JButton b = new JButton(text);
b.addActionListener(this);
b.setFont(Main.f);
b.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
buttons.add(b);
return b;
I've messed around with this code(Mostly for fun, not practicality):
JButton b = new JButton(text){
public void paintComponent(Graphics g){
super.paintComponent(g);
Color bg = getBackground();
int borderchange = -50;
g.setColor(new Color(
Math.max(0,bg.getRed()+borderchange),
Math.max(bg.getGreen()+borderchange,0),
Math.max(bg.getBlue()+borderchange,0)));
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(getBackground());
int border = 4;
g.fillRect(border,border,getWidth()-border,getHeight()-border);
g.setColor(Color.BLACK);
g.setFont(getFont());
g.drawString(getText(),getWidth()/2,getHeight()/2);
}
};
Creating a look and feel for my buttons?
Look and Feel is something like as theme,
by default this ColorScheme has a one, two, three Colors, this theme is applied for all Swing JComponents,
then all Swing JComponents have got the same Color, ColorScheme
not something that you looking for
Completely overriding the paintComponenet method in my JButtons(But
that's a pretty pathetic way to do it... and the border + text isn't
drawn when I override that... obviously a bad idea)
JButton has array of Colors
you can to override paintComponent, fills whole area, Rectangle with one Color (not something that you looking for) or to use GradientPaint
you can to override BasicButtonUI
override proper key in UIManager and put there arrays of Colors
Using button.setContentAreaFilled(false);, and putting a JPanel of
the same size as the button behind the button.
this could be easiest of ways, prepare Icons (or download set of Icons)
use proper methods, implemented (Mouse & Key & KeyBindings) events
in the JButton API
override ButtonModel (by using ChangeListener)
Have you tried:
yourButton.setBackground(COLOR)
?
This is pretty simple, I come from a swing/awt background.
I'm just wondering what the proper way to set the background color for a SWT widget is?
I've been trying:
widget.setBackground( );
Except I have no idea how to create the color Object in SWT?
For standard colors (including common colors and default colors used by the operating system) Use Display.getSystemColor(int), and pass in the SWT.COLOR_* constant for the color you want.
Display display = Display.getCurrent();
Color blue = display.getSystemColor(SWT.COLOR_BLUE);
Color listBackground = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
Note that you do not need to dispose these colors because SWT created them.
To create a color, try this:
Device device = Display.getCurrent ();
Color red = new Color (device, 255, 0, 0);
Remember that in SWT you must explicitly dispose any resources that you create when you are done with them. This includes widgets, fonts, colors, images, displays, printers, and GCs. If you do not dispose these resources, eventually your application will reach the resource limit of your operating system and the application will cease to run.
See also: SWT: Managing Operating System Resources