Re: Graphics2D question
- From: "Peter Duniho" <NpOeStPeAdM@xxxxxxxxxxxxxxxx>
- Date: Fri, 14 Mar 2008 11:06:27 -0700
On Fri, 14 Mar 2008 09:49:12 -0700, RichT <someone@xxxxxxxxxxxxx> wrote:
[...]
Yes sorry again I really didn't give it a thought at the time, what Knute suggested has been extremely helpful and gives me part of my answer, and that is that as long as I have a reference to the graphic of the component I can draw to this and send a repaint call to the component after?
The way I read the above: you've got a GUI component that only redraws itself using a referenced "graphic" (there's no such thing as a "graphic" in Java...do you mean an instance of Image, such as BufferedImage?), while some other code draws into the "graphic" and then tells the component that the "graphic" has changed.
Is that a correct understanding of what you wrote? If so, then that seems fine to me.
Any particular reason for extending JPanel? Does your custom component actually need to act as a simple container for other components? If not, perhaps you should simply be extending JComponent.No particular reason, I chose JPanel as most of the examples I have seen use this.
Well, unless you have a specific reason for using JPanel, then don't. Extend JComponent instead, as it's the actual base class for Swing components.
Why would an image be "enlarged"? How does that happen? Hint: it's at whatever point the image display, including scaling, is configured that you'd set the necessary data that will be used later for drawing the image.
Int the graphic object and then call a repaint on the component to refresh display?
You're answering a question with a question (and one I don't really understand either...what does the word "int" mean here? The usual meaning in this context, "integer", doesn't seem to apply).
[...]I'm not really clear on what the "non gui logic class" is. Anything that needs to know the size of your custom component and involves itself in the drawing of the component is, to my perception, a GUI class.
Ah my idea of a gui class is an extended JPanel stuffed with code :)
Well, fine. But that's not an explanation as to what a "non gui logic class" is.
If you have in mind a class that doesn't know anything about the GUI, thenperhaps you could be more explicit about that.
This is exactly what I had in mind, if my understanding is correct then passin a reference of tthe components graphic object will allow me to draw to the graphic and then fire an event for the component to redraw itself.
See above. If I understand this statement correctly, then it seems like a fine approach. If I don't, then it might or might not be. :)
What interface does that class implement?
I don't mean what's the name of the interface; I mean,
what methods, properties, and/or fields are exposed by that interface?
I would imagine methods something like
scale, translate, draw, update, origin, rotate, drawText, to name a few
Are the transformation operations (scale, translate, rotate...and what's the difference between "translate" and "origin"?) independent of the similar operations that would be set for the custom Swing component? That is, from your previous description, I have the impression that you want to be able to scale and center the image in the custom component, and now from this most recent description I have the impression that you also want to be able to do things like that and other operations on the non-GUI class.
How does your custom component use this hypothetical "non gui logic class"?
I am guessing that the component class would create an instance of the logic class and pass it's graphic object and an image in the constructor and would implement property change listener.
What's the difference between "its graphic object" and "an image"? At the outset of this message, I made the assumption that your ambiguous term "graphic" referred to some sort of Image instance. But if you can have both a "graphic" and an "image", then I'm not so sure.
Any buttons or menu item events would call the relevant method in the logic class.
The logic class would calculate positions, scales and translations etc and then using the graphic reference draw to this and then call repaint on the component, possibly via an event, not 100% sure though
I think it makes more sense to follow the "listener" idiom, where your "non-GUI" class defines a listener interface that the Swing component can implement and respond to. Then when changes are made, the listener is notified. In this case, the Swing component would call repaint() itself, but this allows for a more general-purpose "non-GUI" class. After all, if it's "non-GUI" then why should it know about a JComponent that needs to have repaint() called?
Other than that, your description sounds fine. Of course, that assumes I understand the rest of the model correctly, which may or may not be the case.
Well, typically the custom component would either itself be directly informed of changes to the image, or it would refer to some data structure that itself knows about the changes. By the time repaint() is called, everything should already be set up to draw the custom component correctly. The paintComponent() method itself would then simply use the current state of the data structures involved to draw the correct thing.
This sounds interesting, how would this work? this sounds similar to the Swing Model View pattern?
Well, it's more like being "similar" to the event-driven GUI updating paradigm that practically all mainstream GUI's use.
Model/View is about separating the data from the model. And it works well in an event-driven paradigm. But it's not mandatory...you can easily have views (i.e. Swing components) that incorporate their own model, and that's not in line with the Model/View pattern. Yet, they would still use this "change some data, call repaint() to ask to be repainted" paradigm.
In other words, what I'm describing really isn't optional, the way that Model/View is. A correctly-written GUI application will always use this event-driven paradigm, whether or not it separates the model from the view.
Again, I'm not really sure what you mean here. The Graphics2D instance doesn't have any state that specifically would know about your image. However, you can certainly set the transformation for the Graphics2D based on whatever scaling and translation is necessary. In fact, creating this transformation could be part of the "pre-compute and cache" operation I mentioned earlier. Then all that the paintComponent() method would have to do is call Graphics2D.transform() with the AffineTransform that you'd computed earlier.
By using the reference to the components graphic object ?
Again, that depends on what you mean by "graphic object". But sure, if that "graphic" object keeps track of the transform needed for drawing, that would be a natural place to get the AffineTransform instance.
Note that setting the Graphics2D transform is not the only approach. You can also pass a transform to some of the Graphics2D.drawImage() overloads, and it would have the same effect as changing the Graphics2D's own transform. This is what I'm talking about here:
Alternatively, you could just compute the necessary parameters that would be used later to call one of the many drawImage() overloads defined in Graphics and Graphics2D and rather than setting the transform, just call the appropriate overload directly.
Do you mean Graphics2D.DrawImage, DrawLine etc?
Those are not the names of any methods in the Graphics2D class. But if you mean Graphics2D.drawImage(), yes. For that method, you could just pass a precomputed transform. For drawLine(), there's no such overload...you've have to explicitly transform integer coordinates yourself and then use the Graphics.drawLine() method, or create a Shape representing the line and either transform the Shape before drawing it (some Shape implementations include a transform() method) or set the Graphics2D transform itself.
It all depends on exactly what you want to do.
In the hopes that it might help you out a little, I've attached below an example of a simple custom Swing component that has image and scale properties that can be set, and which always draws the image centered in the component at the scale that's been set. It doesn't demonstrate the additional layer that I think you've been talking about, where there's an intermediate class that actually manages things like holding on to the image, storing settings like scale factor, and implementing notification for listeners, but hopefully it gives you a better idea of how the Swing component side of things would work.
The custom Swing class is first, followed by some simple sample code that demonstrates the use of it. (The demo code just opens image files, but the custom component can also just be assigned some arbitrary image; it would be simple enough to add a public method to notify the component that the image's changed so that the component knows to call repaint(), but as I mentioned before I think it would be better to implement a full-fledged "listener" API that the component can subscribe to if you're going to have anything more elaborate than just a simple reference to an Image instance as shown here).
Pete
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class JImageBox extends JComponent
{
private Image _image;
private double _scale = 1.0;
AffineTransform _transform;
public JImageBox()
{
this.addComponentListener(new ComponentAdapter()
{
public void componentResized(ComponentEvent arg0)
{
_UpdateImage();
}
});
}
public void setImage(BufferedImage image)
{
_image = image;
_UpdateImage();
}
public void setImage(File file)
{
try
{
_image = ImageIO.read(file);
}
catch (IOException e)
{
e.printStackTrace();
_image = null;
}
_UpdateImage();
}
public void setScale(double scale)
{
_scale = scale;
_UpdateImage();
}
protected void paintComponent(Graphics gfx)
{
if (_image != null)
{
Graphics2D gfx2 = (Graphics2D)gfx;
gfx2.drawImage(_image, _transform, null);
}
}
private void _UpdateImage()
{
_transform = null;
if (_image != null)
{
Dimension size = getSize();
double cxImage, cyImage;
cxImage = _image.getWidth(null) * _scale;
cyImage = _image.getHeight(null) * _scale;
_transform = AffineTransform.getTranslateInstance(
(size.getWidth() - cxImage) / 2,
(size.getHeight() - cyImage) / 2);
_transform.scale(_scale, _scale);
}
repaint();
}
}
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
public class TestImageComponentFrame extends JFrame
{
private JImageBox _imagebox = new JImageBox();
public TestImageComponentFrame(String arg0)
{
super(arg0);
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
Box box = Box.createHorizontalBox();
JButton button = new JButton("Open File...");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
_ChooseImage();
}
});
box.add(button);
final JTextField text = new JTextField("1.0", 8);
text.setMaximumSize(text.getPreferredSize());
button = new JButton("Set Scale");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
try
{
_imagebox.setScale(Double.parseDouble(text.getText()));
}
catch (NumberFormatException e)
{
e.printStackTrace();
}
}
});
box.add(button);
box.add(text);
add(box);
add(_imagebox);
}
private void _ChooseImage()
{
FileDialog filedlg = new FileDialog(this, "Please choose an image file to load:", FileDialog.LOAD);
filedlg.setVisible(true);
if (filedlg.getFile() != null)
{
_imagebox.setImage(new File(filedlg.getDirectory() + filedlg.getFile()));
}
}
}
import java.awt.*;
public class TestImageComponent
{
/**
* @param args
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
TestImageComponentFrame frame = new TestImageComponentFrame("TestImageComponentFrame");
frame.setSize(640, 480);
frame.setVisible(true);
}
});
}
}
.
- Follow-Ups:
- Re: Graphics2D question Part II
- From: RichT
- Re: Graphics2D question
- From: RichT
- Re: Graphics2D question Part II
- References:
- Graphics2D question
- From: RichT
- Re: Graphics2D question
- From: Peter Duniho
- Re: Graphics2D question
- From: RichT
- Re: Graphics2D question
- From: Peter Duniho
- Re: Graphics2D question
- From: RichT
- Graphics2D question
- Prev by Date: Re: Java HashSet Problem
- Next by Date: JCA/JCE jar development on eclipse 3.3.2
- Previous by thread: Re: Graphics2D question
- Next by thread: Re: Graphics2D question
- Index(es):