Java 如何在带有空布局和子面板的JPanel中实现缩放

Java 如何在带有空布局和子面板的JPanel中实现缩放,java,jpanel,zooming,jscrollpane,null-layout-manager,Java,Jpanel,Zooming,Jscrollpane,Null Layout Manager,我用一个JScrollPane构建了一个程序。这个滚动窗格包括另一个具有空布局的JPanel,这个面板包括一些其他JPanel[我将它们命名为子面板](带有一些绘制的东西和JLabel) 这些子面板用setBounds(int,int,int,int,int)方法定位在面板中(我不想改变这一点) 在寻找一个很好的缩放解决方案后,我找到了两个选择 -放大“绘制/绘制组件”方法的内部(因为绘制的内容已更改,但x、y轴的比例未更改,且这些子面板的宽度和高度未更改,所以效果不佳->我的鼠标侦听器无法工作

我用一个JScrollPane构建了一个程序。这个滚动窗格包括另一个具有空布局的JPanel,这个面板包括一些其他JPanel[我将它们命名为子面板](带有一些绘制的东西和JLabel) 这些子面板用setBounds(int,int,int,int,int)方法定位在面板中(我不想改变这一点)

在寻找一个很好的缩放解决方案后,我找到了两个选择 -放大“绘制/绘制组件”方法的内部(因为绘制的内容已更改,但x、y轴的比例未更改,且这些子面板的宽度和高度未更改,所以效果不佳->我的鼠标侦听器无法工作(在错误的位置)) -使用JScrollPane的视口进行缩放(根本不起作用(我在google等上找到的结果)

因此,我正在寻找一个很好的解决方案来放大和缩小我的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();
}

 }
导入java.awt.Color;
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.Point;
导入java.awt.event.MouseEvent;
导入java.awt.event.MouseListener;
导入java.awt.event.MouseMotionListener;
导入java.awt.event.mouseweelEvent;
导入java.awt.event.MouseWheelListener;
导入java.awt.geom.AffineTransform;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JScrollPane;
公共类MyAppFrame扩展了JFrame{
私有JScrollPane滚动条;
私人JPanel nullLayoutPanel;
私人JPanel子面板;
私人双重动物因子;
私人双表;
私有静态点按下;
公共MyAppFrame(){
设置大小(200200);
设定位置(50,50);
zoomFactor=1.0;
lastScale=1.0;
nullLayoutPanel=newjpanel(){
//当你用画法尝试时,你会发现我的问题
//然后通过拖动矩形来移动矩形
//向外或向内滚动并尝试移动矩形
//您将看到逻辑rect的位置与绘制的位置不匹配
@凌驾
公共组件(图形g){
超级组件(g);
Graphics2D g2d=(Graphics2D)g.create();
//translate(nullLayoutPanel.getWidth()/2,nullLayoutPanel.getHeight()/2);
g2d.比例(缩放因子,缩放因子);
//translate(-nullLayoutPanel.getWidth()/2,-nullLayoutPanel.getHeight()/2);
//超级油漆(g2d);
g2d.dispose();
}
@凌驾
公共维度getPreferredSize(){
返回新维度((int)(nullLayoutPanel.getWidth()*zoomFactor),(int)(nullLayoutPanel.getHeight()*zoomFactor));
}
};
nullLayoutPanel.setPreferredSize(新维度(400400));
nullLayoutPanel.setLayout(null);
nullLayoutPanel.addMouseWheelListener(新的MouseWheelListener(){
@凌驾
已移动公共无效鼠标滚轮(鼠标滚轮事件e){
如果(例如getScrollType()==MouseWheelEvent.WHEEL\u单元\u滚动){
如果(.1*e.getWheelRotation())>0&&zoomFactor<2.0){
zoomFactor+=(.1*e.getWheelRotation());
}如果(.1*e.getWheelRotation())<0&&zoomFactor>0.1,则为else{
zoomFactor+=(.1*e.getWheelRotation());
}
zoomFactor=Math.round(zoomFactor*10.0)/10.0;
缩放(如getPoint());
}
}
});
滚动=新的JScrollPane(nullLayoutPanel);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HorizontalScrollBar\uAlways);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL\u SCROLLBAR\u ALWAYS);
getContentPane().add(滚动);
子面板=新的JPanel(){
@凌驾
公共空间涂料(图g){
g、 setColor(Color.BLUE);
g、 fillRect(0,0,this.getWidth(),this.getHeight());
}
};
子面板addMouseListener(新MouseListener(){
@凌驾
公共无效MouseEvent(MouseEvent e){
//TODO自动生成的方法存根
}
@凌驾
公共无效鼠标按下(MouseEvent e){
按下=e.getPoint();
}
@凌驾
公共无效mouseExited(MouseEvent e){
//TODO自动生成的方法存根
}
@凌驾
公共无效鼠标事件(鼠标事件e){
//TODO自动生成的方法存根
}
@凌驾
公共无效mouseClicked(MouseEvent e){
//TODO自动生成的方法存根
}
});
子面板addMouseMotionListener(新的MouseMotionListener(){
@凌驾
public void mouseMoved(MouseEvent e){
//TODO自动生成的方法存根
}
@凌驾
公共无效鼠标标记(鼠标事件e){
点对点;
pPoint=subPanel.getLocation();
intx=(int)(pPoint.x-pressed.getX()+e.getX());
int y=(int)(pPoint.y-pressed.getY()+e.getY());
如果(x>0&&y>0)
子面板设置位置(x,y);
重新油漆();
}
});
第10、10、10、10、10子面板;
nullLayoutPanel.add(子面板);
setVisible(真);
}
公共空心对齐视口(点鼠标位置){
//如果比例没有改变,我们就什么都不应该做
如果(缩放因子!=lastScale){
//计算图像缩放已更改的系数
双尺度变换=缩放因子/最后尺度;
//计算缩放的鼠标位置
点scaledMousePosition=新点(
(int)数学圆(mousePosition.x*scaleChange),
(int)数学圆(mousePosition.y*scaleChange)
);
//检索当前视口位置
Point viewportPosition=滚动.getViewport().getViewPosition();
//计算新视口位置
Point newViewportPosition=新点(
viewportPosition.x+scaledMousePosition.x-mousePosition.x,
争夺