Вот плохие новости: это не так прямолинейно, как мы могли бы пожелать, есть ограничение. В Swing графические преобразования применяются только к операции рисования, а не к общему оформлению и событию. Поэтому в Swing зеркальный компонент в основном «непригоден», его нельзя использовать ни для чего другого, кроме отображения зеркального изображения основного компонента. Координаты щелчков мыши и т. Д. Будут неправильными.
Таким образом, это все сложный материал, немного взломать ish.
Существует несколько способов, как вы можете это сделать. Одна из возможностей состоит в том, чтобы использовать два вида на одной модели и сообщить 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 и наблюдатели исходного компонента, потому что это один и тот же тип компонента (View/Controller) на той же модели. Недостатком этого решения является то, что вы создали зеркальный компонент таким образом, который очень специфичен для зеркального компонента.
Другая возможность состоит в том, чтобы создать декораторJComponent
Mirror
, который может отражать любую другую JComponent
. Это немного сложно, как в Java, декораторы не могут переопределять методы декорированного объекта, а Mirror
необходимо обновлять (перекрашивать), а также всякий раз, когда исходный компонент обновляется (перекрашивается).
Вот неполный пример, используя Mirror
, который подключается к соответствующим событиям. Неполный, потому что он перехватывает только DocumentEvent
, но должен также подключаться к другим событиям, например CaretEvent
. Было бы неплохо, если бы у Свинг было что-то вроде PaintEvent
. Насколько мне известно, это не так. (Ну, на самом деле это имеет, но нет соответствующих PaintListener
и addPaintListener()
.) Также неполно, потому что Зеркало не учитывает атрибуты исходного компонента, такие как размер. Он работает только потому, что GridLayout
на MirrorPanel
сохраняет размер зеркала в синхронизации с исходным компонентом.
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()
в случае, если покраска не будет выполнена из-за изменения содержимого, а из-за разрушения буфера (т. Е. Другое окно удалено).
Действительно ли это действительно красиво при изменении размера? И не проще ли рисовать в перевороте, в первую очередь, вместо того, чтобы листать изображение, копируя каждый пиксель? –
Вы потрясающе, я никогда бы не подумал использовать перевернутые пиксели для решения. Благодарю. –