Я использую графику с двойной буферизацией в своем подклассе JLayer для реализации простой анимации смахивания в Java приложении Swing. Он отлично работает на старых дисплеях, но когда я запускаю его на Retina-дисплее, экран теряет удвоенное разрешение при запуске анимации и возвращает его после завершения. Я не уверен, как сохранить более высокое разрешение во время анимации.
Мой метод анимации изначально выглядел так:
private void animate() {
Timer timer = new Timer(frameMillis, null);
final ActionListener actionListener = (evt) -> { /* omitted for brevity */ };
timer.addActionListener(actionListener);
int imageType = BufferedImage.TYPE_INT_ARGB;
upcomingScreen = new BufferedImage(liveComponent.getWidth(), liveComponent.getHeight(), imageType);
Graphics2D graphics2D = (Graphics2D) upcomingScreen.getGraphics();
liveComponent.paint(graphics2D); // liveComponent is a JComponent
graphics2D.dispose();
timer.start();
}
Я пытался удвоить размер изображения, но это не help.
upcomingScreen = new BufferedImage(liveComponent.getWidth()*2, liveComponent.getHeight()*2, imageType);
Чтобы отразить эти изменения, я изменил свой код рисования в LayerUI, удвоив xLimit
, width
, height
:
public void paint(final Graphics g, final JComponent c) {
if (isAnimating) {
int xLimit = (c.getWidth()*2 * frame) / maxFrames;
int width = c.getWidth()*2;
int height = c.getHeight()*2;
g.drawImage(uScreen, 0, 0, xLimit, height, 0, 0, xLimit, height, c);
g.drawImage(pScreen, xLimit, 0, width, height, xLimit, 0, width, height, c);
} else {
super.paint(g, c);
}
}
Это не так Помогите. Это dr aws то же самое с последним изменением или без него, что не имеет смысла.
Вот класс, который иллюстрирует проблему:
<code>/**
* <p>Created by IntelliJ IDEA.
* <p>Date: 5/2/20
* <p>Time: 10:25 AM
*
* @author Miguel Mu\u00f1oz
*/
@SuppressWarnings({"HardcodedLineSeparator", "StringConcatenation", "HardCodedStringLiteral", "DuplicatedCode"})
public final class SwipeViewTest extends JPanel {
public static final String text1 = "Demo of Swipe View.\n\nThe swipe button will toggle between two pages of text. It has a built-in " +
"special effect, which is a swipe. When you hit the swipe button, it should flip between two pages of text. This worked fine on " +
"the older displays, but for some reason, on a Retina display, the text briefly switches to low resolution as the swipe proceeds, " +
"then switches back once it has finished. This code is written for retina displays. I don't know if it will work for the older, " +
"low resolution displays.\n\nYou can watch it swipe by hitting the space bar or by clicking the swipe button.";
public static final String text2 = "Demo of Swipe View.\n\nThis is the second page of the swipe-text demo. The change in resolution is " +
"most easily noticed when watching the line at the top, which doesn't change as the swipe is performed.";
private final SwipeView<TestView> swipeView;
private final TestView testView;
public static void main(String[] args) {
JFrame frame = new JFrame("SwipeView demo");
SwipeViewTest comp = new SwipeViewTest();
comp.install();
frame.add(comp);
frame.setLocationByPlatform(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
private boolean page1 = true;
private SwipeViewTest() {
super(new BorderLayout());
testView = new TestView();
swipeView = SwipeView.wrap(testView, 1000);
add(BorderLayout.CENTER, swipeView.getLayer());
}
private void install() {
JButton jButton = new JButton("Swipe");
jButton.addActionListener(this::doSwipe);
add(jButton, BorderLayout.PAGE_END);
AncestorListener ancestorListener = new AncestorListener() {
@Override
public void ancestorAdded(final AncestorEvent event) {
JComponent button = event.getComponent();
button.requestFocus();
button.removeAncestorListener(this); // execute only once.
}
@Override public void ancestorRemoved(final AncestorEvent event) { }
@Override public void ancestorMoved(final AncestorEvent event) { }
};
jButton.addAncestorListener(ancestorListener);
}
private void doSwipe(ActionEvent ignored) {
swipeView.swipeLeft(this::flipPage);
}
private void flipPage() {
page1 = !page1;
if (page1) {
testView.setText(text1);
} else {
testView.setText(text2);
}
}
private static class TestView extends JPanel {
private final JTextArea textArea;
TestView() {
super(new BorderLayout());
textArea = new JTextArea(20, 40);
JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
textArea.setText(text1);
add(scrollPane, BorderLayout.CENTER);
}
private void setText(String text) {
textArea.setText(text);
}
}
/**
* SwipeView adds a swipe special effect to a Component. This draws a swipe-right or swipe-left effect on a chosen
* action. It also optionally supports a repeated action when the mouse is held down.
* <p>
* This class is very specific right now, but I hope to generalize it for other special effects later.
* <p>Created by IntelliJ IDEA.
* <p>Date: 4/4/18
* <p>Time: 12:38 AM
*
* @author Miguel Mu\u00f1oz
*/
@SuppressWarnings("MagicNumber")
public static final class SwipeView<C extends JComponent> extends LayerUI<C> {
public static <J extends JComponent> SwipeView<J> wrap(J view, int durationMillis) {
JLayer<J> jLayer = new JLayer<>(view);
final SwipeView<J> ui = new SwipeView<>(view, jLayer, durationMillis);
jLayer.setUI(ui);
return ui;
}
private final C liveComponent;
private Image priorScreen = null;
private Image upcomingScreen = null;
private final JLayer<C> layer;
private boolean isAnimating = false;
private SwipeDirection swipeDirection = SwipeDirection.SWIPE_RIGHT;
private final int maxFrames;
// Calculated:
@SuppressWarnings("FieldCanBeLocal")
private final int frameMillis;
private int frame = 0;
private final long startTime = System.currentTimeMillis();
private SwipeView(C view, JLayer<C> theLayer, int animationDurationMillis) {
super();
liveComponent = view;
layer = theLayer;
maxFrames = (30 * animationDurationMillis) / 1000;
frameMillis = animationDurationMillis / maxFrames;
}
public JLayer<C> getLayer() { return layer; }
/**
* Perform the specified operation with a swipe-right special effect. This is often used in an ActionListener:
* <pre>
* first.addActionListener((e) -> swipeView.swipeRight(recordModel::goFirst));
*
* Здесь слушатель Action выполнит Swipe -после выполнения метода goFirst () объекта recordModel. * * @param operation Операция * / @SuppressWarnings ("WeakerAccess") publi c void swipeRight (Runnable операция) {swipe (операция, SwipeDirection.SWIPE_RIGHT); } / ** * Выполнение указанной операции со специальным эффектом смахивания влево. Это часто используется в ActionListener: *
* first.addActionListener((e) -> swipeView.swipeLeft(recordModel::goFirst));
*
* Здесь слушатель Action будет выполнять Swipe-Left после выполнения метода goFirst () для recordModel. * * @param operation Операция * / @SuppressWarnings ("WeakerAccess") publi c void swipeLeft (Операция Runnable) {swipe (операция, SwipeDirection.SWIPE_LEFT); } закрытое частное сканирование (операция Runnable, SwipeDirection swipeDirection) {prepareToAnimate (swipeDirection); operation.run (); одушевленные (); } // @SuppressWarnings ({"HardCodedStringLiteral", "HardcodedFileSeparator"}) @Override publi c void paint (final Graphics g, final JComponent c) {if (isAnimating) {int xLimit = (c .getWidth) () * 2 * frame) / maxFrames; if (swipeDirection == SwipeDirection.SWIPE_LEFT) {xLimit = (c .getWidth () * 2) - xLimit; } int width = c .getWidth () * 2; int height = c .getHeight () * 2; // // noinspection UseOfSystemOutOrSystemErr // System.out.printf ("Размеры: фрейм:% d /% d (при% d) xLimit:% 4d (% 4d x% 4d) (из% 4d x% 4d) Анимация: % b% n ", // frame, maxFrames, System.currentTimeMillis () - startTime, xLimit, ширина, высота, c .getWidth (), c .getHeight (), isAnimating); assert upcomingScreen! = null; assert priorScreen! = null; Изображение pScreen = Objects.requireNonNull (priorScreen); Изображение uScreen = Objects.requireNonNull (предстоящий экран); if (swipeDirection == SwipeDirection.SWIPE_RIGHT) {g.drawImage (uScreen, 0, 0, xLimit, высота, 0, 0, xLimit, высота, c); g.drawImage (pScreen, xLimit, 0, ширина, высота, xLimit, 0, ширина, высота, c); } else {g.drawImage (uScreen, xLimit, 0, ширина, высота, xLimit, 0, ширина, высота, c); g.drawImage (pScreen, 0, 0, xLimit, высота, 0, 0, xLimit, высота, c); }} else {super.paint (g, c); }} private void prepareToAnimate (SwipeDirection swipeDirection) {this.swipeDirection = swipeDirection; isAnimating = true; кадр = 0; // Сохранение текущего состояния priorScreen = new BufferedImage (liveComponent.getWidth () * 2, liveComponent.getHeight () * 2, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics2D = (Graphics2D) priorScreen.getGraphics (); liveComponent.paint (Graphics2D); graphics2D.dispose (); } private void animate () {Timer timer = new Timer (frameMillis, null); final ActionListener actionListener = (evt) -> {frame ++; layer.repaint (); if (frame == maxFrames) {frame = 0; isAnimating = false; таймер. стоп(); // Расследуем: у меня течет таймер? }}; timer.addActionListener (ActionListener); upcomingScreen = new BufferedImage (liveComponent.getWidth () * 2, liveComponent.getHeight () * 2, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics2D = (Graphics2D) upcomingScreen.getGraphics (); liveComponent.paint (Graphics2D); graphics2D.dispose (); timer.start (); }} publi c stati c enum SwipeDirection {@SuppressWarnings ("JavaDo c") SWIPE_RIGHT, @SuppressWarnings ("JavaDo c") SWIPE_LEFT}}