Consider a transparent JPanel
with an overridden paintComponent(Graphics?)
that fills an opaque green circle underneath the cursor's current position (it also fills an opaque red square, always at the panel's top-left). The panel has been add
ed to a transparent, undecorated JFrame
. Some random numbers are also written on-screen to indicate where and where not a repaint is happening. A stray Jbutton
is also present.
Upon moving the cursor, lot of flickering is observed. How to remedy this?
Maybe there's a prototypical way seasoned coders implement simple but dynamic, opaque drawing on transparent JFrames.
code (kotlin) (java acceptable too)
import java.awt.Color
import java.awt.Dimension
import java.awt.Graphics
import java.awt.Point
import java.awt.event.MouseEvent
import java.awt.event.MouseMotionAdapter
import javax.swing.*
import kotlin.random.Random
fun main() {
SwingUtilities.invokeLater {
val pn = object : JPanel() {
var pt: Point?=null
val w=256
val h=w
init {
addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(e: MouseEvent?) {
super.mouseMoved(e)
// efficient repainting from .html
var lpt=pt//make a local copy of a global nullable to keep compiler happy
if (lpt!=null)
repaint(lpt.x-w/2,lpt.y-h/2,w,h)
pt=e?.point
lpt=pt
if (lpt!=null)
repaint(lpt.x-w/2,lpt.y-h/2,w,h)
}
})
}
//few random numbers to show that indeed only the square under the cursor is being repainted
val rand= Random.Default
override fun paintComponent(g: Graphics?) {
super.paintComponent(g)
if (g!=null){
g.color= Color.red
val pt=pt
g.color = Color.red
g.fillRect(10,10,100,100)
g.color=Color.black
g.drawString(rand.nextInt(1000).toString(),10+50,10+50,)
g.color = Color.green
pt?.also {
g.fillOval(pt.x-w/2,pt.y-h/2,w,h)
g.color=Color.black
g.drawString(rand.nextInt(1000).toString(),pt.x,pt.y)
}
}
}
}
pn.add(JButton("hello"))
pn.preferredSize = Dimension(500, 500)
pn.isOpaque = false // panel must be transparent
pn.border = BorderFactory.createLineBorder(Color.black,5)
val fr = JFrame()
fr.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
fr.isUndecorated = true
fr.background=Color(0, 0, 0,0)//notice the 0 alpha
fr.add(pn)
fr.pack()
fr.setLocationRelativeTo(null)
fr.isVisible = true
}
}
Environment
Linux 6.9.3-76060903-generic #202405300957~1738770968~22.04~d5f7c84 SMP PREEMPT_DYNAMIC Wed F x86_64 x86_64 x86_64 GNU/Linux
Pop!_OS 22.04 LTS
GNOME version 42.9
Windowing System X11
openjdk 21.0.6 2025-01-21
OpenJDK Runtime Environment (build 21.0.6+7-Ubuntu-122.04.1)
OpenJDK 64-Bit Server VM (build 21.0.6+7-Ubuntu-122.04.1, mixed mode, sharing)
Screen Refresh Rate: 60.01 Hz and 165.02 Hz (flicker seen in both)
Display scaling: 100% and 125% (flicker seen in both)
Edit(s):
- No flicker when run on Windows 11 instead of the original Linux operating system, with (Microsoft's build of) the same JDK and on same hardware with same screen resolution, refresh rate and scaling.
- No flicker when tested on a Windows 11 virtual machine running in the original Linux OS, using VirtualBox. The VM is pretty watered down compared to the native hardware and yet shows no flicker - may have to do with the JVM build difference.
Consider a transparent JPanel
with an overridden paintComponent(Graphics?)
that fills an opaque green circle underneath the cursor's current position (it also fills an opaque red square, always at the panel's top-left). The panel has been add
ed to a transparent, undecorated JFrame
. Some random numbers are also written on-screen to indicate where and where not a repaint is happening. A stray Jbutton
is also present.
Upon moving the cursor, lot of flickering is observed. How to remedy this?
Maybe there's a prototypical way seasoned coders implement simple but dynamic, opaque drawing on transparent JFrames.
code (kotlin) (java acceptable too)
import java.awt.Color
import java.awt.Dimension
import java.awt.Graphics
import java.awt.Point
import java.awt.event.MouseEvent
import java.awt.event.MouseMotionAdapter
import javax.swing.*
import kotlin.random.Random
fun main() {
SwingUtilities.invokeLater {
val pn = object : JPanel() {
var pt: Point?=null
val w=256
val h=w
init {
addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(e: MouseEvent?) {
super.mouseMoved(e)
// efficient repainting from https://docs.oracle/javase/tutorial/uiswing/painting/refining.html
var lpt=pt//make a local copy of a global nullable to keep compiler happy
if (lpt!=null)
repaint(lpt.x-w/2,lpt.y-h/2,w,h)
pt=e?.point
lpt=pt
if (lpt!=null)
repaint(lpt.x-w/2,lpt.y-h/2,w,h)
}
})
}
//few random numbers to show that indeed only the square under the cursor is being repainted
val rand= Random.Default
override fun paintComponent(g: Graphics?) {
super.paintComponent(g)
if (g!=null){
g.color= Color.red
val pt=pt
g.color = Color.red
g.fillRect(10,10,100,100)
g.color=Color.black
g.drawString(rand.nextInt(1000).toString(),10+50,10+50,)
g.color = Color.green
pt?.also {
g.fillOval(pt.x-w/2,pt.y-h/2,w,h)
g.color=Color.black
g.drawString(rand.nextInt(1000).toString(),pt.x,pt.y)
}
}
}
}
pn.add(JButton("hello"))
pn.preferredSize = Dimension(500, 500)
pn.isOpaque = false // panel must be transparent
pn.border = BorderFactory.createLineBorder(Color.black,5)
val fr = JFrame()
fr.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
fr.isUndecorated = true
fr.background=Color(0, 0, 0,0)//notice the 0 alpha
fr.add(pn)
fr.pack()
fr.setLocationRelativeTo(null)
fr.isVisible = true
}
}
Environment
Linux 6.9.3-76060903-generic #202405300957~1738770968~22.04~d5f7c84 SMP PREEMPT_DYNAMIC Wed F x86_64 x86_64 x86_64 GNU/Linux
Pop!_OS 22.04 LTS
GNOME version 42.9
Windowing System X11
openjdk 21.0.6 2025-01-21
OpenJDK Runtime Environment (build 21.0.6+7-Ubuntu-122.04.1)
OpenJDK 64-Bit Server VM (build 21.0.6+7-Ubuntu-122.04.1, mixed mode, sharing)
Screen Refresh Rate: 60.01 Hz and 165.02 Hz (flicker seen in both)
Display scaling: 100% and 125% (flicker seen in both)
Edit(s):
- No flicker when run on Windows 11 instead of the original Linux operating system, with (Microsoft's build of) the same JDK and on same hardware with same screen resolution, refresh rate and scaling.
- No flicker when tested on a Windows 11 virtual machine running in the original Linux OS, using VirtualBox. The VM is pretty watered down compared to the native hardware and yet shows no flicker - may have to do with the JVM build difference.
1 Answer
Reset to default 0I gave it a try in java, and I merely replaced pt?.also {
with a null check.
Whether that was the cause I do not know. But the pure java version seems to work. I thought the JButton would have been a problem as this comes over the green circle.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Random;
import javax.swing.*;
public class Stov {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JPanel pn = new JPanel() {
Point pt = null;
int w = 256;
int h = w;
{
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
super.mouseMoved(e);
// efficient repainting from https://docs.oracle/javase/tutorial/uiswing/painting/refining.html
Point lpt = pt; //make a local copy of a global nullable to keep compiler happy
if (lpt != null)
repaint(lpt.x - w / 2, lpt.y - h / 2, w, h);
pt = e.getPoint();
lpt = pt;
if (lpt != null)
repaint(lpt.x - w / 2, lpt.y - h / 2, w, h);
}
});
}
//few random numbers to show that indeed only the square under the cursor is being repainted
Random rand = new Random();
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (g != null) {
g.setColor(Color.red);
//val pt=pt;
g.setColor(Color.red);
g.fillRect(10, 10, 100, 100);
g.setColor(Color.black);
g.drawString(String.valueOf(rand.nextInt(1000)), 10 + 50, 10 + 50);
g.setColor(Color.green);
//pt?.also {
if (pt != null) {
g.fillOval(pt.x - w / 2, pt.y - h / 2, w, h);
g.setColor(Color.black);
g.drawString(String.valueOf(rand.nextInt(1000)), pt.x, pt.y);
}
}
}
};
pn.add(new JButton("hello"));
pn.setPreferredSize(new Dimension(500, 500));
pn.setOpaque(false); // panel must be transparent
pn.setBorder(BorderFactory.createLineBorder(Color.black, 5));
JFrame fr = new JFrame();
fr.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
fr.setUndecorated(true);
fr.setBackground(new Color(0, 0, 0, 0));//notice the 0 alpha
fr.add(pn);
fr.pack();
fr.setLocationRelativeTo(null);
fr.setVisible(true);
});
}
}
fr.rootPane
and inpn
(JPanel) by settingisDoubleBuffered
to true has no effect (and was already true anyways) – lineage Commented Feb 17 at 14:18e?.point
isnull
sometimes. – talex Commented Feb 17 at 14:25e
is nullable sopt=e?.point
doesn't execute whenevere
is null. Practically, I have never founde
to benull
– lineage Commented Feb 17 at 16:50