I am creating lines and other components and want them to respond like a Swing button events as a line would be clickable :
class CustomLine extends JComponent {
private int destx = 100;
private int desty = 100;
private int startx = 0;
private int starty = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(startx, starty, destx, desty);
}
}
how this works?
It would be a math problem. You need to find the equation of the line and then find the distance from point (mouse click).
The math is done for you, for example here
You will also need to figure out handling mouse events.
What about extending it from JButton?
Just overrive the paint-Method like you did with JComponent and use it like a normal Button.
I'm not sure, if you need to alter the Border, too. I'm not familiar with altering GUI-Elements, but maybe this should do the trick.
Related
So I want to change the position of an image which I implemented as a JComponent after I click the corresponding button, however, it does not do anything when getting clicked.
I think the problem may lay inside my paintComponent method, since I want the image to be drawn in the center of my panel in the beginning.
The panel itself has a GridBagLayout, although I think that anchoring it in the center would not help either, since I want to move the image whenever the button is clicked and only want it to show up in the center of the panel when I open the frame...
For this button in particular I want my JComponent/image to move 50 pixels to the right.
Anyway here's what I did:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = (this.getWidth() - img.getWidth(null))/2;
int y = (this.getHeight() - img.getHeight(null))/2;
g.drawImage(img, x, y, null);
}
#Override
public void actionPerformed(ActionEvent e) {
img.repaint(img.getX()+50, img.getY(), img.getHeight(), img.getWidth());
}
Currently, your paintComponent method always uses the same values for x and y, so it should come as no surprise that the image doesn’t move.
x and y are state that needs to be remembered. This is done by defining instance fields:
private int x;
private int y;
You can’t initialize them at construction time, because a component has a width and height of zero when it’s created (usually). Fortunately, there is a method which tells you when a component has been given a width and height; that method is addNotify, which you can and should override:
#Override
public void addNotify() {
super.addNotify();
x = (this.getWidth() - img.getWidth(this)) / 2;
y = (this.getHeight() - img.getHeight(this)) / 2;
}
Notice that you should pass this (your component instance) to any method which expects an ImageObserver argument, rather than null.
Now paintComponent doesn’t need to do any calculation. It just paints the image at whatever x and y happen to be:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, x, y, this);
}
And now that your coordinates are instance fields, you can change them directly:
#Override
public void actionPerformed(ActionEvent event) {
x += 50;
repaint();
}
I am currently working on a test driven interactive 2D fluid dynamics simulation for my Bachelor thesis. The basic idea is that the user can draw shapes on his screen and the program will simulate how a fluid will flow around these shapes.
So far I have only started with the painting process and I have already run into a little problem.
First, here is a little UML diagram that shows my project structure so far
As you can see, I have created a Painter class that can visit several shapes and will call one of the abstract paint methods depending on the shape type. Then there is the SwingPainter class which inherits from the Painter class and implements the three paint methods as well as the clear method.
Here is the code for my SwingPainter:
public class SwingPainter extends Painter {
private final GraphicPanel graphicPanel;
public SwingPainter(GraphicPanel graphicPanel) {
this.graphicPanel = graphicPanel;
}
private Graphics getGraphics() {
return graphicPanel.getGraphics();
}
#Override
protected void paintLine(Point start, Point end) {
getGraphics().drawLine(start.getX(), start.getY(), end.getX(), end.getY());
}
#Override
protected void paintRectangle(Point start, Point end) {
int minX = start.getX() < end.getX() ? start.getX() : end.getX();
int minY = start.getY() < end.getY() ? start.getY() : end.getY();
int width = Math.abs(start.getX() - end.getX());
int height = Math.abs(start.getY() - end.getY());
getGraphics().drawRect(minX, minY, width, height);
}
#Override
protected void paintCircle(Point center, double radius) {
int minX = center.getX() - (int) radius;
int minY = center.getY() - (int) radius;
int diameter = (int) radius * 2;
getGraphics().drawOval(minX, minY, diameter, diameter);
}
#Override
public void clear() {
graphicPanel.paintComponent(getGraphics());
}
}
So my problem is that the GraphicPanelPresenter(see UML diagram) is responsible for passing the Painter/Visitor to the shapes when the user left clicks or moves the mouse(the second point of a line will follow the cursor for example). That means the actual painting is done outside of Swing's paint method. As a result I have encountered some flickering while painting and I was wondering if anyone knew how to fix that without throwing the whole painter/visitor functionality out the window(since there are other features concerning the shapes that still have to be implemented and could easily be handled by simply passing them another type of visitor).
Well, that was a rather lengthy description for a rather small problem, sorry for the wall of text, but I would be glad for any kind of hint!
You need to do all the of painting in the various Swing paint methods that are designed to be overridden for custom painting (such as paintComponent). Only Swing will know when it's done drawing, or when a new frame needs to be drawn to.
What it really seems like you need to do, is to have your GraphicsPanel have a reference to the object that holds your shape's states, and then draw the graphics that represent that state in paintComponent.
This should also simplify your class relationship diagram as well, since within GraphicsPanel you can call the state object's methods from your listeners to change the state (I would choose different names to make the state object not aware that the changes are based on UI interactions, so if right click rotates, make the method called rotate instead of handleRightClick).
I want to capture mouse events in Swing Component, like MouseDrag Event etc, but I have found there are some events missing when I move my mouse very quickly,
It seems not all of the events are captured. The trace of mouse I captured is discrete but I want to the trace of mouse and the precision is 1 pixel.
Could you help me please? Thanks a lot.
You can't do this. Not even the mouse itself reports every single pixel (or whatever unit it uses) to the computer.
You'll have to interpolate the missing points. A single linear interpolation should do the trick.
If you only want to capture mouse movement on a certain Component, then the MouseDrag event etc will be all you need. As Matti Virkkunen said, you have to do point-to-point interpolation if you want to have a continuous line.
If you are asking about capturing all events that happen inside a Container and its sub-components, then you might consider accessing the EventQueues.
However, I have no actual knowledge of that, but a search on google might get you where u need.
One really easy solution - especially when it comes to painting - is using the provided Graphics and Graphics2D objects:
static class MyPanel extends JPanel {
private static final long serialVersionUID = -5482850214654836564L;
private int lastX = -1;
private int lastY = -1;
public MyPanel() {
super(true); // activate double buffering
addMouseListener(new MouseAdapter() {
#Override public void mousePressed(final MouseEvent pE) {
final int newX = pE.getX();
final int newY = pE.getY();
final Graphics g = getGraphics();
if (g == null) return; // panel not visible
g.drawLine(lastX, lastY, newX, newY); // or add to list
lastX = newX;
lastY = newY;
}
});
}
}
public static void main(final String[] args) {
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setBounds(200, 200, 400, 400);
f.add(new MyPanel());
f.setVisible(true);
}
For a more consistent system take a look into Java Shapes (RoundRectangle, Polygon) etc.
Those could be created in the listener part, then stored in a list, and inside the paint()/paintComponent() method you could draw those shapes with g.fill() or g.drawPolygon(p).
I have defined a class called Stone to add graphical stones to a JPanel:
public class Stone {
private int x, y;
private Color color;
private static final int radius = 18;
Stone(Color color) {
this.color = color;
}
public Stone(int x, int y, Color color) {
this(color);
this.x = x;
this.y = y;
}
void draw(Graphics g) {
g.setColor(color);
g.fillOval(x - radius, y - radius, 2 * radius, 2 * radius);
}
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
I want to draw them on a JPanel. Do I have to do this within the paint method of JPanel or is it possible to use the add method of JPanel?
A quick answer is that you should extend a JComponent (because you want to add it to JPanel) and override the paintComponent method (because you want some custom painting of your object).
The easiest thing to try is to make your Stone class extend JComponent, rename draw() to paintComponent() and add a Stone instance to your JPanel.
It depends on what you want to do with it on the screen. As the others mentioned you could inherit from JComponent, which could be a good choice if the user wants to interact with it in some way.
A more light-weight approach could be to implement the Shape interface, or provide a method getShape().
You could use the ShapeIcon I wrote some time ago, to add it to a JLabel:
http://softsmithy.sourceforge.net/lib/docs/api/org/softsmithy/lib/swing/icon/ShapeIcon.html
Then add the JLabel to the JPanel.
But maybe instead of adding individual Stone icons to individual JLabels, you want to draw an image first and show that in the JPanel?
If you could tell us more about your goals we could help you better.
How can I mousedrag different BufferedImages in Java2D?
For instance, if I have ten or more images, how can I move that images which my mouse is over?
Now I'm importing an BufferedImage with
BufferedImage img = new BufferdImage(new File("filename"));
And I'm painting this with Graphics2D with
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawImage(img, x1, y1, null);
g2d.drawImage(img2, x2, y2,null);
}
Everytime I'm moving on a image I'm repaint()-ing the entire screen.
My mousemove class is as follows
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
x1 = e.getX() - (img.getWidth() / 2);
y1 = e.getY() - (img.getHeight() / 2);
repaint();
}
}
With this method I'm able to "drag" one picture, but what to do when I will drag more individually?
Use the BufferedImage to create an ImageIcon which you use to create a JLabel. Then you add the JLabel to the panel that uses a null layout. No custom painting code is required to do this.
Now if you want to drag the label around you can use the Component Mover.
You can try making a custom component that contains only a single image. Along with your painting and mouse motion handling code, the component overrides the contains method so that it returns true only if the coordinates are within the image.
These components are then stacked in a JLayeredPane, (hopefully) only moving the images that the mouse is on top of.
From what you ask I suppose that your current repainting logic is global. You need to apply it to every image you have. So, if you for instance display every image in JPanel attach MouseMotionListener to every such panel and make this logic happen in JPanel.
If you post more code - especially of the component you show your images in - I will be able to go into more details.
Here's is a simple example that implements dragging for either single- or multiple-selections. The object Node would correspond roughly to your object Card.
Addendum: Also considered the Overlap Layout mentioned in this answer to a related question. Instead of List<Node>, your program would manage a List<Card>, where each Card is a JLabel having a card image.
I should make tree arrays:
one for the x-values
one for the y-values
one for the BufferedImages
So, something like this:
int[] xValues = new int[10];
int[] yValues = new int[10];
BufferedImage[] imgs = new BufferedImage[10];
Then the
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
for (int i = 0; i < 10; i++)
{
xValues[i] = e.getX() - (imgs[i].getWidth() / 2);
yValues[i] = e.getY() - (imgs[i].getHeight() / 2);
}
repaint();
}
}
Then paint them like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
for (int i = 0; i < 10; i++)
{
g2d.drawImage(imgs[i], xValues[i], yValues[i], null);
}
}
I think something like this is what you need.
Here's the code for my JLayeredPane init. My problem here is that my images don't show up...
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new java.awt.Dimension(500, 410));
layeredPane.setBorder(javax.swing.BorderFactory.createTitledBorder(
"Center deck"));
for(BufferedImage imgs : images){
JLabel label = new JLabel(new ImageIcon(imgs));
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
}
add(layeredPane);