Cross Platform Layout Relative to Background Image - java

I've created a super class (ImagePanel) which extends JPanel and paints an image as the background. In my ImagePanel subclass I'm using GroupLayout (via the NetBeans GUI Designer) to overlay the panel with JTextFields which are aligned with the underlying image.
This approach works as intended on a single platform; however, when I run the application on a different platform, the JTextFields are resized/moved based on the Look and Feel. If I set the layout manager to null the JTextFields remain in the appropriate position, but I lose the resizing of the JTextFields. Ideally, I would like to keep the position of the JTextFields, but have them sized according to the L&F? How can I approach this differently?
/**
* Extends JPanel adding the ability to paint a background image.
*/
public class ImagePanel extends JPanel implements Serializable
{
public static final String PROP_IMAGEFILE = "imageFile";
//~--- fields -------------------------------------------------------------
private ImageIcon imageIcon;
private String imageFile;
/**
* Constructs a new ImagePanel.
*/
public ImagePanel()
{
// required by Beans specification.
}
/**
* Get the path to the image file used to paint the background.
*
* #return the path.
*/
public String getImageFile()
{
return imageFile;
}
/**
* Set the path to the image file used to paint the background.
*
* #param imageFile the image file path.
*/
public void setImageFile(String imageFile)
{
String oldImageFile = this.imageFile;
this.imageFile = imageFile;
imageIcon = new ImageIcon(getClass().getResource(imageFile));
firePropertyChange(PROP_IMAGEFILE, oldImageFile, imageFile);
}
/**
* Overridden to draw image background image.
*/
#Override
public void paintComponent(Graphics g)
{
/* Draw image on the panel */
super.paintComponent(g);
if (imageIcon != null)
{
/* create image icon to get image */
Image image = imageIcon.getImage();
if (image != null)
{
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
}
}
}
On Windows:
On Linux:

I am not sure how well compound layouts will work in this case and it might very well be one of the 1% of cases where you do need a null layout (though that should be avoided whenever humanly possible). as mentioned, miglayout might work with with a little playing around, but what you might have to do is hard code ratio values rather than position values. Calculate the percentage of the image that represents the location and size of each component and, after the image has been drawn, and use those ratio values to programatically lay out your components.
A new layoutmanager could be written to accomplish the same thing (and would probably be preferred to the null layout method above. The add(); method could take 5 variables (component, ratio value for location x, ratio value for location y, size value for locaton x, size value for locaton y);. I am not well versed as to how to write a layout manager, but it is an option.

Related

Will setShape() clear the background of a JFrame?

I would like to write a program in java...
I want to set the shape of the window(a JFrame) to a set of PNG Images(with a transparent background).
(Actually, I would like to make the window change its shape continual, and it look like an animation!)
And, I read images from files,save them to a array, then, I use class GeneralPath to get the area of my animated character(in png images), save it to areaArray.
After all things are done, I start the paint thread. It works well...But sometimes the window would flash(ah...I mean a flicker happened, but the background color I saw when flashing is transparent, I could saw my desktop wallpaper!).
I don't want to see the flicker/flash again, would someone help me? Thanks!
P.S. : Sorry for my poor English!
public class JCustomFrame extends JFrame implements Runnable
{
private final int max_frame=18; //Here is the max numbers of my png images
private BufferedImage[] BufImageArray; //The array to save all the png images
private BufferedImage nowImage; //Save the image to be drawn in this turn
private int counter; //Indicate which png image to be drawn
private Area[] areaArray; //Save the shapes of the animated character in each png image
public void run()// a thread function to paint the frame continual
{
while(true){
if(counter==max_frame)counter=0;
nowImage=BufImageArray[counter];
setShape(areaArray[counter]);
repaint();
try{
Thread.sleep(100);
}catch(InterruptedException e){
System.out.println("Thread.sleep error!");
}
counter++;
}
}
public JCustomFrame()
{
super();
setUndecorated(true);
setBackground(new Color(0,0,0,0));
counter= 0;
//...some codes here
new Thread(this).start();
}
public void paint(Graphics graphic)
{
graphic.drawImage(nowImage,0,0,this);
}
}
Here is a sample code to run the program:
import javax.swing.*;
public class MainFrame
{
public static void main(String[] args)
{
JCustomFrame myFrame = new JCustomFrame();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300,400);
myFrame.setVisible(true);
return ;
}
}
I modified 2 lines above, the "png file name" and the "max_frame" to apply the new image files.
I found that if I put the same program on Windows rather than my OpenSuse, it works very well(without the flicker), here I upload all the pack of my source(include the image file).
Here my code is.
Thanks again.
==================================================================================
Thanks Andrew Thompson for suggestions.
This time, I try to delete the codes unrelated to the problem, and paste a gif to show the situation. The codes above isn't runnable, but the source code in the link works well.
P.S. The flicker/flash happened in random frame, isn't completely the same as the gif shows.
('cause I could only add a transparent panel in my gif image at a fixed sequence)
Thanks!!

Java Swing - flickering Canvas graphics

I have to write a simple Java app which can load pictures, show it in a GUI form, allow the user to apply some transformation, and show the transformed picture.
My solution is working fine, but the UI is flickering a bit, because the repaint method called too often (for example when the user scaling the image with a JSlider)
My code looks like this:
public class ImageCanvas extends Canvas
{
private BufferedImage image;
// ...
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if(image != null)
{
// I draw out the image...
}
}
public void setImage(BufferedImage image)
{
this.image = image;
this.repaint();
}
public void setRotation(double rotation)
{
this.rotation = rotation;
this.repaint();
}
public void setScale(double scaleX, double scaleY)
{
//set the scaling field, then repaint ....
}
// and so on...
}
And, of course, I have an ImageCanvas control on my main UI, and I simply call the public methods (see for example the "setRotation" method above) which repaint the canvas area. I know it's a simple question, but I don't even find a DoubleBuffered property on the Canvas...
Any help appreciated.
Double buffering is built-in for Swing (i.e. JComponent derived) classes.
If you want built-in double-buffering, you should extend JPanel rather than Canvas, and override paintComponent, not paint.
If you can use JPanel than go for it. Please make sure you are not overriding the JPanel.paint method, override JPanel.paintComponent instead.
See this link for details.
Usually graphic lags in these applications can be caused by setting a empty variable at the top of the script, then changing its value, then waiting for the repaint to update it. You could try changing the:
setRotation(double rotation);
so that it rotates the image in that method.
Just a general thing I happen to see while dealing with graphics.

making jfilechooser show image thumbnails

I wanted to create a JFileChooser with thumbnail view of image files.So I subclassed FileView and in the method which creates ImageIcon did some scaling sothat thumbnail images are shown.
However,the overall effect is that, the filechooser widget takes some time before opening a directory and showing thumbnails..In createImageIcon() below,I need to call new ImageIcon() twice-once with the image filepath and next with the resized image as constructor argument.I think this is what slows the widget .
Is there a more efficient alternative?Any suggestions/pointers most welcome.
thanks,
mark
public static void main(String[] args) {
JFileChooser chooser=new JFileChooser();
ThumbNailView thumbView=new ThumbNailView();
chooser.setFileView(thumbView);
}
class ThumbNailView extends FileView{
public Icon getIcon(File f){
Icon icon=null;
if(isImageFile(f.getPath())){
icon=createImageIcon(f.getPath(),null);
}
return icon;
}
private ImageIcon createImageIcon(String path,String description) {
if (path != null) {
ImageIcon icon=new ImageIcon(path);
Image img = icon.getImage() ;
Image newimg = img.getScaledInstance( 16, 16, java.awt.Image.SCALE_SMOOTH ) ;
return new ImageIcon(newimg);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
private boolean isImageFile(String filename){
//return true if this is image
}
I was actually surprised to see that, despite using the native look & feel in Windows, the file chooser indeed doesn't have a thumbnail view. I tried your example and you're going along the right lines, but I see how slow it was for folders with a lot of large images. The overhead is, of course, due to I/O when reading the file contents and then interpreting the image, which is unavoidable.
What's even worse, is that I found out that FileView.getIcon(File) is called a lot - before the file list is shown, when you mouse over an icon, and when the selection changes. If we don't cache the images after loading them, we'll be pointlessly reloading images all the time.
The obvious solution is to push all the image loading off onto another thread or a thread pool, and once we have our scaled-down result, put it into a temporary cache so it can be retrieved again.
I played around with Image and ImageIcon a lot and I discovered that an ImageIcon's image can be changed at any time by calling setImage(Image). What this means for us is, within getIcon(File), we can immediately return a blank or default icon, but keep a reference to it, passing it along to a worker thread that will load the image in the background and set the icon's image later when it's done (The only catch is that we must call repaint() to see the change).
For this example, I'm using an ExecutorService cached thread pool (this is the fastest way to get all images, but uses a lot of I/O) to process the image loading tasks. I'm also using a WeakHashMap as the cache, to ensure that we only hold onto the cached icons for as long as we need them. You could use another kind of Map, but you would have to manage the number of icons you hold onto, to avoid running out of memory.
package guitest;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.filechooser.FileView;
public class ThumbnailFileChooser extends JFileChooser {
/** All preview icons will be this width and height */
private static final int ICON_SIZE = 16;
/** This blank icon will be used while previews are loading */
private static final Image LOADING_IMAGE = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB);
/** Edit this to determine what file types will be previewed. */
private final Pattern imageFilePattern = Pattern.compile(".+?\\.(png|jpe?g|gif|tiff?)$", Pattern.CASE_INSENSITIVE);
/** Use a weak hash map to cache images until the next garbage collection (saves memory) */
private final Map imageCache = new WeakHashMap();
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFileChooser chooser = new ThumbnailFileChooser();
chooser.showOpenDialog(null);
System.exit(1);
}
public ThumbnailFileChooser() {
super();
}
// --- Override the other constructors as needed ---
{
// This initializer block is always executed after any constructor call.
setFileView(new ThumbnailView());
}
private class ThumbnailView extends FileView {
/** This thread pool is where the thumnnail icon loaders run */
private final ExecutorService executor = Executors.newCachedThreadPool();
public Icon getIcon(File file) {
if (!imageFilePattern.matcher(file.getName()).matches()) {
return null;
}
// Our cache makes browsing back and forth lightning-fast! :D
synchronized (imageCache) {
ImageIcon icon = imageCache.get(file);
if (icon == null) {
// Create a new icon with the default image
icon = new ImageIcon(LOADING_IMAGE);
// Add to the cache
imageCache.put(file, icon);
// Submit a new task to load the image and update the icon
executor.submit(new ThumbnailIconLoader(icon, file));
}
return icon;
}
}
}
private class ThumbnailIconLoader implements Runnable {
private final ImageIcon icon;
private final File file;
public ThumbnailIconLoader(ImageIcon i, File f) {
icon = i;
file = f;
}
public void run() {
System.out.println("Loading image: " + file);
// Load and scale the image down, then replace the icon's old image with the new one.
ImageIcon newIcon = new ImageIcon(file.getAbsolutePath());
Image img = newIcon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_SMOOTH);
icon.setImage(img);
// Repaint the dialog so we see the new icon.
SwingUtilities.invokeLater(new Runnable() {public void run() {repaint();}});
}
}
}
Known issues:
1) We don't maintain the image's aspect ratio when scaling. Doing so could result in icons with strange dimensions that will break the alignment of the list view. The solution is probably to create a new BufferedImage that is 16x16 and render the scaled image on top of it, centered. You can implement that if you wish!
2) If a file is not an image, or is corrupted, no icon will be shown at all. It looks like the program only detects this error while rendering the image, not when we load or scale it, so we can't detect this in advance. However, we might detect it if we fix issue 1.
Use fileDialog instead of JfileChooser for choising the image:
FileDialog fd = new FileDialog(frame, "Test", FileDialog.LOAD);
String Image_path
fd.setVisible(true);
name = fd.getDirectory() + fd.getFile();
image_path=name;
ImageIcon icon= new ImageIcon(name);
icon.setImage(icon.getImage().getScaledInstance(jLabel2.getWidth(),jLabel2.getHeight() , Image.SCALE_DEFAULT));
jLabel2.setIcon(icon);
You could use a default icon for each fileand load the actual icons in another thread (perhaps using a SwingWorker?). As the icons are loaded the SwingWorker could call back and update the FileView.
Not sure if a single SwingWorker would do the trick, or whether it would be better to use one for each icon being loaded.

