I have some code I found on the internet that allows me to control zooming and panning of a scrollable panel in Java but I want to be able to manipulate the shapes within this area and having trouble translating the x and y coordinates back to the original (unzoomed) dimensions ..
There are a few things I would like to do with these shapes but to start, how can I paint the two entity rectangles red when the mouse moves within them?
Here is code I have so far:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class CanvasPane extends JPanel {
private static Canvas canvas;
public CanvasPane(boolean isDoubleBuffered) {
super(isDoubleBuffered);
setLayout(new BorderLayout());
canvas = new Canvas(1.0);
JScrollPane pane = new JScrollPane(canvas);
pane.getViewport().setBackground(Color.DARK_GRAY);
add(pane, BorderLayout.CENTER);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Test Graphics");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new CanvasPane(true), BorderLayout.CENTER);
frame.pack();
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
//Initial scrolling of the canvas to its center
Rectangle canvasRectangle = canvas.getBounds();
Rectangle visibleRectangle = canvas.getVisibleRect();
double tx = (canvasRectangle.getWidth() - visibleRectangle.getWidth())/2;
double ty = (canvasRectangle.getHeight() - visibleRectangle.getHeight())/2;
visibleRectangle.setBounds((int)tx, (int)ty, visibleRectangle.width, visibleRectangle.height);
canvas.scrollRectToVisible(visibleRectangle);
}
}
class Canvas extends JComponent implements MouseWheelListener, MouseMotionListener, MouseListener {
private static final long serialVersionUID = 1L;
private double zoom = 1.0;
public static final double SCALE_STEP = 0.1d;
private Dimension initialSize;
private Point origin;
private double previousZoom = zoom;
private double scrollX = 0d;
private double scrollY = 0d;
private Rectangle2D workspace = new Rectangle2D.Double(0,0, 1024, 768);
private Rectangle entity1 = new Rectangle(10, 10, 100, 100);
private Rectangle entity2 = new Rectangle(300, 300, 100, 100);
public Canvas(double zoom) {
this.zoom = zoom;
addMouseWheelListener(this);
addMouseMotionListener(this);
addMouseListener(this);
setAutoscrolls(true);
setPreferredSize(new Dimension((int)workspace.getWidth(), (int)workspace.getHeight()));
}
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//Zoom graphics
g2d.scale(zoom, zoom);
//translate graphics to be always in center of the canvas
Rectangle size = getBounds();
double tx = ((size.getWidth() - workspace.getWidth() * zoom) / 2) / zoom;
double ty = ((size.getHeight() - workspace.getHeight() * zoom) / 2) / zoom;
g2d.translate(tx, ty);
//Draw
g2d.setColor(Color.LIGHT_GRAY);
g2d.fill(workspace);
g2d.setColor(Color.DARK_GRAY);
g2d.setStroke(new BasicStroke(5.0f));
g2d.draw(workspace);
g2d.draw(entity1);
g2d.draw(entity2);
}
#Override public void setSize(Dimension size) {
super.setSize(size);
if (initialSize == null) {
this.initialSize = size;
}
}
#Override public void setPreferredSize(Dimension preferredSize) {
super.setPreferredSize(preferredSize);
if (initialSize == null) {
this.initialSize = preferredSize;
}
}
public void mouseWheelMoved(MouseWheelEvent e) {
double zoomFactor = -SCALE_STEP * e.getPreciseWheelRotation() * zoom;
zoom = Math.abs(zoom + zoomFactor);
//Here we calculate new size of canvas relative to zoom.
Dimension d = new Dimension(
(int)(initialSize.width * zoom),
(int)(initialSize.height * zoom));
setPreferredSize(d);
setSize(d);
validate();
followMouseOrCenter(e.getPoint());
previousZoom = zoom;
}
public void followMouseOrCenter(Point2D point) {
Rectangle size = getBounds();
Rectangle visibleRect = getVisibleRect();
scrollX = size.getCenterX();
scrollY = size.getCenterY();
if (point != null) {
scrollX = point.getX() / previousZoom * zoom - (point.getX() - visibleRect.getX());
scrollY = point.getY() / previousZoom * zoom - (point.getY() - visibleRect.getY());
}
visibleRect.setRect(scrollX, scrollY, visibleRect.getWidth(), visibleRect.getHeight());
scrollRectToVisible(visibleRect);
}
public void mouseDragged(MouseEvent e) {
if (origin != null) {
int deltaX = origin.x - e.getX();
int deltaY = origin.y - e.getY();
Rectangle view = getVisibleRect();
view.x += deltaX;
view.y += deltaY;
scrollRectToVisible(view);
}
}
public void mousePressed(MouseEvent e) {
origin = new Point(e.getPoint());
}
public void mouseMoved(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
I have tried to calculate using following code but this isn't quite right
public void mouseMoved(MouseEvent e) {
double x = e.getX() / zoom;
double y = e.getY() / zoom;
double x2 = getWidth() - workspace.getWidth() * zoom;
double y2 = getHeight() - workspace.getHeight() * zoom;
if(x2 > 0) x -= x2;
if(y2 > 0) y -= y2;
Point p = new Point((int)x, (int)y);
if(entity1.contains(p)) {
intersects = true;
}
else {
intersects = false;
}
repaint();
}
This seems to work even better but I am not sure if its the cleanest solution ...
public void mouseMoved(MouseEvent e) {
double x = e.getX() / zoom;
double y = e.getY() / zoom;
// double x2 = getWidth() - workspace.getWidth() * zoom;
// double y2 = getHeight() - workspace.getHeight() * zoom;
double x2 = ((getWidth() - workspace.getWidth() * zoom) / 2) / zoom;
double y2 = ((getHeight() - workspace.getHeight() * zoom) / 2) / zoom;
if(x2 > 0) x -= x2;
if(y2 > 0) y -= y2;
Point p = new Point((int)x, (int)y);
if(entity1.contains(p)) {
intersects = true;
}
else {
intersects = false;
}
repaint();
}
Related
I have below code, I need to alter it so that the balls are generated with a mouse click rather than all of them generating at once. I know I need to use a mouse listener but I do not know how I can integrate that into what I have without "breaking" the app.
No changes needed
import javax.swing.JFrame;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
public class Display {
public final int width;
public final int height;
private JFrame frame;
private boolean closeRequested;
private long lastFrameTime;
private BufferStrategy bufferStrategy;
private Graphics2D graphics;
public Display(int width, int height){
this.width = width;
this.height = height;
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setAutoRequestFocus(true);
frame.setResizable(false);
frame.addWindowListener(new WindowAdapter(){
#Override
public void windowClosing(WindowEvent e){
closeRequested = true;
}
});
Canvas canvas = new Canvas();
canvas.setIgnoreRepaint(true);
canvas.setPreferredSize(new Dimension(width, height));
frame.getContentPane().add(canvas);
frame.setVisible(true);
frame.pack();
frame.setLocationRelativeTo(null);
canvas.createBufferStrategy(2);
bufferStrategy = canvas.getBufferStrategy();
graphics = (Graphics2D) bufferStrategy.getDrawGraphics();
lastFrameTime = System.currentTimeMillis();
}
public boolean isCloseRequested(){
return closeRequested;
}
public void destroy() {
frame.dispose();
}
public void update(){
if (bufferStrategy.contentsLost()){
graphics.dispose();
graphics = (Graphics2D) bufferStrategy.getDrawGraphics();
}
bufferStrategy.show();
}
public Graphics2D getGraphics() {
return graphics;
}
public void sync(int fps) {
if (fps < 1){
return;
}
long currentFrameTime = System.currentTimeMillis();
long deltaTime = currentFrameTime - lastFrameTime;
long timeToSleep = (1000/fps) - deltaTime;
while (System.currentTimeMillis() - currentFrameTime < timeToSleep){
try{
Thread.sleep(1L);
} catch (InterruptedException e) {
}
}
lastFrameTime = System.currentTimeMillis();
}
}
No changes needed
import java.awt.Color;
public class Ball {
public float x;
public float y;
public float sX;
public float sY;
public int radius;
public Color color;
public Ball(float x, float y, float sX, float sY, int radius, Color color){
this.x = x;
this.y = y;
this.sX = sX;
this.sY = sY;
this.radius = radius;
this.color = color;
}
}
This is where I think the mouse listener would be added in for generating the new balls.
How to change addBalls() logic to generate ball with mouse click?
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Random;
public class BouncingBallsApp {
private Display display;
private ArrayList<Ball> balls = new ArrayList<>();
public BouncingBallsApp() {
display = new Display(800,600);
addBalls();
mainLoop();
display.destroy();
}
private void mainLoop() {
while (!display.isCloseRequested()){
updatePhysics();
draw(display.getGraphics());
display.update();
display.sync(60);
}
}
//Question????How to change this logic to generate ball with mouse click
private void addBalls() {
int numberOfBalls = 20;
Random random = new Random();
for (int i = 0; i < numberOfBalls; i++){
int radius = random.nextInt(40) + 10;
int x = random.nextInt(display.width - radius * 2) + radius;
int y = random.nextInt(display.height - radius * 2) + radius;
float sX = random.nextFloat() * 10f + 3f;
float sY = random.nextFloat() * 10f + 3f;
Color color;
switch (random.nextInt(4)){
case 0:
color = Color.red;
break;
case 1:
color = Color.green;
break;
case 2:
color = Color.yellow;
break;
default:
color = Color.blue;
break;
}
Ball ball = new Ball(x, y, sX, sY, radius, color);
balls.add(ball);
}
}
private void updatePhysics() {
for (Ball ball : balls){
ball.x += ball.sX;
ball.y += ball.sY;
if (ball.x - ball.radius < 0){
ball.sX = Math.abs(ball.sX);
} else if (ball.x + ball.radius > display.width){
ball.sX = -Math.abs(ball.sX);
}
if (ball.y - ball.radius < 0){
ball.sY = Math.abs(ball.sY);
} else if (ball.y + ball.radius > display.height){
ball.sY = -Math.abs(ball.sY);
}
}
}
private void draw(Graphics2D g) {
g.setBackground(Color.black);
g.clearRect(0,0, display.width, display.height);
for (Ball ball : balls){
g.setColor(ball.color);
int x = (int) (ball.x - ball.radius);
int y = (int) (ball.y - ball.radius);
int size = ball.radius * 2;
g.fillOval(x, y, size, size);
}
}
public static void main(String[] args) {
new BouncingBallsApp();
}
}
In BouncingBallsApp constructor do the following changes:
public BouncingBallsApp() {
display = new Display(800,600);
//instead of calling add balls directly, use a mouse listener
//addBalls();
display.addMouseListener(getListener());
mainLoop();
display.destroy();
}
Add getListener() method to BouncingBallsApp:
private MouseListener getListener() {
return new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
addBalls(1); //call add balls when mouse pressed
}
};
}
And slightly change addBalls() so that numberOfBalls becomes an argument:
private void addBalls(int numberOfBalls) {
//int numberOfBalls = 20;
.....
Add mouse listener support to Display:
//add mouse listener to canvas
void addMouseListener(MouseListener listener) {
canvas.addMouseListener(listener); //requiers to make canvas a field
}
All done.
To generate balls, simply click the canvas.
(A link to the full code (you can run it online). )
So, I have a program where I can add shapes to JPanel using Path2D objects and then I can click and drag them around. What I want to do is be able to find the final X and Y coordinates of the shape AFTER it has been drug. The coordinates need to be the top-left coordinates. Any ideas?
// add a circle to center of the screen
public void addCircle(int width, int height) {
Path2D circ = new Path2D.Double();
circ.append(new Ellipse2D.Double(getWidth() / 2 - width / 2,
getHeight() / 2 - height / 2, width, height), true);
shapes.add(circ);
repaint();
}
..................
//paint all shapes
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.draw(shape);
}
}
..................
// if the mouse click is in the circle, set pressed=true
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = 0; i < shapes.size(); i++) {
if (shapes.get(i) != null
&& shapes.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
}
}
//if pressed=true, move circle with mouse
#Override
public void mouseDragged(MouseEvent e) {
if (pressed && !lineSelected) {
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
shapes.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
//I need to find the new coordinates here!!!!!!!!!!!
repaint();
}
}
Full Code
class Canvas extends JPanel {
private static final long serialVersionUID = 1L;
private List<Path2D> shapes = new ArrayList<Path2D>();
private int currentIndex;
private Point lineStartingPoint = new Point();
private Point lineEndingPoint = new Point();
private boolean drawing;
private boolean lineSelected;
private Path2D.Double linePath;
private Shapes myShapes = new Shapes();
private List<Path2D> circles = new ArrayList<Path2D>();
public Canvas() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
}
public void setList(ArrayList<UMLCircle> shapes) {
}
public List<UMLCircle> getList() {
return null;
}
public void addSquare(int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
shapes.add(rect2);
repaint();
}
public void addLine() {
lineSelected = true;
repaint();
}
public void addCircle(int width, int height) {
myShapes.addCircle(getWidth() / 2 - width / 2, getHeight() / 2 - height
/ 2, width, height);
Path2D circ = new Path2D.Double();
circ.append(new Ellipse2D.Double(getWidth() / 2 - width / 2,
getHeight() / 2 - height / 2, width, height), true);
circles.add(circ);
shapes.add(circ);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
this.setOpaque(true);
this.setBackground(Color.WHITE);
Graphics2D g2 = (Graphics2D) g;
if (lineSelected) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(2));
g2.drawLine(lineStartingPoint.x, lineStartingPoint.y,
lineEndingPoint.x, lineEndingPoint.y);
}
g2.setStroke(new BasicStroke(2));
for (Path2D shape : shapes) {
g2.draw(shape);
}
}
class MyMouseAdapter extends MouseAdapter {
private boolean pressed = false;
private Point point;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (int i = 0; i < shapes.size(); i++) {
if (shapes.get(i) != null
&& shapes.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
if (circles.get(i) != null
&& circles.get(i).contains(e.getPoint())) {
currentIndex = i;
pressed = true;
this.point = e.getPoint();
}
}
if (lineSelected) {
drawing = true;
lineStartingPoint = e.getPoint();
lineEndingPoint = lineStartingPoint;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed && !lineSelected) {
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
shapes.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
myShapes.updateCircleLocation(currentIndex, point.x, point.y);
System.out.println(point.x + " " + point.y);
repaint();
}
if (drawing) {
lineEndingPoint = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (drawing && lineSelected) {
drawing = false;
lineSelected = false;
lineEndingPoint = e.getPoint();
linePath = new Path2D.Double();
linePath.moveTo(lineStartingPoint.getX(),
lineStartingPoint.getY());
linePath.lineTo(lineEndingPoint.getX(), lineEndingPoint.getY());
shapes.add(linePath);
repaint();
}
pressed = false;
}
}
}
EDIT
Calculation for offset:
int deltaX = e.getX() - point.x;
int deltaY = e.getY() - point.y;
shapes.get(currentIndex).transform(
AffineTransform.getTranslateInstance(deltaX, deltaY));
point = e.getPoint();
int newXPos = e.getX() - deltaX; // final X pos
int newXPos = e.getX() - deltaX; // final Y pos
Before you can do anything, you need to know the offset of the mouse click from the corner of the shape, this will allow you to drag the object so it doesn't suddenly "jump" to your current mouse position...
#Override
public void mousePressed(MouseEvent e) {
//...
for (Shape shape : myShapes) {
//...
this.point = e.getPoint();
int deltaX = point.x - shape.getBounds().x;
int deltaY = point.y - shape.getBounds().y;
offset = new Point(deltaX, deltaY);
//...
}
}
}
Then, you would calculate the delta of change, between the current mouse position and the offset. Because you're applying a translate, you also need to subtract the shape's current location, as the translation is based on the shape's current location and we only want to apply the amount of change
#Override
public void mouseDragged(MouseEvent e) {
if (pressed) {
int index = myShapes.indexOf(clickedShape);
myShapes.remove(index);
int deltaX = e.getPoint().x - offset.x;
int deltaY = e.getPoint().y - offset.y;
clickedShape = new Path2D.Double(clickedShape,
AffineTransform.getTranslateInstance(
deltaX - clickedShape.getBounds().x,
deltaY - clickedShape.getBounds().y));
myShapes.add(index, clickedShape);
point = e.getPoint();
repaint();
}
}
Now, having said all that, don't do this...
protected void paintComponent(Graphics g) {
//...
this.setOpaque(true);
this.setBackground(Color.WHITE);
Changing the state of the component from within the paint method can setup a infinite loop of repaint requests, which can chock your system. Also, the changes you make won't be applied to the current graphics context, as these properties are generally applied by the paint method...
And a working copy....
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
class Canvas extends JPanel {
private static final long serialVersionUID = 1L;
private boolean drawing;
private List<Shape> myShapes = new ArrayList<Shape>();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Canvas());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public Canvas() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
addSquare(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void setList(ArrayList<Shape> shapes) {
}
public List<Shape> getList() {
return null;
}
public void addSquare(int width, int height) {
Path2D rect2 = new Path2D.Double();
rect2.append(new Rectangle(getWidth() / 2 - width / 2, getHeight() / 2
- height / 2, width, height), true);
myShapes.add(rect2);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
for (Shape shape : myShapes) {
g2.draw(shape);
}
}
class MyMouseAdapter extends MouseAdapter {
private boolean pressed = false;
private Point point;
private Point offset;
private Shape clickedShape;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
for (Shape shape : myShapes) {
if (shape != null
&& shape.contains(e.getPoint())) {
System.out.println("Clicked");
pressed = true;
clickedShape = shape;
this.point = e.getPoint();
int deltaX = point.x - shape.getBounds().x;
int deltaY = point.y - shape.getBounds().y;
offset = new Point(deltaX, deltaY);
System.out.println(point + "x" + offset);
repaint();
break;
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (pressed) {
int index = myShapes.indexOf(clickedShape);
myShapes.remove(index);
int deltaX = e.getPoint().x - offset.x;
int deltaY = e.getPoint().y - offset.y;
clickedShape = new Path2D.Double(clickedShape,
AffineTransform.getTranslateInstance(
deltaX - clickedShape.getBounds().x,
deltaY - clickedShape.getBounds().y));
myShapes.add(index, clickedShape);
point = e.getPoint();
repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
offset = null;
pressed = false;
}
}
}
I want to create a Java Application for my parent's Estate Agency holiday let online booking service.
Unfortunately I can't yet post images but they want a kind of slider style booking service in which the user slides the bar to select price, bedrooms etc. The design they have given me uses curved sliders but I can't seem to find any help online. They want 5 sliders in a circle which displays the selected figures and has a button to confirm.
Does anyone have any ideas? Would it involve drawing a circular curve or something like that? Also is it going to be more trouble than it's worth - after all there are online alternative booking systems but it would be nice to have a bespoke one created.
Thanks for you help.
When it comes to GUI components and the details of their look and style and behavior and intended usage, there usualy are infinitely many degrees of freedom.
Should this be solved with a dedicated look and feel? Should it be possible to influence the colors? The width of the "knob"? The start- and end angles of the curve? Would you like to have BoundedRangeModel in the background, to use it as a drop-in-replacement for a JSlider? ....
However, I wrote a very simple sketch, solely based on own painting and mouse listeners: One can modify the minimum- and maximum angles and values, and drag the knob with the mouse.
Due to the lack of details, it is not clear whether this is an appropriate solution for you. It does not have a `BoundedRangeModel´. It does not support listeners (although this would be the easiest to add). There may be some glitches concerning the behavior for border cases, and the solution for these will depend on details that you simply did not specify.
This is what it looks like:
The code as a MCVE:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class CurvedSliderTest
{
public static void main(String[] args) throws IOException
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
CurvedSlider gaugePanel = new CurvedSlider();
f.getContentPane().add(gaugePanel, BorderLayout.CENTER);
JPanel controlPanel = createControlPanel(gaugePanel);
f.getContentPane().add(controlPanel, BorderLayout.NORTH);
f.setSize(600,800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
static JPanel createControlPanel(final CurvedSlider gaugePanel)
{
final JSlider minAngleSlider = new JSlider(0, 100, 0);
final JSlider maxAngleSlider = new JSlider(0, 100, 0);
final JSlider minValueSlider = new JSlider(0, 100, 0);
final JSlider maxValueSlider = new JSlider(0, 100, 0);
final JSlider valueSlider = new JSlider(0, 100, 0);
JPanel controlPanel = new JPanel(new GridLayout(0,2));
controlPanel.add(new JLabel("minAngle"));
controlPanel.add(minAngleSlider);
controlPanel.add(new JLabel("maxAngle"));
controlPanel.add(maxAngleSlider);
controlPanel.add(new JLabel("minValue"));
controlPanel.add(minValueSlider);
controlPanel.add(new JLabel("maxValue"));
controlPanel.add(maxValueSlider);
controlPanel.add(new JLabel("value"));
controlPanel.add(valueSlider);
ChangeListener changeListener = new ChangeListener()
{
#Override
public void stateChanged(ChangeEvent e)
{
double minAngle = minAngleSlider.getValue() / 100.0 * Math.PI * 2;
double maxAngle = maxAngleSlider.getValue() / 100.0 * Math.PI * 2;
double minValue = minValueSlider.getValue() / 100.0;
double maxValue = maxValueSlider.getValue() / 100.0;
double value = valueSlider.getValue() / 100.0;
gaugePanel.setAngles(minAngle, maxAngle);
gaugePanel.setRange(minValue, maxValue);
gaugePanel.setValue(value);
}
};
minAngleSlider.addChangeListener(changeListener);
maxAngleSlider.addChangeListener(changeListener);
minValueSlider.addChangeListener(changeListener);
maxValueSlider.addChangeListener(changeListener);
valueSlider.addChangeListener(changeListener);
minAngleSlider.setValue(50);
maxAngleSlider.setValue(0);
minValueSlider.setValue(10);
maxValueSlider.setValue(90);
valueSlider.setValue(50);
return controlPanel;
}
}
class CurvedSlider extends JPanel implements MouseListener, MouseMotionListener
{
private double minAngleRad = 0.0;
private double maxAngleRad = 0.0;
private double minValue = 0.0;
private double maxValue = 0.0;
private double value = 0.0;
CurvedSlider()
{
addMouseListener(this);
addMouseMotionListener(this);
}
void setAngles(double minAngleRad, double maxAngleRad)
{
this.minAngleRad = minAngleRad;
this.maxAngleRad = maxAngleRad;
repaint();
}
void setRange(double minValue, double maxValue)
{
this.minValue = minValue;
this.maxValue = maxValue;
repaint();
}
void setValue(double value)
{
this.value = value;
repaint();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0,0,getWidth(),getHeight());
boolean printValues = false;
printValues = true;
if (printValues)
{
int ty = 20;
g.setColor(Color.BLACK);
g.drawString("minAngle "+Math.toDegrees(minAngleRad), 20, ty+=20);
g.drawString("maxAngle "+Math.toDegrees(maxAngleRad), 20, ty+=20);
g.drawString("minValue "+minValue, 20, ty+=20);
g.drawString("maxValue "+maxValue, 20, ty+=20);
g.drawString("value "+value, 20, ty+=20);
}
double alpha = (value - minValue) / (maxValue - minValue);
double angleRad = minAngleRad + alpha * (maxAngleRad - minAngleRad);
double radius = Math.min(getWidth(), getHeight()) / 3.0;
final double thickness = 15;
double xC = getWidth() / 2.0;
double yC = getHeight() / 2.0;
double x0 = xC + Math.cos(angleRad) * (radius - thickness);
double y0 = yC - Math.sin(angleRad) * (radius - thickness);
double x1 = xC + Math.cos(angleRad) * radius;
double y1 = yC - Math.sin(angleRad) * radius;
Shape background0 = new Arc2D.Double(
xC-radius, yC-radius,
radius+radius, radius+radius,
Math.toDegrees(minAngleRad),
Math.toDegrees(maxAngleRad-minAngleRad),
Arc2D.PIE);
Shape background1 = new Ellipse2D.Double(
xC-radius+thickness, yC-radius+thickness,
radius+radius-thickness-thickness,
radius+radius-thickness-thickness);
Area a = new Area(background0);
a.subtract(new Area(background1));
g.setColor(Color.GRAY);
g.fill(a);
g.setStroke(new BasicStroke(3.0f,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.setColor(Color.LIGHT_GRAY);
g.draw(a);
g.setStroke(new BasicStroke(8.0f,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.setColor(Color.BLACK);
g.draw(new Line2D.Double(x0, y0, x1, y1));
}
private void updateAngle(Point p)
{
double xC = getWidth() / 2.0;
double yC = getHeight() / 2.0;
double dx = p.getX() - xC;
double dy = p.getY() - yC;
double angleRad = Math.atan2(-dy, dx);
if (angleRad < -Math.PI / 2)
{
angleRad = 2 * Math.PI + angleRad;
}
angleRad = Math.max(maxAngleRad, Math.min(minAngleRad, angleRad));
double alpha = (angleRad - minAngleRad) / (maxAngleRad - minAngleRad);
double value = minValue + alpha * (maxValue - minValue);
setValue(value);
}
#Override
public void mouseDragged(MouseEvent e)
{
updateAngle(e.getPoint());
}
#Override
public void mouseMoved(MouseEvent e)
{
}
#Override
public void mousePressed(MouseEvent e)
{
updateAngle(e.getPoint());
}
#Override
public void mouseClicked(MouseEvent e)
{
}
#Override
public void mouseReleased(MouseEvent e)
{
}
#Override
public void mouseEntered(MouseEvent e)
{
}
#Override
public void mouseExited(MouseEvent e)
{
}
}
How to draw circle on mouse dragged event, and then how to move that circle on mouse dragged event in Java?
My code is below.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class r extends JPanel {
public int x1, x2, y1, y2, r, w, h,xDist,yDist;
public static boolean flag = false, pressFlag = false;
public r() {
setBackground(Color.WHITE);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent m) {
// pressFlag = true;
if (r > (int) Math.sqrt(Math.abs(m.getX() - x1) * Math.abs(m.getX() - x1) + Math.abs(m.getY() - y1) * Math.abs(m.getY() - y1))) {
flag = true;
yDist=xDist=x2 = y2 = 0;
} else {
x1 = y1 = 0;
r=x2 = y2 = 0;
x1 = m.getX();
y1 = m.getY();
}
repaint();
}
public void mouseReleased(MouseEvent m) {
w = x2 - x1;
h = y2 - y1;
r = (int) Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
flag = false;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent m) {
if (flag && (x2!=0 && y2!=0)) {
xDist=(m.getX()-x2);
yDist=(m.getY()-y2);
}
x2 = m.getX();
y2 = m.getY();
repaint();
}
});
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (flag) {
x1=x1+xDist;
y1=y1+yDist;
g.drawOval(x1, y1, w, h);
} else {
g.drawOval(x1, y1, x2 - x1, y2 - y1);
}
}
}
public class q extends JFrame {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setSize(300, 300);
jFrame.add(new r());
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Something along those lines works nicely for me:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DragCircle extends JPanel {
private final class MouseDrag extends MouseAdapter {
private boolean dragging = false;
private Point last;
#Override
public void mousePressed(MouseEvent m) {
last = m.getPoint();
dragging = isInsideEllipse(last);
if (!dragging) {
x = last.x;
y = last.y;
width = 0;
height = 0;
}
repaint();
}
#Override
public void mouseReleased(MouseEvent m) {
last = null;
dragging = false;
repaint();
}
#Override
public void mouseDragged(MouseEvent m) {
int dx = m.getX() - last.x;
int dy = m.getY() - last.y;
if (dragging) {
x += dx;
y += dy;
} else {
width += dx;
height += dy;
}
last = m.getPoint();
repaint();
}
}
private int x;
private int y;
private int width;
private int height;
private MouseDrag mouseDrag;
public DragCircle() {
setBackground(Color.WHITE);
mouseDrag = new MouseDrag();
addMouseListener(mouseDrag);
addMouseMotionListener(mouseDrag);
}
public boolean isInsideEllipse(Point point) {
return new Ellipse2D.Float(x, y, width, height).contains(point);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawOval(x, y, width, height);
}
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setSize(300, 300);
jFrame.add(new DragCircle());
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I'm working on a Java program that displays a map (inherited from JComponent) within a JScrollPane. When the MouseWheelListener fires, the map zooms and the JScrollPane's viewport is adjusted to center on the location of the mouse.
This all works fine, except that the call to setSize(Dimension d) forces the map to repaint immediately before the view is adjusted, causing a "stutter." However, I cannot adjust the view until after setSize has completed execution or the calculations for "centering" the viewport will be haywire (due to getHeight() and getWidth() calls,) therefore the viewport adjustment is within a runnable called with invokeLater.
I would like to find a way to move directly from the previous map size and viewport location to the new view, without seeing the scene repainted twice.
setIgnoreRepaint(boolean) did not work for me. Is there another way to go about this?
EDIT: Here's what I worked up from your sample code that replicates my issue, although not as noticably as there's far less computation going on in the drawing. If you scroll rapidly over the image, you'll see that there's a brief stutter between the resizing of the hexagons to their new size and the adjustment of the viewport to its new position.
You can see the hexagons being re-drawn twice. (Once when the setSize() method is called and once when the setViewPosition() method is called.)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class ZoomWithSelectionInViewport implements MouseWheelListener{
private int zoom = 80;
JComponent b;
int hexSize = 3;
public ZoomWithSelectionInViewport() throws Exception{
b = new JComponent() {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(700, 700);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = ((Graphics2D) g);
int vertOffsetX, vertOffsetY, horizOffsetX, horizOffsetY;
vertOffsetX = (int)((double)hexSize* Math.sqrt(3.0f));
vertOffsetY = (int)((double)-hexSize-1* Math.sqrt(3.0f)/2.0f);
horizOffsetX = (int) ((double)hexSize* Math.sqrt(3.0f));
horizOffsetY = (int) ((double)hexSize+1* Math.sqrt(3.0f)/2.0f);
for(int x = 0; x < 50; x++)
{
for(int y = 0; y < 50; y++)
{
int[] xcoords = new int[6]; int[] ycoords = new int[6];
for(int i = 0; i < 6; i++)
{
xcoords[i] = (int)((hexSize+x * horizOffsetX + y * vertOffsetX) + (double)hexSize * Math.cos(i * 2 * Math.PI / 6));
ycoords[i] = (int)(((getSize().height /2 )+ x * horizOffsetY + y * vertOffsetY) + (double)hexSize * Math.sin(i * 2 * Math.PI / 6));
}
g2d.setStroke(new BasicStroke(hexSize/2.5f));
g2d.setColor(Color.GRAY);
g2d.drawPolygon(xcoords, ycoords, 6);
}
}
}
};
JScrollPane view = new JScrollPane(b);
b.addMouseWheelListener(this);
JFrame f = new JFrame();
f.setLocation(10, 10);
f.setDefaultCloseOperation(3);
f.add(view);
f.setSize(500,500);
f.setVisible(true);
view.setWheelScrollingEnabled(false);
}
public void mouseWheelMoved(MouseWheelEvent e) {
zoom = 100*-Integer.signum(e.getWheelRotation());
if(hexSize - Integer.signum(e.getWheelRotation()) > 0)
hexSize-= Integer.signum(e.getWheelRotation());
Dimension targetSize = new Dimension(b.getWidth()+zoom,b.getHeight()+zoom);
b.setPreferredSize(targetSize);
b.setSize(targetSize);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JViewport tempView = (JViewport)b.getParent();
tempView.setViewPosition(new Point(b.getWidth()/2,b.getHeight()/2));
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
ZoomWithSelectionInViewport example = new ZoomWithSelectionInViewport();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
my curiosity, no idea what's happends, could you please use this SSCCE add there your issues and edit with the code your question
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class ZoomWithSelectionInViewport {
private Point startPoint = new Point(0, 0);
private Point rectLocale = new Point();
private Dimension rectSize = new Dimension();
private int zoom = 80;
private BufferedImage capture = null;
private BufferedImage raw;
public ZoomWithSelectionInViewport() throws Exception {
raw = new Robot().createScreenCapture(new Rectangle(
Toolkit.getDefaultToolkit().getScreenSize()));
MouseBehavior behavior = new MouseBehavior();
JPanel b = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(500, 500);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = ((Graphics2D) g);
g2d.drawImage(raw, 0, 0, null);
if (capture != null) {
int width2 = (int) (rectSize.width + rectSize.width * (zoom / 500d));
int height2 = (int) (rectSize.height + rectSize.height * (zoom / 500d));
int x2 = rectLocale.x - ((width2 - rectSize.width) / 2);
int y2 = rectLocale.y - ((height2 - rectSize.height) / 2);
Image scaledInstance = capture.getScaledInstance(
width2, height2, Image.SCALE_AREA_AVERAGING);
g2d.drawImage(scaledInstance, x2, y2, null);
g2d.drawRect(x2, y2, width2, height2);
} else {
g2d.draw(new Rectangle(rectLocale, rectSize));
}
}
};
b.addMouseMotionListener(behavior);
b.addMouseListener(behavior);
b.addMouseWheelListener(behavior);
JFrame f = new JFrame();
f.setLocation(10, 10);
f.setDefaultCloseOperation(3);
f.add(b);
f.pack();
f.setVisible(true);
}
private class MouseBehavior extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
startPoint = e.getPoint();
rectLocale = new Point();
rectSize = new Dimension();
capture = null;
if (e.getSource() instanceof JComponent) {
((JComponent) e.getSource()).repaint();
}
}
#Override
public void mouseDragged(MouseEvent e) {
Point currentPoint = e.getPoint();
rectSize.width = Math.abs(currentPoint.x - startPoint.x);
rectSize.height = Math.abs(currentPoint.y - startPoint.y);
if (e.isShiftDown()) {
rectSize.width = rectSize.height = Math.min(rectSize.width, rectSize.height);
int dx = startPoint.x - rectSize.width;
int dy = startPoint.y - rectSize.height;
rectLocale.x = startPoint.x < currentPoint.x ? startPoint.x : Math.max(dx, dy);
rectLocale.y = startPoint.y < currentPoint.y ? startPoint.y : Math.min(dx, dy);
} else {
rectLocale.x = Math.min(currentPoint.x, startPoint.x);
rectLocale.y = Math.min(currentPoint.y, startPoint.y);
}
if (e.getSource() instanceof JComponent) {
((JComponent) e.getSource()).repaint();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (rectSize.width <= 0 || rectSize.height <= 0) {
capture = null;
} else {
capture = raw.getSubimage(Math.max(0, rectLocale.x),
Math.max(0, rectLocale.y), rectSize.width, rectSize.height);
}
if (e.getSource() instanceof JComponent) {
((JComponent) e.getSource()).repaint();
}
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
zoom = Math.min(2000, Math.max(0, zoom + e.getUnitsToScroll() * 10));
if (e.getSource() instanceof JComponent) {
((JComponent) e.getSource()).repaint();
}
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
ZoomWithSelectionInViewport example = new ZoomWithSelectionInViewport();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
an alternative could be
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
public class ZoomDemo {
private PaintSurface canvas = new PaintSurface();
private JFrame frame = new JFrame();
private AffineTransform aT = new AffineTransform();
private Point2D p1 = null;
private Point2D p2 = null;
public ZoomDemo() {
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
ScaleListener scaleListener = new ScaleListener();
canvas.addMouseWheelListener(scaleListener);
canvas.addMouseListener(scaleListener);
frame.add(canvas);
frame.setVisible(true);
}
public class ScaleListener extends MouseAdapter {
private double scale = 1;
#Override
public void mouseClicked(MouseEvent e) {
p1 = e.getPoint();
try {
p2 = aT.inverseTransform(p1, new Point2D.Double());
/*
* p1 is the point relative to canvas where the user physically
* held the mouse.
*
* Since you may want to deal with a virtual mouse location
* relative to an untransformed canvas, you inverse transform p1
* to p2.
*
* For example: when the user held the mouse over, let's say,
* the displayed left upper corner of the red rectangle.
*
* p2 now will point to the upper left corner of the red
* rectangle in an untransformed canvas.
*/
applyScale();
} catch (NoninvertibleTransformException e1) {
e1.printStackTrace();
}
canvas.repaint();
}
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (p1 != null && p2 != null) {
scale -= (0.05 * e.getWheelRotation());
if (scale > 5) {
scale = 5;
}
if (scale < 1) {
scale = 1;
aT.setToIdentity();
} else {
applyScale();
}
canvas.repaint();
}
}
private void applyScale() {
aT.setToIdentity();
// *** variation one (your implementation)
aT.translate(p1.getX(), p1.getY());
aT.scale(scale, scale);
aT.translate(-p2.getX(), -p2.getY());
// *** variation two
// aT.translate(p1.getX(), p1.getY());
// aT.scale(scale, scale);
// aT.translate(-p1.getX(), -p1.getY());
// *** variation three
// aT.translate(p2.getX(), p2.getY());
// aT.scale(scale, scale);
// aT.translate(-p2.getX(), -p2.getY());
}
}
public class PaintSurface extends JComponent {
private static final long serialVersionUID = 1L;
{
this.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
/*
* Override paintComponent, not paint!!!
*/
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
try {
g2.setColor(Color.black);
g2.fillRect(0, 0, getWidth(), getHeight());
// g2.setTransform(aT); <<<<<<<<< !!!!!!!
/*
* A transform (translation for example) may already have been
* applied to the Graphics object by a parent. This is removed
* by setTransform.
*/
g2.transform(aT); // <<<<<<<<<< !!!!!!!
g2.setColor(Color.red);
g2.drawRect(50, 50, 100, 100);
g2.setColor(Color.blue);
g2.drawRect(200, 200, 150, 50);
if (p2 != null) {
g2.setColor(Color.green);
g2.fill(new Rectangle2D.Double(p2.getX() - 4, p2.getY() - 4, 8, 8));
}
} finally {
g2.dispose();
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ZoomDemo zoomDemo = new ZoomDemo();
}
});
}
}
same question,
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
//http://stackoverflow.com/questions/6819243/jscrollpane-jumping-when-scrollbars-start-being-used
public class LockViewPortToPoint extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] arg) {
LockViewPortToPoint lockViewPortToPoint = new LockViewPortToPoint();
}
public LockViewPortToPoint() {
initComponents();
setVisible(true);
}
private void initComponents() {
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(600, 600);
setPreferredSize(new Dimension(600, 600));
add(new TopPanel());
}
private class TopPanel extends JPanel {
private static final long serialVersionUID = 1L;
private JScrollPane scrollPane;
TopPanel() {
setPreferredSize(new Dimension(500, 500));
scrollPane = new JScrollPane(new InteriorPanel());
scrollPane.setPreferredSize(new Dimension(500, 500));
scrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(10, 490));
scrollPane.getHorizontalScrollBar().setPreferredSize(new Dimension(490, 10));
scrollPane.setWheelScrollingEnabled(false);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
add(scrollPane);
}
}
private class InteriorPanel extends JPanel {
private static final long serialVersionUID = 1L;
private double scale = 10.0;
private final double scaleModifier = 0.1;
private final int width = 10;
private Point loc = new Point(0, 0);
private final int SIZE = 10;
private Point orig = new Point(250, 250);
InteriorPanel() {
super(true);
setPreferredSize(new Dimension((int) (scale * width * SIZE), (int) (scale * width * SIZE)));
this.addMouseWheelListener(new MapMouseWheelListener());
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2D.scale(scale, scale);
for (int row = 0; row <= SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if ((col + row) % 2 == 0) {
g2D.setColor(Color.white);
} else {
g2D.setColor(Color.black);
}
g2D.fillRect(col * width, row * width, width, width);
}
}
}
private void incrementScale(int notches) {
double modifier = 0;
final double prevScale = scale;
if (notches != 0) {
modifier = 1.0 + -notches / Math.abs(notches) * scaleModifier;
}
scale *= Math.pow(modifier, Math.abs(notches));
/*if (scale * width < 1) {
scale = 1.0/width;
} else if (scale * width * 3 > parentHeight || scale * width * 3 > parentWidth) {
if (parentHeight > parentWidth) {
scale = parentWidth / 3.0 / width;
} else {
scale = parentHeight / 3.0 / width;
}
} else if (scale * width * SIZE < parentWidth) {
scale = parentWidth / (double)SIZE / width;
} else if (scale * width * SIZE < parentHeight) {
scale = parentHeight / (double)SIZE / width;
}*/
setPreferredSize(new Dimension((int) (scale * width * SIZE), (int) (scale * width * SIZE)));
orig = new Point(((int) (scale * width * SIZE)) / 2, ((int) (scale * width * SIZE) / 2));
final JViewport viewport = ((JViewport) (getParent().getParent().getComponent(0)));
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
viewport.setViewPosition(new Point(
orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
orig.y - (int) Math.round(loc.y * (1 - scale / prevScale))));
}
});
/*viewport.scrollRectToVisible(new Rectangle(new Point(
orig.x - (int) Math.round(loc.x * (1 - scale / prevScale)),
orig.y - (int) Math.round(loc.y * (1 - scale / prevScale))))); */
System.out.println(orig + "\n " + loc + "\n " + (1 - scale / prevScale));
revalidate();
repaint();
}
private class MapMouseWheelListener implements MouseWheelListener {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
loc = e.getPoint();
incrementScale(e.getWheelRotation());
}
}
}
}
another example
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
//http://stackoverflow.com/questions/115103/how-do-you-implement-position-sensitive-zooming-inside-a-jscrollpane
public class FPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private Dimension preferredSize = new Dimension(400, 400);
private Rectangle2D[] rects = new Rectangle2D[50];
public static void main(String[] args) {
JFrame jf = new JFrame("test");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setSize(400, 400);
jf.add(new JScrollPane(new FPanel()));
jf.setVisible(true);
}
public FPanel() {
// generate rectangles with pseudo-random coords
for (int i = 0; i < rects.length; i++) {
rects[i] = new Rectangle2D.Double(
Math.random() * .8, Math.random() * .8,
Math.random() * .2, Math.random() * .2);
}
// mouse listener to detect scrollwheel events
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
updatePreferredSize(e.getWheelRotation(), e.getPoint());
}
});
}
private void updatePreferredSize(int wheelRotation, Point stablePoint) {
double scaleFactor = findScaleFactor(wheelRotation);
scaleBy(scaleFactor);
Point offset = findOffset(stablePoint, scaleFactor);
offsetBy(offset);
getParent().doLayout();
revalidate();
repaint();
}
private double findScaleFactor(int wheelRotation) {
double d = wheelRotation * 1.08;
return (d > 0) ? 1 / d : -d;
}
private void scaleBy(double scaleFactor) {
int w = (int) (getWidth() * scaleFactor);
int h = (int) (getHeight() * scaleFactor);
preferredSize.setSize(w, h);
}
private Point findOffset(Point stablePoint, double scaleFactor) {
int x = (int) (stablePoint.x * scaleFactor) - stablePoint.x;
int y = (int) (stablePoint.y * scaleFactor) - stablePoint.y;
return new Point(x, y);
}
private void offsetBy(Point offset) {
Point location = getLocation();
setLocation(location.x - offset.x, location.y - offset.y);
}
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
private Rectangle2D r = new Rectangle2D.Float();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
int w = getWidth();
int h = getHeight();
for (Rectangle2D rect : rects) {
r.setRect(rect.getX() * w, rect.getY() * h,
rect.getWidth() * w, rect.getHeight() * h);
((Graphics2D) g).draw(r);
}
}
}