Java / Swing how to deal with different screen DPI and density settings? - java

Hi everyone I've recently come across the problem that now days some monitors have different DPI settings, some monitors are much more dense such as, for example, Apple's Retina displays.
How would one compensate for the different DPI settings and densities on various monitors with Java? Is it possible to scale the controls to be the same size on any monitor. I know when programming for Android all measurements are in "DP" and you can specify different images for three different display densities.
Is there a way I could choose different images using Java / Swing for different display densities so my application doesn't look blurry on higher density monitors? Or is this not that important or possible? I know Chrome currently doesn't take into account DPI but Internet Explorer and other apps do.
Thank you for your help, I'm a long time Java developer I've just never thought about DPI before and was wondering how I might go about it :)

First, trust the platform's Look & Feel designers to choose sensible default sizes for text and controls. Then, avoid the temptation to frustrate those defaults. Here are some heuristics:
Use layouts; when contemplating absolute positioning, consider a custom layout.
Respect the preferred size of components.
Avoid non-resizable components.
In animation, scale graphics to the size of the enclosing Window.
Test across a range of platforms, using emulation as required.

First thanks trashgod for the useful links you provided for they were very helpful.
Below I have listed some specifics on how to deal with issues concerning DPI changes that I have run into.
Font Issues:
If the font is not fitting in the component then look at Component Sizing Issues below for often the component is not sizing to what is inside. Otherwise in a JLabel you can apply HTML tags to automatically line wrap the text (there are other solutions but this works for me).
If the component's text is naturally the default font, size, and style (Tahoma 11 plain) then everything is automatically done for you. Know that user574171 gave pictures showing different font and layout managers issues and strengths in his link to http://kynosarges.org/GuiDpiScaling.html in his answer to How to set the DPI of Java Swing apps on Windows/Linux?. This question here is asking about the "Swing" issues but his link is useful in also comparing WPF, JavaFX, and Swing layouts with a basic components and different fonts. You can see there the "Swing a" font is the best, that is Tahoma 11 Plain. However it only works if the component is naturally the default font, size, and style (Tahoma 11 Plain). This means bolding, underlining, sizing, and a different font for the text will throw off the auto sizing for different DPI.
If the text is the default font (Tahoma) but bolded, underlined, or a different size then the text does not scale for different DPI's. To fix the scaling for different DPI's then check the box labeled "Derive the font from the default font." The "Relative" option will take the default DPI scaling font and apply your relative changes such as bigger or smaller, bold, and underlined. Also know that if the component is a JLabel then DPI scaling is automatic using HTML tags despite how you format the text (line wraps too).
If the component is not naturally the default font, style, and size (Tahoma 11 plain) or you want a different font (ie not Tahoma) then you need to associate the changes in another component that is naturally the default like a JLabel. Therefore if 125 DPI increases the font by 3 then you increase your desired font by the same.
Component Sizing Issues:
All components have a preferred, minimum, and maximum size properties but I have found that they do not play well with DPI changes. The "Respect the preferred size of components" bullet in trashgod's answer tells you all about it. To summarize let the default size of the component take care of itself for it will try to size to whatever is inside.
If the component can use the default sizes then DPI handling can be done for you. Just as trashgod's said "First, trust the platform's Look & Feel designers to choose sensible default sizes for text and controls. Then, avoid the temptation to frustrate those defaults." Again, the component of the default size usually sizes to what is inside (or acts re-sizable). From my own experience when I set re-sizable components I do it via "Horizontal Resizable" or "Vertical Resizable" for I have found it works very well with DPI changes as well as window resizing.
If the default size does of the component does not work then first try looking below at some specific issues I found. Alternatively try scaling the component according to changes in a component that does get scaled right. IE, apply ratio of what you expected the scaling component to be at 100% DPI and the change to the new DPI. Examples of components that need to be scaled to other component changes would be one that it's text is filled by code or it is an image.
As a last resort use a JScrollPane to allow the user to scroll to see the whole thing. If the window/dialog is fairly large then a JScrollPane is a great idea because if the DPI is higher for the user's eyesight then the window often is bigger than the screen.
Note:
I have found that encapsulating groups of components in a JPanel helps swing handle DPI changes a lot better.
JTextField Size Issues:
Set the columns property and nothing else but the text if you wish. The columns property is the number of letters or numbers you want to show. Typically used when allowing the user to enter a number. Know that the columns property works great for multiple platforms.
JSpinner Size Issues:
Set the model to be accurate, valid, and default values. Therefore the auto-sizing of the component is done for the max value and works great for DPI changes and other platforms.
Table Row Height Issue:
Table row height does not change with higher DPI. I encountered this issue and it seems to be due to me trying to be compatible with older versions of Java. To fix multiply the change in font size ratio or change in JLabel height ratio due to the DPI change.