Transparent VerticalFieldManager background

My goal is to have a splash screen with an animated gif 80 pixels below center of the screen. Loading the screen's background image and animated gif is easy, as is positioning the animated gif 80px below center. My problem is that the VerticalFieldManager background (which contains the animated gif field) is filled with all white (by default). I can set the manager's background color, but the screen's background image isn't just one solid color.
public final class SplashScreen extends MainScreen {
public SplashScreen() {
// create and load the background image BitmapField
this.add(backgroundImage);
// create and load the progress bar
BitmapField progressBar = new BitmapField(progressBarImage, Field.FIELD_HCENTER | Field.USE_ALL_WIDTH | Field.NON_FOCUSABLE);
VerticalFieldManager manager = new VerticalFieldManager(Field.USE_ALL_WIDTH | Field.FIELD_HCENTER) {
protected void sublayout(int maxWidth, int maxHeight) {
// positioning code...
}
};
manager.add(progressBar);
this.setStatus(manager);
}
}
I've tried various subpaint() overrides to set the Graphics, but can't seem to set anything other than a solid color. Calling setGlobalAlpha() doesn't have the desired results either (as noted in other posts).
Any thoughts?
You may try using the paintBackground method to paint all of your background images and colors.
protected void paintBackground(Graphics g) {
g.setGlobalAlpha(255);
g.setColor(backgroundColor);
... more background drawing ...
}
For modifying the background on screens, use
this.getMainManager().setBackground(...);
You should be able to set and image, a color, whatever you need for the background to the screen.

