我有一个使用自定义UI委托绘制的JButton(CustomButtonUI扩展了BasicButtonUI)。 CustomButtonUI的paint()方法绘制带有圆角“抗锯齿”角的按钮,使外观尽可能“平滑”。
每当我将鼠标拖过时,按钮的“抗锯齿”边缘就会消失 按钮。这使得按钮边缘看起来“像素化”。但是,一旦我添加了一行代码来重新绘制按钮的父级,即使我将鼠标拖到按钮上,抗锯齿也会启动。
现在,我的问题涉及到这是一个好主意吗?毕竟我从子组件重绘父组件。我想知道这是否会导致重绘的循环?如果是父母 试图重绘它的孩子,孩子们试图重新绘制它的父母 - 然后我假设我们正在谈论一个循环。
我附上了我的代码作为参考。任何评论都非常欢迎!
public class JCustomButtonUI extends BasicButtonUI {
@Override
public void installUI(JComponent c) {
super.installUI(c);
AbstractButton b = (AbstractButton) c;
b.setBorderPainted(false);
}
@Override
public void paint(Graphics g, JComponent c) {
//Cast the Graphics instance to a Graphics2D instance.
Graphics2D g2d = (Graphics2D) g;
JButton b = (JButton) c;
//Repaint parent component to make sure that we get "antialiased"
//edges.
b.getParent().repaint();
//Get the component's height and width.
int w = (int) g.getClipBounds().getWidth();
int h = ((int) g.getClipBounds().getHeight());
//Make sure the button is drawn with "antialiased" edges.
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GRAY);
g2d.fillRoundRect(0, 0, w, h, w, h);
}
}
为了说明别名和抗锯齿边框,请看下面的两张图片。当我(从ButtonUI的paint()方法)手动调用父JPanel的重绘方法时,所有边框都是完全抗锯齿的。但是,当我不手动调用父JPanel的重绘方法时,一旦我将鼠标悬停在按钮上,边框就不再是抗锯齿的了。
我已经分享了整个“组件”,它包含一个JPanel,一个JSlider和一些Snipt上的JButton。请从http://snipt.org/wnllg获取。
似乎我已经设法让它发挥作用。我没有在paintComponent()方法中绘制JPanel的背景,而是创建了一个安装在JPanel上的JCustomPanelUI。我不认为这是解决方案本身,但我没有使用Graphics实例的宽度和高度,而是尝试使用JPanel本身的widht和height。当我使用Graphics实例的宽度和高度时,我不太确定为什么出错了。我认为Graphics实例的宽度和高度已经根据JPanel组件的尺寸进行了“准备”。您可以在此处查看最终组件:http://snipt.org/wnlli,
答案 0 :(得分:4)
我已将示例简化为抗锯齿,我无法重现该问题。它似乎不依赖于平台。我不确定您使用getClipBounds()
的原因。
附录:
JPanel
背景(渐变)需要闪耀......
我更新了示例,在透明按钮后面使用渐变背景;我并排放置了反锯齿(左)和别名(右)的例子。我没有看到意外的行为。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.plaf.basic.BasicButtonUI;
/** @see http://stackoverflow.com/questions/5169647 */
public class ButtonUITest extends JPanel {
public ButtonUITest() {
this.setLayout(new GridLayout(1, 0));
this.setPreferredSize(new Dimension(640, 480));
this.add(new CustomButton(true));
this.add(new CustomButton(false));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = this.getWidth();
int h = this.getHeight();
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(new GradientPaint(0, 0, Color.blue, w, h, Color.red));
g2d.fillRect(0, 0, w, h);
}
private void display() {
JFrame f = new JFrame("ButtonUITest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static class CustomButton extends JButton {
public CustomButton(boolean antialiased) {
this.setOpaque(false);
this.setUI(new CustomButtonUI(antialiased));
}
}
private static class CustomButtonUI extends BasicButtonUI {
private boolean antialiased;
public CustomButtonUI(boolean antialiased) {
this.antialiased = antialiased;
}
@Override
public void paint(Graphics g, JComponent c) {
int w = c.getWidth();
int h = c.getHeight();
Graphics2D g2d = (Graphics2D) g;
if (antialiased) {
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillOval(0, 0, w, 2 * h);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new ButtonUITest().display();
}
});
}
}
答案 1 :(得分:2)
要使抗锯齿功能始终如一,您的组件需要为false
返回isOpaque
。否则,RepaintManager可以跳过绘制组件后面的区域。
我怀疑如果你使用屏幕放大镜来查看“不带锯条”的边缘,你会发现它们确实是抗锯齿的。但这是针对黑色未上漆的背景,而不是父母的背景。
答案 2 :(得分:1)
知道他们正在谈论什么的Swing专家很快就会在这里。与此同时,让我对此发表评论:
现在,我的问题涉及到了 这是一个好主意?我毕竟做了 从a重绘父组件 子组件。我想知道这是不是 到一个重绘的循环?如果是父母 试图重绘它的孩子和 孩子们试图重绘其父母 - 然后我假设我们正在谈论一个 循环。
一旦你试一试并发现它在你的机器上不是问题,你可能会尝试在所有JVM上都是如此。也就是说,证据是在布丁中,或随机的笨拙通常会导致Swing的积极结果。递归循环有一种方法可以使程序在Java中快速停止,所以答案是......如果这完全错误你已经知道了。另外你可以把sysouts放在那里看看如果发生这种情况(显然不是这样)。
尽管如此,可能有更好的方法来解决您的问题,但如果您的答案有效,请暂时坚持下去。
答案 3 :(得分:0)
AFAIK,repaint()只是告诉系统在下一个绘制周期中重新绘制该组件,即 如果你在paint()方法中调用repaint(),重绘可能会在下一个循环中完成。
通常,Swing在自己的线程中运行,因此重复重绘不应该停止应用程序逻辑。但是,它可能正在使用处理能力,如果系统总是重新绘制ui,即使没有变化。
您可以尝试编写日志消息,以查看按钮的绘制时间和频率。 如果这种情况持续发生,即使没有任何事情会导致ui更新,您可能必须找到解决方案。如果没有,你应该没事。
编辑:有一个名为RepaintManager的课程,您可能会感兴趣。
答案 4 :(得分:0)
在那里做一个重绘肯定是一个坏主意。尝试在b.setOpaque(false)
中添加installUI()
。由于抗锯齿,您的按钮不再绘制其整个边界。您需要让背景显示以填补空白。