I'm making a program that has an image that you scroll around on, and I can't figure out how to update the image if a button is pressed (For example: Adds a Green Ellipse to the image.) It already draws the image into the JScrollPane and you can scroll around, but when you click a button it doesn't refresh the image. (more details in code)
Here is the code:
public class PegMaster extends JPanel implements ActionListener {
//Note: not complete code
public PegBox[] pegbox = new PegBox[9];
public static Dimension size = new Dimension(520, 500);
public BufferedImage canvas;
public Graphics2D g2d;
public JScrollPane scroller;
JPanel panel;
private Canvas window;
JScrollPane pictureScrollPane;
public PegMaster() {
JButton button = new JButton("test");
button.addActionListener(this);
add(button);
canvas = new BufferedImage((int)size.getWidth()-30, 75 * GUESSES, BufferedImage.TYPE_INT_RGB);
g2d = canvas.createGraphics();
for(int i = 0;i<=pegbox.length-1;i++) {
pegbox[i] = new PegBox(i, g2d);
}
window = new Canvas(new ImageIcon(toImage(canvas)), 1);
//Class Canvas is a Scrollable JLabel to draw to (the image)
pictureScrollPane = new JScrollPane(window);
pictureScrollPane.setPreferredSize(new Dimension((int)size.getWidth()-10, (int)size.getHeight()-20));
pictureScrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
add(pictureScrollPane);
//adds the scrollpane, but can't update the image in it
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
//just adds the scrollpane
}
});
}
public void paint(Graphics g) {
super.paint(g);
for(int i = 0;i<=pegbox.length-1;i++) {
//pegbox[i] = new PegBox(i);
pegbox[i].draw(g2d);
}
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//tried re-making the scrollpane, didn't work.
//window = new Canvas(new ImageIcon(toImage(canvas)), 1);
//pictureScrollPane = new JScrollPane(window);
//pictureScrollPane.setPreferredSize(new Dimension((int)size.getWidth()-10 (int)size.getHeight()-20));
//pictureScrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
//tried imageupdate: pictureScrollPane.imageUpdate(canvas, 0, 0, 0 (int)size.getWidth()-10, (int)size.getHeight()-20);
//remove(pictureScrollPane);
//tried this: pictureScrollPane.revalidate();
repaint();
}
}
Firstly, don't use Canvas it's a heavy weight component, it will only cause you issues in the long run, use either JComponent or JPanel
Secondly, don't override paint, use paintComponent instead. paint does a lot of work, including painting things like the border and child components. It's better if you use paintComponent as it's at the right layer within the paint hierarchy for what you want do to.
Thirdly, NEVER call something like Thread.sleep while in the Event Dispatching Thread. This will cause the event queue to pause and stop responding to events, making you program look like it's stalled.
Fourthly, NEVER call repaint (invalidate, revalidate or any method that might cause a repaint request to occur) within a paint method. You will simply end up maxing out your CPU and you will be forced to kill the process.
Fifthly, you didn't provide the actionPerformed method, which is probably where all the action (and problems) are. I'd imagin you need to call window.repaint() and possibly window.invalidate() (in reverse order), but since you didn't provided use with this code, that's simply speculation...
Try this class which displays an Image. This can be added to a JScrollPane
public class ImagePanel extends JPanel {
public Image img;
public ImagePanel(Image img){
this.img = img;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(img, 0, 0, this);
}
}
Now add this class to the JScrollPane. To update it, just change the image reference and call the repaint() method on the component
The above solution didn't solved my purpose so I researched and found this.
Please follow the link for whole example. I have added the code to refer in case the link changes.
public class ScrollablePicture extends JLabel
implements Scrollable,
MouseMotionListener {
private int maxUnitIncrement = 1;
private boolean missingPicture = false;
public ScrollablePicture(ImageIcon i, int m) {
super(i);
if (i == null) {
missingPicture = true;
setText("No picture found.");
setHorizontalAlignment(CENTER);
setOpaque(true);
setBackground(Color.white);
}
maxUnitIncrement = m;
//Let the user scroll by dragging to outside the window.
setAutoscrolls(true); //enable synthetic drag events
addMouseMotionListener(this); //handle mouse drags
}
//Methods required by the MouseMotionListener interface:
public void mouseMoved(MouseEvent e) { }
public void mouseDragged(MouseEvent e) {
//The user is dragging us, so scroll!
Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
scrollRectToVisible(r);
}
public Dimension getPreferredSize() {
if (missingPicture) {
return new Dimension(320, 480);
} else {
return super.getPreferredSize();
}
}
public Dimension getPreferredScrollableViewportSize() {
return getPreferredSize();
}
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
//Get the current position.
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}
//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return visibleRect.width - maxUnitIncrement;
} else {
return visibleRect.height - maxUnitIncrement;
}
}
public boolean getScrollableTracksViewportWidth() {
return false;
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
public void setMaxUnitIncrement(int pixels) {
maxUnitIncrement = pixels;
}
Related
When a user clicks on the corner of a JFrame to resize and drags the mouse around, the JFrame redraws based on the current position of the mouse as the user drags. How can you listen to these events?
Below is the what I have currently tried:
public final class TestFrame extends JFrame {
public TestFrame() {
this.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// This is only called when the user releases the mouse button.
System.out.println("componentResized");
}
});
}
// These methods do not appear to be called at all when a JFrame
// is being resized.
#Override
public void setSize(int width, int height) {
System.out.println("setSize");
}
#Override
public void setBounds(Rectangle r) {
System.out.println("setBounds A");
}
#Override
public void setBounds(int x, int y, int width, int height) {
System.out.println("setBounds B");
}
}
How can I determine and constrain how the user resizes a window (based on the current aspect ratio of the window) as they are dragging around the mouse around?
You can add a component listener and implement the componentResized function like that:
JFrame component = new JFrame("My Frame");
component.addComponentListener(new ComponentAdapter()
{
public void componentResized(ComponentEvent evt) {
Component c = (Component)evt.getSource();
//........
}
});
EDIT: Apparently, for JFrame, the componentResized event is hooked to the mouseReleased event. That's why the method is invoked when the mouse button is released.
One way to achieve what you want, is to add a JPanel that will cover the whole area of your JFrame. Then add the componentListener to the JPanel (componentResized for JPanel is called even while your mouse is still dragging). When your frame is resized, your panel will also be resized too.
I know, this isn't the most elegant solution, but it works!
Another workaround (which is very similar to Alex's but a little more straightforward) is to listen to the events from the JFrame's root pane instead:
public final class TestFrame extends JFrame {
public TestFrame() {
this.getRootPane().addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// This is only called when the user releases the mouse button.
System.out.println("componentResized");
}
});
}
}
Depending on other implementation details, it's possible that the root pane might be changed. If that's a possibility then you could override setRootPane() and deal with that. Since setRootPane() is protected and only called from the constructor though, it's unlikely you'll need to do that.
You probably need to override something like validate (don't forget to call the super). Of course, that still may not work if you are using a windowing system to configured to drag outlines.
public class MouseDrag extends Component implements MouseListener,
MouseMotionListener {
/** The Image we are to paint */
Image curImage;
/** Kludge for showStatus */
static Label status;
/** true if we are in drag */
boolean inDrag = false;
/** starting location of a drag */
int startX = -1, startY = -1;
/** current location of a drag */
int curX = -1, curY = -1;
// "main" method
public static void main(String[] av) {
JFrame f = new JFrame("Mouse Dragger");
Container cp = f.getContentPane();
if (av.length < 1) {
System.err.println("Usage: MouseDrag imagefile");
System.exit(1);
}
Image im = Toolkit.getDefaultToolkit().getImage(av[0]);
// create a MouseDrag object
MouseDrag j = new MouseDrag(im);
cp.setLayout(new BorderLayout());
cp.add(BorderLayout.NORTH, new Label(
"Hello, and welcome to the world of Java"));
cp.add(BorderLayout.CENTER, j);
cp.add(BorderLayout.SOUTH, status = new Label());
status.setSize(f.getSize().width, status.getSize().height);
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// "Constructor" - creates the object
public MouseDrag(Image i) {
super();
curImage = i;
setSize(300, 200);
addMouseListener(this);
addMouseMotionListener(this);
}
public void showStatus(String s) {
status.setText(s);
}
// Five methods from MouseListener:
/** Called when the mouse has been clicked on a component. */
public void mouseClicked(MouseEvent e) {
}
/** Called when the mouse enters a component. */
public void mouseEntered(MouseEvent e) {
}
/** Called when the mouse exits a component. */
public void mouseExited(MouseEvent e) {
}
// And two methods from MouseMotionListener:
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
// System.err.println("mouse drag to " + p);
showStatus("mouse Dragged to " + p);
curX = p.x;
curY = p.y;
if (inDrag) {
repaint();
}
}
public void paint(Graphics g) {
int w = curX - startX, h = curY - startY;
Dimension d = getSize();
g.drawImage(curImage, 0, 0, d.width, d.height, this);
if (startX < 0 || startY < 0)
return;
System.err.println("paint:drawRect #[" + startX + "," + startY
+ "] size " + w + "x" + h);
g.setColor(Color.red);
g.fillRect(startX, startY, w, h);
}
}
How can I implement Marquee effect in Java Swing
Here's an example using javax.swing.Timer.
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
/** #see http://stackoverflow.com/questions/3617326 */
public class MarqueeTest {
private void display() {
JFrame f = new JFrame("MarqueeTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String s = "Tomorrow, and tomorrow, and tomorrow, "
+ "creeps in this petty pace from day to day, "
+ "to the last syllable of recorded time; ... "
+ "It is a tale told by an idiot, full of "
+ "sound and fury signifying nothing.";
MarqueePanel mp = new MarqueePanel(s, 32);
f.add(mp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
mp.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MarqueeTest().display();
}
});
}
}
/** Side-scroll n characters of s. */
class MarqueePanel extends JPanel implements ActionListener {
private static final int RATE = 12;
private final Timer timer = new Timer(1000 / RATE, this);
private final JLabel label = new JLabel();
private final String s;
private final int n;
private int index;
public MarqueePanel(String s, int n) {
if (s == null || n < 1) {
throw new IllegalArgumentException("Null string or n < 1");
}
StringBuilder sb = new StringBuilder(n);
for (int i = 0; i < n; i++) {
sb.append(' ');
}
this.s = sb + s + sb;
this.n = n;
label.setFont(new Font("Serif", Font.ITALIC, 36));
label.setText(sb.toString());
this.add(label);
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
#Override
public void actionPerformed(ActionEvent e) {
index++;
if (index > s.length() - n) {
index = 0;
}
label.setText(s.substring(index, index + n));
}
}
I know this is a late answer, but I just saw another question about a marquee that was closed because it was considered a duplicate of this answer.
So I thought I'd add my suggestion which takes a approach different from the other answers suggested here.
The MarqueePanel scrolls components on a panel not just text. So this allows you to take full advantage of any Swing component. A simple marquee can be used by adding a JLabel with text. A fancier marquee might use a JLabel with HTML so you can use different fonts and color for the text. You can even add a second component with an image.
Basic answer is you draw your text / graphic into a bitmap and then implement a component that paints the bitmap offset by some amount. Usually marquees / tickers scroll left so the offset increases which means the bitmap is painted at -offset. Your component runs a timer that fires periodically, incrementing the offset and invalidating itself so it repaints.
Things like wrapping are a little more complex to deal with but fairly straightforward. If the offset exceeds the bitmap width you reset it back to 0. If the offset + component width > bitmap width you paint the remainder of the component starting from the beginning of the bitmap.
The key to a decent ticker is to make the scrolling as smooth and as flicker free as possible. Therefore it may be necessary to consider double buffering the result, first painting the scrolling bit into a bitmap and then rendering that in one go rather than painting straight into the screen.
Here is some code that I threw together to get you started. I normally would take the ActionListener code and put that in some sort of MarqueeController class to keep this logic separate from the panel, but that's a different question about organizing the MVC architecture, and in a simple enough class like this it may not be so important.
There are also various animation libraries that would help you do this, but I don't normally like to include libraries into projects only to solve one problem like this.
public class MarqueePanel extends JPanel {
private JLabel textLabel;
private int panelLocation;
private ActionListener taskPerformer;
private boolean isRunning = false;
public static final int FRAMES_PER_SECOND = 24;
public static final int MOVEMENT_PER_FRAME = 5;
/**
* Class constructor creates a marquee panel.
*/
public MarqueePanel() {
this.setLayout(null);
this.textLabel = new JLabel("Scrolling Text Here");
this.panelLocation = 0;
this.taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
MarqueePanel.this.tickAnimation();
}
}
}
/**
* Starts the animation.
*/
public void start() {
this.isRunning = true;
this.tickAnimation();
}
/**
* Stops the animation.
*/
public void stop() {
this.isRunning = false;
}
/**
* Moves the label one frame to the left. If it's out of display range, move it back
* to the right, out of display range.
*/
private void tickAnimation() {
this.panelLocation -= MarqueePanel.MOVEMENT_PER_FRAME;
if (this.panelLocation < this.textLabel.getWidth())
this.panelLocaton = this.getWidth();
this.textLabel.setLocation(this.panelLocation, 0);
this.repaint();
if (this.isRunning) {
Timer t = new Timer(1000 / MarqueePanel.FRAMES_PER_SECOND, this.taskPerformer);
t.setRepeats(false);
t.start();
}
}
}
Add a JLabel to your frame or panel.
ScrollText s= new ScrollText("ello Everyone.");
jLabel3.add(s);
public class ScrollText extends JComponent {
private BufferedImage image;
private Dimension imageSize;
private volatile int currOffset;
private Thread internalThread;
private volatile boolean noStopRequested;
public ScrollText(String text) {
currOffset = 0;
buildImage(text);
setMinimumSize(imageSize);
setPreferredSize(imageSize);
setMaximumSize(imageSize);
setSize(imageSize);
noStopRequested = true;
Runnable r = new Runnable() {
public void run() {
try {
runWork();
} catch (Exception x) {
x.printStackTrace();
}
}
};
internalThread = new Thread(r, "ScrollText");
internalThread.start();
}
private void buildImage(String text) {
RenderingHints renderHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
BufferedImage scratchImage = new BufferedImage(1, 1,
BufferedImage.TYPE_INT_RGB);
Graphics2D scratchG2 = scratchImage.createGraphics();
scratchG2.setRenderingHints(renderHints);
Font font = new Font("Serif", Font.BOLD | Font.ITALIC, 24);
FontRenderContext frc = scratchG2.getFontRenderContext();
TextLayout tl = new TextLayout(text, font, frc);
Rectangle2D textBounds = tl.getBounds();
int textWidth = (int) Math.ceil(textBounds.getWidth());
int textHeight = (int) Math.ceil(textBounds.getHeight());
int horizontalPad = 600;
int verticalPad = 10;
imageSize = new Dimension(textWidth + horizontalPad, textHeight
+ verticalPad);
image = new BufferedImage(imageSize.width, imageSize.height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
g2.setRenderingHints(renderHints);
int baselineOffset = (verticalPad / 2) - ((int) textBounds.getY());
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, imageSize.width, imageSize.height);
g2.setColor(Color.GREEN);
tl.draw(g2, 0, baselineOffset);
// Free-up resources right away, but keep "image" for
// animation.
scratchG2.dispose();
scratchImage.flush();
g2.dispose();
}
public void paint(Graphics g) {
// Make sure to clip the edges, regardless of curr size
g.setClip(0, 0, imageSize.width, imageSize.height);
int localOffset = currOffset; // in case it changes
g.drawImage(image, -localOffset, 0, this);
g.drawImage(image, imageSize.width - localOffset, 0, this);
// draw outline
g.setColor(Color.black);
g.drawRect(0, 0, imageSize.width - 1, imageSize.height - 1);
}
private void runWork() {
while (noStopRequested) {
try {
Thread.sleep(10); // 10 frames per second
// adjust the scroll position
currOffset = (currOffset + 1) % imageSize.width;
// signal the event thread to call paint()
repaint();
} catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
}
public void stopRequest() {
noStopRequested = false;
internalThread.interrupt();
}
public boolean isAlive() {
return internalThread.isAlive();
}
}
This is supposed to be an improvement of #camickr MarqueePanel. Please see above.
To map mouse events to the specific components added to MarqueePanel
Override add(Component comp) of MarqueePanel in order to direct all mouse events of the components
An issue here is what do do with the MouseEvents fired from the individual components.
My approach is to remove the mouse listeners form the components added and let the MarqueePanel redirect the event to the correct component.
In my case these components are supposed to be links.
#Override
public Component add(Component comp) {
comp = super.add(comp);
if(comp instanceof MouseListener)
comp.removeMouseListener((MouseListener)comp);
comp.addMouseListener(this);
return comp;
}
Then map the component x to a MarqueePanel x and finally the correct component
#Override
public void mouseClicked(MouseEvent e)
{
Component source = (Component)e.getSource();
int x = source.getX() + e.getX();
int y = source.getY();
MarqueePanel2 marqueePanel = (MarqueePanel2) ((JComponent)e.getSource()).getParent();
double x2 = marqueePanel.getWidth();
double x1 = Math.abs(marqueePanel.scrollOffset);
if(x >= x1 && x <= x2)
{
System.out.println("Bang " + x1);
Component componentAt = getComponentAt(x+marqueePanel.scrollOffset, y);
if(comp instanceof MouseListener)
((MouseListener) componentAt).mouseClicked(e);
System.out.println(componentAt.getName());
}
else
{
return;
}
//System.out.println(x);
}
My head gets so messed up working with this right-to-left stuff that I'm not even sure where the problem is, but essentially, I found scroll bars to seem "not quite right" when set to right-to-left orientation. I boiled this down to a test program, which works like this:
The scroll bars start at UP and LEFT (which feels wrong) and you can see purple.
If you resize the window to fit everything, you see that the red is in the upper left.
If you resize the window smaller and move the scroll bars to UP and LEFT, you get purple, which seems wrong.
If the scroll bars are UP and RIGHT, you get the red.
Swiping on the mouse moves the scroll bar in the right position but seems to move the contents in the wrong direction.
I can't tell which bits are backwards and which bits are right, but it seems vaguely correct that the viewport start at the upper right, but the scrollbar being on the left is surely inconsistent. However, if I do try to flip the rendering of the scroll bar, the bar itself doesn't get painted, as if Aqua has detected me trying to cheat somehow.
AquaScrollBarUI does not contain a single call to isLeftToRight(), but maybe the scroll bar is fine and it's the pane itself which is wrong?
I assume this is a Swing bug, so I'll probably be reporting it, but is there an elegant solution to this mess? I am writing a custom look and feel which subclasses Aqua, so that I can put this sort of fix as close as possible to Aqua.
import javax.swing.*;
import java.awt.*;
public class ScrollPaneOrientationTest implements Runnable {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new ScrollPaneOrientationTest());
}
#Override
public void run() {
JPanel view = new ViewPanel();
JScrollPane scrollPane = new JScrollPane(view);
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
frame.add(scrollPane, BorderLayout.CENTER);
frame.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class ViewPanel extends JPanel implements Scrollable {
private static final int SIZE = 1000;
private final Paint paint = new GradientPaint(new Point(0, 0), Color.RED, new Point(SIZE, SIZE), Color.BLUE);
#Override
protected void paintComponent(Graphics g) {
((Graphics2D) g).setPaint(paint);
((Graphics2D) g).fill(new Rectangle(0, 0, SIZE, SIZE));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(400, 400);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 8;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 400;
}
#Override
public boolean getScrollableTracksViewportWidth() {
return false;
}
#Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
}
Here's the solution I'm going with initially. I guess I'll accept my own answer if a long time passes with no better options.
public class HaquaScrollPaneUI extends AquaScrollPaneUI {
#NotNull
#SuppressWarnings("UnusedDeclaration") // called via reflection
public static ComponentUI createUI(JComponent component) {
return new HaquaScrollPaneUI();
}
#Override
protected MouseWheelListener createMouseWheelListener() {
return new FixedXYMouseWheelHandler();
}
protected class FixedXYMouseWheelHandler extends XYMouseWheelHandler {
#Override
public void mouseWheelMoved(MouseWheelEvent event) {
if (event.isShiftDown() &&
!scrollpane.getComponentOrientation().isLeftToRight()) {
// fixes mouse wheel over scroll pane
event = new MouseWheelEvent(
event.getComponent(), event.getID(), event.getWhen(),
event.getModifiers(),
event.getX(), event.getY(),
event.getXOnScreen(), event.getYOnScreen(),
event.getClickCount(), event.isPopupTrigger(),
event.getScrollType(),
-event.getScrollAmount(),
-event.getWheelRotation(),
-event.getPreciseWheelRotation());
}
super.mouseWheelMoved(event);
}
}
}
public class HaquaScrollBarUI extends AquaScrollBarUI {
#NotNull
#SuppressWarnings("UnusedDeclaration") // called via reflection
public static ComponentUI createUI(JComponent component) {
return new HaquaScrollBarUI();
}
#Override
public void update(Graphics graphics, JComponent component) {
if (fScrollBar.getOrientation() == Adjustable.HORIZONTAL &&
!fScrollBar.getComponentOrientation().isLeftToRight()) {
// fixes painting direction
Graphics2D graphicsCopy = (Graphics2D) graphics.create();
try {
graphicsCopy.translate(fScrollBar.getWidth() - 1, 0);
graphicsCopy.scale(-1, 1);
super.update(graphicsCopy, component);
} finally {
graphicsCopy.dispose();
}
} else {
super.update(graphics, component);
}
}
#Override
protected TrackListener createTrackListener() {
// fixes mouse behaviour
return new FlippedTrackListener();
}
private class FlippedTrackListener extends TrackListener {
#Override
public void mouseReleased(MouseEvent event) {
super.mouseReleased(flipEvent(event));
}
#Override
public void mousePressed(MouseEvent event) {
super.mousePressed(flipEvent(event));
}
#Override
public void mouseDragged(MouseEvent event) {
super.mouseDragged(flipEvent(event));
}
// These ones aren't used, but I figured it would be sane
// to override them anyway, just in case.
#Override
public void mouseClicked(MouseEvent event) {
super.mouseClicked(flipEvent(event));
}
#Override
public void mouseEntered(MouseEvent event) {
super.mouseEntered(flipEvent(event));
}
#Override
public void mouseExited(MouseEvent event) {
super.mouseExited(flipEvent(event));
}
#Override
public void mouseWheelMoved(MouseWheelEvent event) {
super.mouseWheelMoved(flipEvent(event));
}
#Override
public void mouseMoved(MouseEvent event) {
super.mouseMoved(flipEvent(event));
}
private MouseEvent flipEvent(MouseEvent event) {
if (fScrollBar.getOrientation() == Adjustable.HORIZONTAL &&
!fScrollBar.getComponentOrientation().isLeftToRight()) {
int x = fScrollBar.getWidth() - 1 - event.getX();
int y = event.getY();
Point scrollBarLocationOnScreen = fScrollBar.getLocationOnScreen();
int xOnScreen = x + scrollBarLocationOnScreen.x;
int yOnScreen = y + scrollBarLocationOnScreen.y;
return new MouseEvent(
event.getComponent(), event.getID(), event.getWhen(),
event.getModifiersEx(),
x, y, xOnScreen, yOnScreen,
event.getClickCount(), event.isPopupTrigger(), event.getButton());
} else {
return event;
}
}
private MouseWheelEvent flipEvent(MouseWheelEvent event) {
if (fScrollBar.getOrientation() == Adjustable.HORIZONTAL &&
!fScrollBar.getComponentOrientation().isLeftToRight()) {
int x = event.getX();
x = fScrollBar.getWidth() - 1 - x;
int y = event.getY();
Point scrollBarLocationOnScreen = fScrollBar.getLocationOnScreen();
int xOnScreen = x + scrollBarLocationOnScreen.x;
int yOnScreen = y + scrollBarLocationOnScreen.y;
return new MouseWheelEvent(
event.getComponent(), event.getID(), event.getWhen(),
event.getModifiersEx(),
x, y, xOnScreen, yOnScreen,
event.getClickCount(), event.isPopupTrigger(),
event.getScrollType(), event.getScrollAmount(),
event.getWheelRotation(),
event.getPreciseWheelRotation());
} else {
return event;
}
}
}
}
I have created a label with overriding its paintComponent (to paint it in a different format with an arc) and located them on a panel. Everything worked fine, however the labels which are located in same location (by .setlocation) are creating problems.
Lets say I have 3 labels at the same location with different shapes. A-B-C
A is first created, then B is created and lastly c is created.
When I click on A, my function paints A and displays "youve clicked A"
But when I click on B or C, it acts like i have clicked on A.
(I'm adding labels in a panel stacked in scroll pane )
Here is the code where i make labels
for(int i=0;i<rowcount;i++){
arcstart = Rawazimuth(azimuth[i]);
//custom JLabel to paint it like an arc
MyLabel FLabel = new MyLabel(100, 50, (int)arcstart,cellid[i],lon[i],lat[i]);
FLabel.setOpaque(true);
Dimension LabelSize = new Dimension( 100,50);
FLabel.setSize(LabelSize);
//locate the JLabel to labels location. it might be same or not
FLabel.setLocation(lon[i], lat[i]);
MyPanel.add(FLabel);
}
this is my custom jlabel class
public MyLabel(int W, int H, int start,int outcellid,int lonn, int latt) {
addMouseListener(this);
arcsizeW = W;
arcsizeH = H;
arcstart = start;
cellid = outcellid;
clicked = false;
lon = lonn;
lat = latt;
}
#Override
public void paintComponent(Graphics g){
// true if button is clicked, so paint acordingly
if(clicked ==true){
g.setColor(dummyg.getColor());
}else{
g.setColor(Color.blue);
}
// draw the arc
g.fillArc(0, 0,arcsizeW , arcsizeH, arcstart, 60);
}
//if cell is clicked, change its color to red and print its cellid
#Override
public void mouseClicked(MouseEvent e) {
System.out.println(cellid);
dummyg = this.getGraphics();
dummyg.setColor(Color.red);
this.paintComponent(dummyg);
clicked = true;
}// other listener stuff}
so how do i prevent this?
i think i can use jlayerpane, but i will need at least 4 layers ( dunno if its ok ).
Make sure you are calling super.paintComponent. This is important to ensure that the component is painting correcting.
Mouse events are like rain. They will hit the first component registered to receive mouse events and stop (like rain hitting an umbrella).
If you have components that overlap, where they overlap, the mouse events go to the first component registered to handle them (in this case A).
Here's a quick example to show what I mean...
As you hover over each circle, it will highlight, try hovering over the middle...
public class OverlappingMouseTest {
public static void main(String[] args) {
new OverlappingMouseTest();
}
public OverlappingMouseTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new BackgroundPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class BackgroundPane extends JLayeredPane {
private CirclePane redPane;
private CirclePane greenPane;
private CirclePane bluePane;
public BackgroundPane() {
redPane = new CirclePane(Color.RED);
greenPane = new CirclePane(Color.GREEN);
bluePane = new CirclePane(Color.BLUE);
add(redPane);
add(greenPane);
add(bluePane);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void doLayout() {
Dimension size = new Dimension(100, 100);
int width = getWidth() - 1;
int height = getHeight() - 1;
Point p = new Point();
p.x = (width / 2) - 50;
p.y = (height / 2) - 75;
redPane.setBounds(new Rectangle(p, size));
p.x = (width / 2) - 75;
p.y = (height / 2) - 25;
bluePane.setBounds(new Rectangle(p, size));
p.x = (width / 2) - 25;
p.y = (height / 2) - 25;
greenPane.setBounds(new Rectangle(p, size));
}
}
protected class CirclePane extends JPanel {
private boolean mouseHover = false;
public CirclePane(Color background) {
setOpaque(false);
setBackground(background);
addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent me) {
mouseHover = false;
repaint();
}
#Override
public void mouseEntered(MouseEvent me) {
mouseHover = true;
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs.create();
if (!mouseHover) {
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
}
g2d.setColor(getBackground());
g2d.fill(new Ellipse2D.Float(0, 0, getWidth() - 1, getHeight() - 1));
g2d.dispose();
}
}
}
Everything worked fine, however the JLabel's which are located in same
location ( by .setlocation ) are creating problems.
Lets say i have 3 Jlabels at the same location with different shapes.
A-B-C A is first created, then B is created and lastly c is created.
there are tree ways, to use
JLayer (Java7), based on JXLayer (Java6)
OverlayLayout
JLayeredPane
I have solved my problem.
The real problem that i found was, Java is interpreting each component with their x,y location and width & height. So even if your label does not overlap ( graphically ) they can be interpreted as same component.
To solve my problem,
I found the angle between mouse click point and component center; then calculated the closest component and reached it.
Layers and such doesn't work in this situation, the only other way around is possibly overriding the contains method for the actualy shape.
When a user clicks on the corner of a JFrame to resize and drags the mouse around, the JFrame redraws based on the current position of the mouse as the user drags. How can you listen to these events?
Below is the what I have currently tried:
public final class TestFrame extends JFrame {
public TestFrame() {
this.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// This is only called when the user releases the mouse button.
System.out.println("componentResized");
}
});
}
// These methods do not appear to be called at all when a JFrame
// is being resized.
#Override
public void setSize(int width, int height) {
System.out.println("setSize");
}
#Override
public void setBounds(Rectangle r) {
System.out.println("setBounds A");
}
#Override
public void setBounds(int x, int y, int width, int height) {
System.out.println("setBounds B");
}
}
How can I determine and constrain how the user resizes a window (based on the current aspect ratio of the window) as they are dragging around the mouse around?
You can add a component listener and implement the componentResized function like that:
JFrame component = new JFrame("My Frame");
component.addComponentListener(new ComponentAdapter()
{
public void componentResized(ComponentEvent evt) {
Component c = (Component)evt.getSource();
//........
}
});
EDIT: Apparently, for JFrame, the componentResized event is hooked to the mouseReleased event. That's why the method is invoked when the mouse button is released.
One way to achieve what you want, is to add a JPanel that will cover the whole area of your JFrame. Then add the componentListener to the JPanel (componentResized for JPanel is called even while your mouse is still dragging). When your frame is resized, your panel will also be resized too.
I know, this isn't the most elegant solution, but it works!
Another workaround (which is very similar to Alex's but a little more straightforward) is to listen to the events from the JFrame's root pane instead:
public final class TestFrame extends JFrame {
public TestFrame() {
this.getRootPane().addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// This is only called when the user releases the mouse button.
System.out.println("componentResized");
}
});
}
}
Depending on other implementation details, it's possible that the root pane might be changed. If that's a possibility then you could override setRootPane() and deal with that. Since setRootPane() is protected and only called from the constructor though, it's unlikely you'll need to do that.
You probably need to override something like validate (don't forget to call the super). Of course, that still may not work if you are using a windowing system to configured to drag outlines.
public class MouseDrag extends Component implements MouseListener,
MouseMotionListener {
/** The Image we are to paint */
Image curImage;
/** Kludge for showStatus */
static Label status;
/** true if we are in drag */
boolean inDrag = false;
/** starting location of a drag */
int startX = -1, startY = -1;
/** current location of a drag */
int curX = -1, curY = -1;
// "main" method
public static void main(String[] av) {
JFrame f = new JFrame("Mouse Dragger");
Container cp = f.getContentPane();
if (av.length < 1) {
System.err.println("Usage: MouseDrag imagefile");
System.exit(1);
}
Image im = Toolkit.getDefaultToolkit().getImage(av[0]);
// create a MouseDrag object
MouseDrag j = new MouseDrag(im);
cp.setLayout(new BorderLayout());
cp.add(BorderLayout.NORTH, new Label(
"Hello, and welcome to the world of Java"));
cp.add(BorderLayout.CENTER, j);
cp.add(BorderLayout.SOUTH, status = new Label());
status.setSize(f.getSize().width, status.getSize().height);
f.pack();
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// "Constructor" - creates the object
public MouseDrag(Image i) {
super();
curImage = i;
setSize(300, 200);
addMouseListener(this);
addMouseMotionListener(this);
}
public void showStatus(String s) {
status.setText(s);
}
// Five methods from MouseListener:
/** Called when the mouse has been clicked on a component. */
public void mouseClicked(MouseEvent e) {
}
/** Called when the mouse enters a component. */
public void mouseEntered(MouseEvent e) {
}
/** Called when the mouse exits a component. */
public void mouseExited(MouseEvent e) {
}
// And two methods from MouseMotionListener:
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
// System.err.println("mouse drag to " + p);
showStatus("mouse Dragged to " + p);
curX = p.x;
curY = p.y;
if (inDrag) {
repaint();
}
}
public void paint(Graphics g) {
int w = curX - startX, h = curY - startY;
Dimension d = getSize();
g.drawImage(curImage, 0, 0, d.width, d.height, this);
if (startX < 0 || startY < 0)
return;
System.err.println("paint:drawRect #[" + startX + "," + startY
+ "] size " + w + "x" + h);
g.setColor(Color.red);
g.fillRect(startX, startY, w, h);
}
}