JTabbedPane - set default border around tabs..?

I am using a JTabbedPane in my application. I have added two tabs which are instances of a custom class "ContentPanel". This extends JPanel and sets the background, border etc etc. Basically it means I dont have to set the properties of each JPanel I want to apply this colour scheme to. I notice that not only does their border appear but another border (which, I think, is blue - at least on my screen) appears around this border, connected to the tab "selectors" themselves (i.e. the buttons you click on to get the appropriate view). I would like to change this border as it just looks odd against a gold / brown colour scheme. Does anyone have any idea how to do this? I have tried JTabbedPane.setBorder(Border b) but that doesnt work. That simply sets a border around the entire thing, including the tab selectors.. not what I want.
Any help with this would be greatly appreciated.
These colors are defined in the Look and Feel. If you look at the code for BasicTabbedPaneUI, you will notice that installDefaults() sets a bunch of protected Color instance variables. The keys they are defined against in the L&F are also available here.
protected void installDefaults() {
LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
"TabbedPane.foreground", "TabbedPane.font");
highlight = UIManager.getColor("TabbedPane.light");
lightHighlight = UIManager.getColor("TabbedPane.highlight");
shadow = UIManager.getColor("TabbedPane.shadow");
darkShadow = UIManager.getColor("TabbedPane.darkShadow");
//...
// a lot more stuff
//...
}
If you do not want to go as far as define your own L&F, you have the ability to set a custom UI delegate on your tabbed pane:
myTabbedPane.setUI(new BasicTabbedPaneUI() {
#Override
protected void installDefaults() {
super.installDefaults();
highlight = Color.pink;
lightHighlight = Color.green;
shadow = Color.red;
darkShadow = Color.cyan;
focus = Color.yellow;
}
});
you may of course want to change those color settings. As set, you will see which vars are used where.
None affecting L&F and JVM run-time system-wide settings code solution.
Create your own tabbed-pane class and nested tabbed-pane-UI class to deal with the issue for a "specific" class of tabbed-pane. The code below is original: (The last answer was 2010, but this may be useful too.)
public class DisplayTabbedPane extends JTabbedPane implements
MouseListener, ChangeListener {
public DisplayTabbedPane() {
setTabPlacement(SwingConstants.BOTTOM);
// UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
// works but is a JVM system wide change rather than a specific change
NoInsetTabbedPaneUI ui = new NoInsetTabbedPaneUI();
// this will build the L&F settings for various tabbed UI components.
setUI( ui );
// override the content border insets to remove the tabbed-pane
// blue border around the pane
ui.overrideContentBorderInsetsOfUI();
}
/**
* Class to modify the UI layout of tabbed-pane which we wish to override
* in some way. This modification only applies to objects of this class.
* Doing UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
* would affect all tabbed-panes in the JVM run-time.
*
* This is free to use, no copyright but is "AS IS".
*/
class NoInsetTabbedPaneUI extends MetalTabbedPaneUI {
/**
* Create tabbed-pane-UI object to allow fine control of the
* L&F of this specific object.
*/
NoInsetTabbedPaneUI(){
super();
}
/**
* Override the content border insets of the UI which represent
* the L&F of the border around the pane. In this case only care
* about having a bottom inset.
*/
public void overrideContentBorderInsetsOfUI(){
this.contentBorderInsets.top = 0;
this.contentBorderInsets.left = 0;
this.contentBorderInsets.right = 0;
this.contentBorderInsets.bottom = 2;
}
}
........
}
Change Look And Feel with "UIManager"
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Enabled].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Enabled+MouseOver].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Enabled+Pressed].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Focused+MouseOver+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Focused+Pressed+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Focused+Selected].backgroundPainter", new BackgroundPainter(Color.GRAY));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[MouseOver+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Pressed+Selected].backgroundPainter", new BackgroundPainter(Color.white));
UIManager.getLookAndFeelDefaults().put("TabbedPane:TabbedPaneTab[Selected].backgroundPainter", new BackgroundPainter(Color.white));
BackgroundPainter class
public class BackgroundPainter implements Painter<JComponent> {
private Color color = null;
BackgroundPainter(Color c) {
color = c;
}
#Override
public void paint(Graphics2D g, JComponent object, int width, int height) {
if (color != null) {
g.setColor(color);
g.fillRect(0, 0, width - 1, height - 1);
}
}
}

Categories

Resources