【问题标题】:Mirroring JLabel or JTextArea镜像 JLabel 或 JTextArea
【发布时间】:2015-03-05 21:18:20
【问题描述】:

我需要获取 JLabel 或 JTextArea 的镜像。

http://www.subirimagenes.net/i/150305101716451074.jpg

如果我使用 JTextArea,我需要完全镜像这些字母。 如果我使用 JLabel,我需要格式和镜像字母。

示例是在 Photoshop 上创建的。

我的想法是使用 graphics(),但我不知道该怎么做。

【问题讨论】:

    标签: java mirror


    【解决方案1】:

    这是一个坏消息:它并不像我们希望的那样直截了当,有一个限制。在 Swing 中,图形变换只应用于绘制操作,而不是一般的布局和事件过程。因此,在 Swing 中,镜像组件基本上是“无法使用”的,它只能用于显示主组件的镜像。鼠标点击坐标等会出错。

    因此,这都是一些棘手的事情,有点hackish。

    有多种方法可以做到这一点。 一种可能性是在一个模型上使用两个视图,并告诉其中一个视图的Graphics 水平翻转。

    这里有一个例子,展示了一个翻转的JEditorPane

    import java.awt.*;
    import java.awt.geom.*;
    import javax.swing.*;
    
    public class MirrorText {
        public static void main(final String... args) {
            SwingUtilities.invokeLater(MirrorText::setupUI);
        }
        public static void setupUI() {
            final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
            final JEditorPane mirroredEditor = new JEditorPane("text/html", "") {
                protected Graphics getComponentGraphics(final Graphics g) {
                    return horizontalFlip(super.getComponentGraphics(g), getWidth());
                }
            };
            mirroredEditor.setDocument(editor.getDocument());
    
            final JFrame frame = new JFrame("mirrored label");
            final JPanel mirrorPanel = new JPanel(new GridLayout(1, 2));
            mirrorPanel.add(new JScrollPane(editor));
            mirrorPanel.add(new JScrollPane(mirroredEditor));
            frame.add(mirrorPanel);
            frame.pack();
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.setVisible(true);
        }
        public static Graphics horizontalFlip(final Graphics g, final int width) {
            final Graphics2D g2d = (Graphics2D) g;
            final AffineTransform tx = g2d.getTransform();
            tx.scale(-1.0, 1.0);
            tx.translate(-width, 0);
            g2d.setTransform(tx);
            return g2d;
        }
    }
    

    这种解决方案的优点是完全镜像原始组件的 MVC 和观察者,因为它是相同模型上的相同类型的组件(视图/控制器)。 此解决方案的缺点是您创建镜像组件的方式非常特定于被镜像的组件。

    另一种可能性是创建一个 装饰器 JComponent Mirror 可以镜像任意其他 JComponent。这有点棘手,因为在 Java 中,装饰器不能覆盖被装饰对象的方法,并且每当更新(重绘)原始组件时,Mirror 也需要更新(重绘)。

    这是一个使用Mirror 的不完整示例,它与相应的事件挂钩。不完整,因为它只挂钩DocumentEvent,但也应该挂钩其他事件,例如CaretEvent。如果 Swing 有类似 PaintEvent 的东西,那就太好了。据我所知,还没有。 (嗯,事实上它有,但是没有对应的PaintListeneraddPaintListener()。) 也是不完整的,因为镜像没有观察到原始组件的属性,如大小。它之所以有效,是因为MirrorPanel 上的GridLayout 使镜像大小与原始组件保持同步。

    import java.awt.*;
    import java.awt.geom.*;
    import javax.swing.*;
    import javax.swing.event.*;
    
    public class MirrorText {
        public static void main(final String... args) {
            SwingUtilities.invokeLater(MirrorText::setupUI);
        }
        public static void setupUI() {
            final JEditorPane editor = new JEditorPane("text/html", "<h1>Mirrored</h1><p>This is mirrored text.</p>");
            final MirrorPanel mirrorPanel = new MirrorPanel(new JScrollPane(editor));
    
            editor.getDocument().addDocumentListener(new DocumentListener() {
                public void changedUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
                public void insertUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
                public void removeUpdate(final DocumentEvent e) { mirrorPanel.updateMirror(); }
            });
    
            final JFrame frame = new JFrame("mirrored label");
            frame.add(mirrorPanel);
            frame.pack();
            frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
            frame.setVisible(true);
        }
    }
    
    class MirrorPanel extends JPanel {
        public MirrorPanel(final JComponent c) {
            super(new GridLayout(1, 2));
            add(c);
            add(new Mirror(c));
        }
        public void updateMirror() {
            repaint();
        }
    }
    
    class Mirror extends JComponent {
        private final JComponent mirroredComponent;
        public Mirror(final JComponent mirroredComponent) {
            this.mirroredComponent = mirroredComponent;
        }
        public static Graphics horizontalFlip(final Graphics g, final int width) {
            final Graphics2D g2d = (Graphics2D) g;
            final AffineTransform tx = g2d.getTransform();
            tx.scale(-1.0, 1.0);
            tx.translate(-width, 0);
            g2d.setTransform(tx);
            return g2d;
        }
        public void paint(final Graphics g) {
            mirroredComponent.paint(horizontalFlip(g, mirroredComponent.getWidth()));
        }
    }
    

    可能还有更多的可能性。例如,可以覆盖镜像组件的paint() 方法来更新镜像组件。这样可以避免收到通知,但会导致不必要的paint() 调用,以防由于内容更改但由于缓冲区破坏(即其他窗口移开)而未完成绘制。

    【讨论】:

    • 1+ 用于使用 Graphics2D scale() 和 translate() 方法,如果我正确阅读问题,我会建议 :)
    • 感谢您的回复,我正在尝试您的解决方案。乍一看可以,谢谢你的建议。如果这两个答案中的任何一个有效,我将编辑我的帖子。
    【解决方案2】:

    这是创建镜像的一种方法。

    基本上,您在 BufferedImage 上打印 JTextArea 的内容。然后在 X 轴上反转 BufferedImage 的像素。

    package com.ggl.testing;
    
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.image.BufferedImage;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextArea;
    import javax.swing.SwingUtilities;
    import javax.swing.event.DocumentEvent;
    import javax.swing.event.DocumentListener;
    
    public class MirrorImage implements Runnable {
    
        private JFrame frame;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new MirrorImage());
        }
    
        @Override
        public void run() {
            frame = new JFrame("Mirror Image Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            JPanel mainPanel = new JPanel();
            mainPanel.setLayout(new FlowLayout());
    
            JPanel textPanel = new JPanel();
    
            JTextArea textArea = new JTextArea(15, 30);
            textPanel.add(textArea);
    
            mainPanel.add(textPanel);
    
            MirrorPanel mirrorPanel = new MirrorPanel();
            mirrorPanel.setPreferredSize(textPanel.getPreferredSize());
    
            mainPanel.add(mirrorPanel);
    
            TextListener listener = new TextListener(textArea, mirrorPanel);
            textArea.getDocument().addDocumentListener(listener);
    
            frame.add(mainPanel);
    
            frame.pack();
            frame.setLocationByPlatform(true);
    
            listener.createImage(textArea);
    
            frame.setVisible(true);
        }
    
        private class MirrorPanel extends JPanel {
    
            private static final long serialVersionUID = 2496058019297247364L;
    
            private Image image;
    
            public void setImage(Image image) {
                this.image = image;
                repaint();
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.drawImage(image, (getWidth() - image.getWidth(this)) / 2,
                        (getHeight() - image.getHeight(this)) / 2, this);
            }
        }
    
        private class TextListener implements DocumentListener {
    
            private JTextArea textArea;
    
            private MirrorPanel mirrorPanel;
    
            public TextListener(JTextArea textArea, MirrorPanel mirrorPanel) {
                this.textArea = textArea;
                this.mirrorPanel = mirrorPanel;
            }
    
            @Override
            public void insertUpdate(DocumentEvent event) {
                createImage(textArea);
            }
    
            @Override
            public void removeUpdate(DocumentEvent event) {
                createImage(textArea);
            }
    
            @Override
            public void changedUpdate(DocumentEvent event) {
                createImage(textArea);
            }
    
            public void createImage(JTextArea textArea) {
                BufferedImage img = new BufferedImage(textArea.getWidth(),
                        textArea.getHeight(), BufferedImage.TYPE_INT_RGB);
    
                Graphics2D g2d = img.createGraphics();
                textArea.printAll(g2d);
                g2d.dispose();
    
                createMirrorImage(img);
                mirrorPanel.setImage(img);
            }
    
            private void createMirrorImage(BufferedImage img) {
                int width = img.getWidth();
                int height = img.getHeight();
    
                int[][] pixels = new int[width][height];
    
                for (int i = width - 1; i >= 0; i--) {
                    int j = width - i - 1;
                    for (int k = 0; k < height; k++) {
                        pixels[j][k] = img.getRGB(i, k);
                    }
                }
    
                for (int i = 0; i < width; i++) {
                    for (int j = 0; j < height; j++) {
                        img.setRGB(i, j, pixels[i][j]);
                    }
                }
            }
    
        }
    
    }
    

    【讨论】:

    • 这在调整大小时真的表现得很好吗?与通过复制每个像素来翻转图像相比,首先以翻转的方式进行绘制不是更简单吗?
    • 你太棒了,我从来没有想过使用倒置像素来解决。谢谢。
    猜你喜欢
    • 2013-10-22
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 2014-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多