Use JRE 9 (or greater).
This is because the Java runtime (older versions) declared themselves to be "DPI-aware" but didn't really supported it for AWT and Swing. Java applications were sized and rendered based on pixels rather than being properly scaled, this included HiDPI displays.
Anyways, this has been recently solved.
See the issue JEP 263: HiDPI Graphics on Windows and Linux
and the upgrade.
So, increasing the font size does not work (because it does not increase the rest of the things); the jvm argument -Dsun.java2d.dpiaware=false does not work (because it is not really supported); and the manifest file + registry edit (for Windows) just does not work.
Then, You need to run it on JRE 9 because it really supports this feature.

By using
System.setProperty( "sun.java2d.uiScale", "1.0" );
in your java code, ui scaling should be deactivated.

Related

How do you utilize SWT's Hi-DPI support for widget sizing?

Does SWT (or JFace) have a public convenience method for converting conventional units to their scaled counterparts? I've found mention of a DPIUtil class but that's part of an internal namespace
If there's not a convenience method available, then is there a reliable way to access the zoom level? I see there's Device#getDeviceZoom() but that is protected. There is Device#getDPI() which is public so it might be useful. Does that take scaling into consideration, or is it naïve and just declares that DPI is 96 for everything?
I'm applying default sizing hints to some panels and I'd like them to take the monitor scaling setting into consideration. E.g., Say on a regular display I want the default to be 300px, but at 150% scaling I want to calculate it to be 450px. The calculation is obviously simple but I need the multiplier.
NOTE: This is related but different from my previous question How do you utilize SWT's Hi-DPI support for icons? because SWT provides classes to specifically handle this with images.
I haven't found anything other than DPIUtil for determining the scale (zoom) factor.
But you don't normally need this information. Specifying a size of 300px will be automatically scaled to 450px by SWT on a 150 scaled device (and any 150 scaled image you provide will be used). I have an iMac with two screens - a 5k screen scaled at 200 and a 2.5k screen not scaled - SWT apps appear the same size on both.
The scaling is actually done in the OS rather than SWT (at least that is how it works on macOS). The OS scales up the sizes, renders fonts at the higher resolution and uses the high resolution images if available. So programs don't need to do anything other than provide hi-res images.
This way even old programs that don't know about zoomed displays still appear at a sensible size.

Low quality icon in taskbar of a Stage. JavaFX

