I'm looking at the documentation for javax.swing.JComponent, and this method stuck out to me because I am currently trying to create a subclass of this type. Is there any point in overriding the getInsets()? What is the purpose of this method when we already have getPreferredSize()?
getInsets defines space which can be taken up by extra content, like borders, which is, generally, added to the preferredSize and offsets the position (translates) of the Graphics context, to ensure that the actual content is painted instead the insets.
Personally, unless you intend to prevent anybody from modifying the state of the components borders or are thinking for providing margins of some sort, I'd leave it alone (and not override it)
Related
Say I have a resizable JFrame GUI, with size Dimension(x,y). I want to insert objects of class JPanelExtender into GUI (preferably using BoxLayout). To keep it simple, let's say each of these objects has a field percent which is a double between 0.0 and 1.0 and the sum of all percents is 1.
How can I make all of the JPanelExtenders always be exactly of size Dimension(x*percent, y), where x and y are the updated JFrame values after resizes?
I have tried using setSize(getParent().getHeight(), getParent.getWidth()*percent)) in the paint(Graphics) method, and overriding the getPreferredSize() method but neither works.
What methods do I have to override? Do I need to use null layout? How does this affect components within my JPanelExtenders?
In general I'm confused about how LayoutManagers allocate space to components, what factors take precedence, and how to control those things myself.
Note: I realize this is a very abstract question and SO likes specific examples and code, but I really want to know in principle and my actual program requires this kind of thing on multiple panels such that sharing specifics would only obscure the crux of my question. Any references to guides and such (I've already read the Oracle LayoutManager tutorials) would be appreciated.
Other Note: It'd be nice if I could use this resizing for components in a GridBagLayout too.
I wanted to access the Graphics class to draw a rectangle, but wanted to do so without having to call the paintComponent method. Is it possible?
Painting should always be done in a painting method.
The most common way is to do the custom painting in the paintComponent(...) method of the component.
Another approach is to "decorate" a component using the JLayer class and implement the painting in the paint(...) method of the JLayer. Read the section from the Swing tutorial on Decorating Components Using the JLayer Class for more information and examples.
Yes, it is possible, the same way it is possible to paint in a BufferedImage (by using straight the Graphics object by calling into the getGraphics() method).
BUT, unlike the case for BufferedImage, doing so for a component is likely to cause a mess. The reason is the underlying AWT/Swing code expects the component to paint itself, (see Component.paint or JComponent.paintComponent) while it (the underlying AWT/Swing code) will take care by about when this rendering needs to occur (e.g. scrolling, resizing, etc).
As such, if you paint from outside the component, at any time the AWT/Swing decides "Well, there's some new painting/repainting to be done", it will invoke the "standard methods", with the expectation your drawing code is there. As you paint it externally, if you don't make special arrangements to be notified of the "need repainting" situation, parts or the entire "drawing-from-outside" will be painted over or not painted at all or all kind of different messy situation.
In other words, if you paint-from-outside, you'll need to write not only the code that does the painting, but also:
re-implement heaps of code already implemented by the AWT/Swing which deals with the circumstances when the component needs to be (re)painted.
disable somehow the AWT/Swing "native" handling of painting.
Rest assured, you don't want that (even if you think you do).
PS. if you describe better why do you need that, you may get more useful suggestions on how to do it the proper way.
I'm implementing a custom Look & Feel using Synth for my application - basically providing custom versions of SynthStyle, SynthPainter and SynthStyleFactory.
I am not using any XML, i.e. everything is done through the Java API. In general this is working just fine.
The best way to set appropriate insets is however proving a little tricky. I can see various options:
Override getInsets for SynthStyle to return specific insets for each Region
Apply a border to components using SynthStyle.installDefaults
Set all insets to (0,0,0,0) and compensate in the painting methods
Create new ComponentUI delegates
What would be the best approach and why?
Did you come to a decision on this yet? Here's my opinion on things...
Overriding getInsets() looks like a nice solution if you have a simple style with a small number of contexts. It should help keep insets consistent throughout your GUI with only one point of change needed.
Applying an empty border to components seems a little hackish for this purpose. If you need to apply a custom border to a component then you might inadvertently remove the empty border unless you override the setBorder() methods to use a compound border.
Setting all insets to 0 is unnecessary since they start at 0 already... I would be very scared at the thought of rewriting paint methods! That's generally done for adding finishing touches to components, not re-defining their entire boundaries.
I'm not entirely sure how creating a new ComponentUI would help, since that's mainly to do with sizing and painting (like the above).
I would try out option 1 first since it will have a global effect on your application, and then start working out what exceptions and contexts you want to put into it.
I have to use a GUI Element that draws a picture at a specific screen position.
If the user selects this picture there is a border drawn around the Image.
Now we want to include another border that identifies pictures with a specific value for the user.
At the moment the Element looks at his internal state if it is selected and then decides how to draw itself.
graphic.drawImage(icon, x, y, null);
if (selected) {
drawBorder();
}
I don't like the idea of adding another if else to this drawing method.
I thought about creating a new class that inherits the behavior of the element and overwrites the draw method but that means duplicating the whole selected code in every inherited class.
Is there a nice possibility so solve this problem without creating a subclass?
Since you tagged this with design-patterns and you seem to be looking for a pattern-oriented approach, I'd suggest taking a look at the state pattern. The example on the wikipedia page even mentions keeping state while drawing a GUI. Unfortunately, this would mean you'd have to create another class with subclasses and overridden methods.
Is this going to be something that is likely to change? I.e. do you realistically think you're going to be adding new behavior to the drawing (e.g. if the user double clicks, draw a different type of border; if the user right clicks, change the color of the border), or is this it? If you see more behavior being added, I think going ahead and taking a more OO approach is wise. If it's just these two cases, I'd say just add and else if statement.
What do you have against if-else?
It makes less sense to me to create a whole new object for the selected item than to check a flag in the drawing function.
one possibility is to allow your drawBorder() method to take parameters:
private void drawBorder(boolean isSelected, boolean hasSpecialValue);
this method can determine which type of border to draw.
I have an application that uses disabled JTextFields in several places which are intended to be transparent - allowing the background to show through instead of the text field's normal background.
When running the new Nimbus LAF these fields are opaque (despite setting setOpaque(false)), and my UI is broken. It's as if the LAF is ignoring the opaque property. Setting a background color explicitly is both difficult in several places, and less than optimal due to background images actually doesn't work - it still paints it's LAF default background over the top, leaving a border-like appearance (the splash screen below has the background explicitly set to match the image).
Any ideas on how I can get Nimbus to not paint the background for a JTextField?
Note: I need a JTextField, rather than a JLabel, because I need the thread-safe setText(), and wrapping capability.
Note: My fallback position is to continue using the system LAF, but Nimbus does look substantially better.
See example images below.
Conclusions
The surprise at this behavior is due to a misinterpretation of what setOpaque() is meant to do - from the Nimbus bug report:
This is a problem the the orginal design of Swing and how it has been confusing for years. The issue is setOpaque(false) has had a side effect in exiting LAFs which is that of hiding the background which is not really what it is ment for. It is ment to say that the component my have transparent parts and swing should paint the parent component behind it.
It's unfortunate that the Nimbus components also appear not to honor setBackground(null) which would otherwise be the recommended way to stop the background painting. Setting a fully transparent background seems unintuitive to me.
In my opinion, setOpaque()/isOpaque() is a faulty public API choice which should have been only:
public boolean isFullyOpaque();
I say this, because isOpaque()==true is a contract with Swing that the component subclass will take responsibility for painting it's entire background - which means the parent can skip painting that region if it wants (which is an important performance enhancement). Something external cannot directly change this contract (legitimately), whose fulfillment may be coded into the component.
So the opacity of the component should not have been settable using setOpaque(). Instead something like setBackground(null) should cause many components to "not have a background" and therefore become not fully opaque. By way of example, in an ideal world most components should have an isOpaque() that looks like this:
public boolean isOpaque() { return (background!=null); }
I ran into this same issue last week using JTextPane. The setOpaque() method works as expected when using any look and feel other than nimbus. Apparently, the nimbus look and feel changes the behaviour we have come to expect with setOpaque() for many Components. Depending on how you look at it, it can be considered a bug. Check the comments on this sun bugid:
nimbus opaque bug
The workaround that worked for me was:
myPane.setOpaque(false); // added by OP
myPane.setBorder(BorderFactory.createEmptyBorder());
myPane.setBackground(new Color(0,0,0,0));
Note from OP: I also had to ensure setOpaque(false) for JTextField so that the parent background was painted - just wanted to mention this for others who follow in case they had experimented with setOpaque(true), as I had.
Hey there Software Monkey.
mmhh what about installing UI's subclasses replacement that actually respect the setOpaque behavior.
I think it is something like setUI or something like that.
You could grab the source code of the nimbus and see what's broken there ( if it is there ) , subclass it and install the "fixed" one.
Yours sound quite intersting, do you have any screenshot we can see?
From the javadoc
public void setBackground(Color bg)
Sets the background color of this component. The background color is
used only if the component is opaque,
and only by subclasses of JComponent
or ComponentUI implementations. Direct
subclasses of JComponent must override
paintComponent to honor this property.
It is up to the look and feel to honor this property, some may choose
to ignore it.
I think the question is how to interpret "opaque" and "background".
For a JTextfield there is the question: "what visible parts are the background?". I'd define "background" as the parts of the bounding rectangle, that are not drawn by the component.
For a "round" button, e.g., this will be the corners outside the circle.
Therefor I'd say a JTextfield has no visible background! It has a rectangular shape and what you are the taking as background is not the field's background but the field's canvas.
Rebuttal from OP
This is an interesting enough idea to be worth responding to in the answer for future viewers (as opposed to in comments).
I have to disagree. I would argue that the part of the component outside the border is not part of the component - it's outside the component. A field with rounded corners is, of necessity, non-opaque, in that it cannot be responsible for painting it's entire rectangular region - this is a side-effect of all components being rectangular in dimensions.
I think this consideration makes the argument for the existing (and misunderstood) meaning of isOpaque(). It also makes my argument that setOpaque() should not exist and that setBackground(null) should cause the component to not paint a background.
I would put forth that the background of a text field is indeed the color of the region inside it's borders, and I don't think you will find very many people to dispute that as an intuitive conclusion - therefore having background apply to that region obeys the rule of least surprise for the API user.