如何正确更新透明的秋千覆盖?

时间:2017-02-24 20:26:54

标签: java swing overlay swingworker

我根据答案使用透明点击覆盖(使用jna启用点击) https://stackoverflow.com/a/28772306/1093872。 然而,实际的可见叠加总是落后一步,即。当在组件上调用repaint()时,将显示应在先前更新中显示的更新。

起初,由于线程不同,我怀疑内存不一致问题。但我也使用SwingWorker尝试sure-to-be-correct方法,但仍面临同样的问题。

The overlay only updated after the next call to repaint, although I used a generous 2000ms delay

AWTUtilities.setWindowOpaque(w, false);似乎以某种方式导致了问题,因为在我将其注释掉或将其设置为true后,问题就会消失,但不幸的是窗口也不透明(显然)。

The update is correct after disabling the transparency

我怀疑这个问题可能与双缓冲有关(缓冲区在下一次绘制之后只绘制后没有交换),但我真的不知道。我还尝试在组件上调用setDoubleBuffered(false)setDoubleBuffered(true),但这不会改变任何内容。

此外,我意识到问题与jna部分无关,因为在删除之后,问题仍然完全相同。

请注意,索引打印与绘图同时发生,因此我知道paintComponent会及时调用,但可见更新仅在下次调用时发生。

关于可能是什么原因以及如何解决这个问题的任何想法?

import com.sun.awt.AWTUtilities;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

import javax.swing.*;
import java.awt.*;

public class OverlayUpdateTest {

    private static MyJComponent myJComponent;

    public static void main(String[] args) {
        setupOverlayWindow();

        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                myJComponent.increaseIndex();
                myJComponent.repaint();

                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private static void setupOverlayWindow() {
        Window w = new Window(null);

        myJComponent = new MyJComponent();
        w.add(myJComponent);
        w.pack();
        w.setLocationRelativeTo(null);
        w.setVisible(true);
        w.setAlwaysOnTop(true);
        /**
         * This sets the background of the window to be transparent.
         */
        AWTUtilities.setWindowOpaque(w, false);
        setTransparent(w);
    }

    private static void setTransparent(Component w) {
        WinDef.HWND hwnd = getHWnd(w);
        int wl = User32.INSTANCE.GetWindowLong(hwnd, WinUser.GWL_EXSTYLE);
        wl = wl | WinUser.WS_EX_LAYERED | WinUser.WS_EX_TRANSPARENT;
        User32.INSTANCE.SetWindowLong(hwnd, WinUser.GWL_EXSTYLE, wl);
    }

    /**
     * Get the window handle from the OS
     */
    private static WinDef.HWND getHWnd(Component w) {
        WinDef.HWND hwnd = new WinDef.HWND();
        hwnd.setPointer(Native.getComponentPointer(w));
        return hwnd;
    }

    private static class MyJComponent extends JComponent {
        private int index = 0;

        /**
         * This will draw a black cross on screen.
         */
        protected void paintComponent(Graphics g) {
            g.setColor(Color.BLACK);
            g.fillRect(0, getHeight() / 2 - 10, getWidth(), 20);
            g.fillRect(getWidth() / 2 - 10, 0, 20, getHeight());
            g.setColor(Color.RED);
            g.drawString("i: " + index, 10, getFont().getSize() + 10);
            System.out.println("i: " + index);
        }

        public Dimension getPreferredSize() {
            return new Dimension(100, 100);
        }

        void increaseIndex() {
            index++;
        }
    }
}

1 个答案:

答案 0 :(得分:0)

经过多次反复试验,我找到了一个有效的解决方案......

Window w = new Window(null);更改为Window w = new JWindow();解决了这个问题。我不知道为什么,但我很高兴。