Why Stage icon so low quality? The original image is much better.
How to fix it?
I used this code to setting image as stage icon:
stage.getIcons().add(new Image("/res/app_icon.png"));
Screenshot:
Original icon:
Update
Unfortunately the implementation of the icon chooser in JavaFX 8 does not always choose the best icon size for the application from the list of available icons.
See:
JDK-8091186 Windows 7 taskbar icon is blurry.
JDK-8087459 Ugly icon in Windows task bar.
The issues are (currently) scheduled to be addressed in Java 9.
Some comments on those issues are:
Indeed, Glass currently supports assigning only one icon for a window, it won't allow one to assign a set of differently sized icons. This feature needs to be implemented in Glass.
--
I have noticed that when setting multiple icons, only the last one in the list returned by getIcons() is used. It doesn't matter if other icons, with better resolutions are in the list.
If you provide a list of icons of different sizes, try putting the size you expect to be used most last in the list (perhaps order the list from smallest to largest or place a 48x48 icon, which is the size used in the quick launch area on Windows 7, as the last or only element in the list). Unfortunately, I do not have access to a Windows machine to test the best icon size for that platform.
Icon Guidelines (but be aware of the information in the update above):
Provide a set of high quality icons of different sizes to the list
returned from
stage.getIcons().
The best fit size will be chosen by the runtime.
Use standard icon
sizes which most operating systems can work with without further scaling (for
instance item size is a power of two or 48x48).
Use high quality source images (the original source image in your question seems a bit blurry to me).
For very small icons, sometimes a pure scale of a larger icon to a smaller size is not best. Sometimes it is best for the icon designer to clean up and remove intricate details from the small icon, which might be OK in a larger icon - see the discussion on icon size on Pushing Pixels to help understand why.
Info on what icon sizes are used by what OSes is in this icon handbook.
stage.getIcons().addAll(
new Image("/res/app_icon64x64.png"),
new Image("/res/app_icon32x32.png"),
new Image("/res/app_icon16x16.png")
);
A great source of high quality icons in a variety of sizes is http://www.iconarchive.com, for example this refresh icon.

How to force fonts to be same size in px for different resolutions

I am working on a Java Swing application that needs to be run on a significant smaller screen size than what it was developed for.
Unfortunately, the layout gets heavily distorted, mainly because the fonts are displaying relatively bigger.
Most of the font sizes are set using embedded html in JLabels and JButtons.
How can I force the font sizes in Swing to be equal in Pixels across different resolutions?
Start with Toolkit(.getToolKit()).getScreenResolution() as per the javadocs: "Returns the screen resolution in dots-per-inch". Next, create a FontMetrics Object for your screen and Font (there are multiple ways to do this). Then you can use FontMetrics.getHeight() with the screen resolution to figure out how big it will display. You can use Font.deriveFont(float) until the size is the same or as similar as possible as what you want

Java Layout difference on identical screen resolutions

I have two screens that are using the same resolution (1280x1024), but one of them has a refresh rate of 59.9 HZ, and the other one has a refresh rate of 75 HZ. I have a full software that's developed using Java (and running under OpenSuse 10.0). The problem is that the Jtables in this software are being displayed differently on both screens: They're being displayed perfectly on one screen, and on the other screen, the widths of the columns are increasing, which is hiding some of the columns.
The other difference that I was able to get from both screens, is when I used the Toolkit.getDefaultToolkit().getScreenResolution(), which gave me 93 on the "good display" screen and 95 on the "bad display" screen.
My question is, why would two screens that are using the same resolution, display GUI components differently? Does it have to do anything with the refresh rate or with the DPI of the screen?
Check your available fonts. The logical font names (serif, sansserif, monospaced, dialog, dialoginput) are created from the fonts available on your machine.
Per the Font javadoc:
Typically, each logical font name maps to several physical fonts in order to cover a large range of characters.
So, if the available fonts do not match, Java may be substituting different physical font characters. That can cause the behavior you are describing.

Create image file with java

I was trying to create a rectangular image where will be 2 things.
A label (100% width and 20% height)
A Text Area (100% width and 80% height)
So lablel will be at top and text area will be at bottom
so I tried to create a JFrame and place both components there. Then I am creating its image with JFrame.createImage(width, height) and in last I used ImageIO.write(). but problem was, There was some extra blank space around the components in Image. When I tried to set its bound then it create an exact image but this image works perfectly on Windows but it doesn't work on Mac. Any idea why?
Is there also another easy way where I can achieve this. I've spent 2 days but couldn't found any solution.
Please advice
Thanks in adnvace
Rendering using Swing components is very versatile, but the user interface delegate for each component varies by platform. Similarly, font metrics also vary by platform and vendor. If you need very fine control over the placement of text, you can access the graphics context's font metrics as seen in this example, and you can mitigate aliasing as shown here.

Categories

Resources