1 回答
TA贡献1898条经验 获得超8个赞
在 99% 的案例中,您在示例应用程序中看到的任何问题都是由于不透明和/或非不透明组件的不正确混合而发生的。
正如我从代码中看到的那样 - 您正在使用setOpaque(...)来更改各种组件的不透明度,但它非常混乱。很快,opaque属性的作用 - 它会影响 Swing 在需要对特定 UI 元素(面板/标签/按钮/等)进行视觉更新时重新绘制该元素的方式。
例如,当您用鼠标悬停按钮时 - 如果它具有不同的悬停状态,则可能需要重新绘制它,无论它只是一个图标还是稍微/完全不同的样式。这就是不透明度发挥作用的地方——opaque=true组件永远不会在它们自己的“下方”传递重绘调用(用其他/适当的术语来说——传递给它们的父组件)。这意味着如果您在面板上有一个不透明的按钮,并且当它变为“悬停”状态时必须重新绘制它 - 该按钮将是唯一要重新绘制的组件,因为没有理由重新绘制它下面的任何东西,因为它是不透明,你实际上不应该能够看透它,所以图形应该用不透明的颜色填充该按钮边界内的所有像素。
理论上。在实践中,如果您将按钮设置为不透明状态,但保持其图形内容透明或半透明(这显然是一个错误,但 Swing 永远不会告诉您)——您最终会看到各种视觉伪像,例如你在你的应用程序中看到。发生这种情况是因为Graphics2D实现经常在 (0,0) 坐标执行不同的绘制操作以优化它们的速度——知道这一点并不重要,但这部分是为什么你可能会看到其他组件“部分”混合在你的组件边界时是透明的。它比那更复杂一点,但它不应该真正重要,因为它只是一个内部 Swing 优化。
类似的视觉问题也可能是由同一布局上的混合opaque=true和组件引起的。opaque=false您的问题很可能也是如此。我确实很快尝试将您的演示中的所有内容都设置为opaque=false并且它确实解决了问题,但这并不是解决问题的正确方法,特别是如果您想让某些组件不透明。这只是意味着问题在于在单个容器中将具有不同不透明度类型的组件相互混合的方式。
我的个人建议 - 永远不要在一个布局中混合不透明和非不透明组件,即使它们有可能重叠(意味着它们的边界将在布局内相交)。甚至更好——永远不要在单个容器中将组件重叠在一起。使用具有适当非空布局的多个嵌套容器,这也将在以后帮助您轻松修改 UI。
我可以给你一个简单的例子来说明为什么它不好:
/**
* @author Mikle Garin
*/
public class OpacityGlitch
{
public static void main ( String[] args )
{
SwingUtilities.invokeLater ( () -> {
final JFrame frame = new JFrame ( "Opacity glitch sample" );
// Opaque by default
final JPanel panel = new JPanel ( null );
// Opaque by default, but might vary with L&F
final JButton button1 = new JButton ( "1111111" );
panel.add ( button1 );
// Non-opaque to demonstrate the problem
final JButton button2 = new JButton ( "2222222" );
panel.add ( button2 );
// Intersecting buttons
button1.setBounds ( 100, 100, 150, 30 );
button2.setBounds ( 130, 115, 150, 30 );
frame.getContentPane ().add ( panel );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setSize ( 500, 500 );
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
} );
}
}
理论上你应该得到的button1总是在最上面(因为它被更早地添加并且最后被涂在容器上),但在实践中 - 如果你尝试悬停其中一个按钮,那么哪个按钮是完全可见的并且在另一个按钮之上将会改变按钮。发生这种情况是因为两个按钮都是不透明的,并且重绘调用不会通过按钮组件到达它们的容器以及与它们进一步相交的任何内容。button2要解决这种特殊情况,使其成为非不透明就足够了,因为它应该始终保持在 下方button1,并且如果它是非不透明的,它将安全地将重绘调用至少传递给它的容器:
button2.setOpaque ( false );
尽管我个人建议在这种情况下使所有涉及的组件不透明,以避免在将来可能更改组件顺序或由于任何用户交互而发生其他可能的问题——例如,应用程序中的滚动条就是最好的例子。容器不必是非不透明的,因为它会正确地在放置在其中的组件之间传递重绘调用,并且也会正确地重绘自身。
一旦将我上面示例中的按钮更改为非不透明,由于为它们正确处理了重绘,问题就会消失。
对于 Swing 的初学者来说,这可能是一个复杂的话题,但我强烈建议您充分理解为什么会发生这种情况以及如何发生这种情况,否则一旦您的应用程序变大,它将成为您未来的一个大问题。
添加回答
举报