Как реализовать масштабирование в JPanel с нулевым макетом и подпанелями

я создал программу с JScrollPane, эта панель прокрутки включает в себя другую JPanel с нулевым макетом, и эта панель включает в себя несколько других JPanel [я назову их подпанелями] (с некоторыми нарисованными вещами и JLabels), эти подпанели расположены на панели с метод setBounds(int,int,int,int) (и я не хочу это менять)

после поиска хорошего решения для масштабирования я нашел два варианта - масштабирование внутри метода paint/paintComponent (отстой, потому что нарисованный материал изменяется, но не масштаб оси x, y, а не с и высота этих подпанелей - > мой mouselisteners не работает (в неправильном месте)) - масштабирование с помощью области просмотра JScrollPane (вообще не работает (что я нашел в гугле и т.д.))

поэтому я ищу хорошее решение для увеличения и уменьшения масштаба моей JPanel

вот некоторый код, чтобы представить, что я имею в виду

 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Point;
 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.AffineTransform;

 import javax.swing.JFrame;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;


 public class MyAppFrame extends JFrame {

private JScrollPane scroll;
private JPanel nullLayoutPanel;
private JPanel subPanel;
private double zoomFactor;
private double lastScale;
private static Point pressed;

public MyAppFrame(){
    setSize(200, 200);
    setLocation(50, 50);
    zoomFactor = 1.0;
    lastScale = 1.0;

    nullLayoutPanel = new JPanel(){


        //when you try it with the paint method you'll see my problem
        //move the rect by dragging it around then
        //scroll out or in and try to move the rect
        //you'll see that the position of the logical rect doesn't fit with the painted one
        @Override
        public void paintComponent(Graphics g){
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            //g2d.translate(nullLayoutPanel.getWidth()/2, nullLayoutPanel.getHeight()/2);
            g2d.scale(zoomFactor, zoomFactor);
            //g2d.translate(-nullLayoutPanel.getWidth()/2, -nullLayoutPanel.getHeight()/2);
            //super.paint(g2d);
            g2d.dispose();
        }

        @Override
        public Dimension getPreferredSize(){
            return new Dimension((int)(nullLayoutPanel.getWidth()*zoomFactor), (int)(nullLayoutPanel.getHeight()*zoomFactor));
        }

    };
    nullLayoutPanel.setPreferredSize(new Dimension(400,400));
    nullLayoutPanel.setLayout(null);
    nullLayoutPanel.addMouseWheelListener(new MouseWheelListener() {

        @Override
        public void mouseWheelMoved(MouseWheelEvent e) {
            if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL){
                if((.1 * e.getWheelRotation()) > 0 && zoomFactor < 2.0){
                    zoomFactor += (.1 * e.getWheelRotation());
                } else if((.1 * e.getWheelRotation()) < 0 && zoomFactor > 0.1){
                    zoomFactor += (.1 * e.getWheelRotation());
                }
                zoomFactor = Math.round(zoomFactor*10.0)/10.0;
                zoom(e.getPoint());
            }
        }
    });


    scroll = new JScrollPane(nullLayoutPanel);
    scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    getContentPane().add(scroll);

    subPanel = new JPanel(){
        @Override
        public void paint(Graphics g){
            g.setColor(Color.BLUE);
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
        }
    };

    subPanel.addMouseListener(new MouseListener() {

        @Override
        public void mouseReleased(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mousePressed(MouseEvent e) {
            pressed = e.getPoint();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseEntered(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseClicked(MouseEvent e) {
            // TODO Auto-generated method stub

        }
    });
    subPanel.addMouseMotionListener(new MouseMotionListener() {

        @Override
        public void mouseMoved(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseDragged(MouseEvent e) {
            Point pPoint;
            pPoint = subPanel.getLocation();
            int x = (int)(pPoint.x - pressed.getX()+e.getX());
            int y = (int)(pPoint.y - pressed.getY()+e.getY());
            if(x>0&&y>0)
                subPanel.setLocation(x,y);
            repaint();
        }
    });

    subPanel.setBounds(10, 10, 10, 10);
    nullLayoutPanel.add(subPanel);


    setVisible(true);

}

public void alignViewPort(Point mousePosition) {
    // if the scale didn't change there is nothing we should do
    if (zoomFactor != lastScale) {
        // compute the factor by that the image zoom has changed
        double scaleChange = zoomFactor / lastScale;

        // compute the scaled mouse position
        Point scaledMousePosition = new Point(
                (int)Math.round(mousePosition.x * scaleChange),
                (int)Math.round(mousePosition.y * scaleChange)
        );

        // retrieve the current viewport position
        Point viewportPosition = scroll.getViewport().getViewPosition();

        // compute the new viewport position
        Point newViewportPosition = new Point(
                viewportPosition.x + scaledMousePosition.x - mousePosition.x,
                viewportPosition.y + scaledMousePosition.y - mousePosition.y
        );

        // update the viewport position
        // IMPORTANT: This call doesn't always update the viewport position. If the call is made twice
        // it works correctly. However the screen still "flickers".
        scroll.getViewport().setViewPosition(newViewportPosition);

        // debug
        if (!newViewportPosition.equals(scroll.getViewport().getViewPosition())) {
            System.out.println("Error: " + newViewportPosition + " != " + scroll.getViewport().getViewPosition());
        }

        // remember the last scale
        lastScale = zoomFactor;
    }
}

public void zoom(Point p){
    alignViewPort(p);
    nullLayoutPanel.revalidate();
    scroll.repaint();
}

/**
 * @param args
 */
public static void main(String[] args) {
    new MyAppFrame();
}

 }

Надеюсь, вы понимаете мою проблему...

edit: изменен некоторый код (вьюпорт)


person DaMp    schedule 19.09.2014    source источник
comment
Вы искали на сайте? Есть несколько ответов, таких как this.   -  person user1803551    schedule 19.09.2014
comment
У меня та же проблема с этим кодом... нарисованный прямоугольник изменил свой масштаб, но логический прямоугольник (с mouselistener) не изменил свой масштаб, только его положение, но проводным способом...   -  person DaMp    schedule 22.09.2014
comment
Как насчет этого?   -  person user1803551    schedule 22.09.2014
comment
И, кстати, вместо MouseListener можно использовать MouseAdapter, чтобы не реализовывать все методы.   -  person user1803551    schedule 22.09.2014
comment
поэтому я изменил некоторые вещи... теперь область прокрутки меняется проводным способом, но мой нарисованный прямоугольник остается того же размера... когда я уменьшаю масштаб, я бы хотел, чтобы мой нарисованный прямоугольник тоже масштабировался...   -  person DaMp    schedule 23.09.2014
comment
Вы хотите увеличить текущее местоположение мыши?   -  person user1803551    schedule 23.09.2014
comment
Судя по всему, JXLayer подходит для этого, MadProgrammer сделал его доступным здесь.   -  person user1803551    schedule 23.09.2014