为了账号安全,请及时绑定邮箱和手机立即绑定

为什么透明的 JButton 在添加透明度时显示框架的图像?

为什么透明的 JButton 在添加透明度时显示框架的图像?

LEATH 2023-02-23 10:45:47
我正在为 Java 应用程序开发一个 GUI,我想要一个背景图像。问题是我有一种充满按钮的“抽屉”,当被选中时,按钮会以红色突出显示。我正在使用方法 buttonName.setBackground(new Color(255, 102, 102, 200)); 同时设置高亮按钮和透明度。问题是,尽管该方法有效并使按钮透明,但透明度会显示按钮后面框架的随机部分,这些是标题,另一个按钮,按钮所在的 JScrollPane 的滚动条等。按钮仍然显示,并且按钮有效,但背景显示来自其他按钮或框架部分的文本。此外,我意识到,如果我单击一个按钮并将鼠标多次移到所选按钮上,透明度就会开始累积,直至变成纯色。如果有人知道如何制作干净透明的按钮,我将不胜感激。我在这里留下了源代码和我正在使用的测试图像。 https://drive.google.com/file/d/1l8R52WTDyP93L0UhTNd3oorD7Qhv-TcP/view?usp=sharing在这张图片中,您可以看到我遇到的 3 种错误,在第一种错误中,您可以看到背景中有另一个按钮,滚动条显示在按钮的左侧。在第二个中,它是导航面板的标题。在第三个中,我将鼠标多次移到选定的按钮上,它变成了纯色而不是透明的。编辑:如果我将它应用到导航面板,我决定检查错误是否仍然存在,因为当应用程序完成时它也必须是透明的。于是我在上面代码的第82行和83行添加了如下几行代码:        panelNav.setBackground(new Color(0, 0, 0, 200));         panelNav.setOpaque(true);在另一张图片中,我将透明度应用于整个导航面板,这是一个 JLabel。在第一个图像中显示了显示框架时出现的内容,甚至还有部分框架显示在导航面板下方。在第二个中显示了当我使用一次滚动条时会发生什么。编辑 2:我将所有用作 JPanel 的 JLabel 替换为实际的 JPanel。可悲的是,错误仍然存在。我在主 JPanel 中添加了一个额外的按钮,我这样做是因为我认为错误起源于向 JScrollPane 添加按钮。但问题似乎直接在于我如何实现方法 buttonName.setBackground()。这是新版本的代码: https://drive.google.com/file/d/1PuHMkEYNbBoafqs5XiyUaeCkIyXfnHFJ/view ?usp=sharing
查看完整描述

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 的初学者来说,这可能是一个复杂的话题,但我强烈建议您充分理解为什么会发生这种情况以及如何发生这种情况,否则一旦您的应用程序变大,它将成为您未来的一个大问题。


查看完整回答
反对 回复 2023-02-23
  • 1 回答
  • 0 关注
  • 80 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信