[英]When does a JPanel paint (or repaint) its child components?
我有一個JButton,它使用自定義UI委托繪制(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 ,
我已經將示例簡化為抗鋸齒,我無法重現該問題。 它似乎不依賴於平台。 我不確定你為什么使用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();
}
});
}
}
要使抗鋸齒一致地工作,您的組件需要為isOpaque
返回false
。 否則,RepaintManager可以跳過繪制組件后面的區域。
我懷疑,如果你使用屏幕放大鏡來查看“不帶鋸條”的邊緣,你會發現它們確實是抗鋸齒的。 但這是針對黑色未上漆的背景,而不是父母的背景。
知道他們正在談論什么的Swing專家很快就會在這里。 在此期間,讓我對此發表評論:
現在,我的問題涉及到這是一個好主意嗎? 畢竟我從子組件重繪父組件。 我想知道這是否會導致重繪的循環? 如果父級嘗試重新繪制其子級並且子級嘗試重新繪制其父級 - 那么我假設我們正在討論循環。
一旦你試一試並發現它在你的機器上不是問題,很可能在你嘗試的所有JVM上都是如此。 也就是說,證據是在布丁中,或隨機的笨拙通常會導致Swing的積極結果。 遞歸循環有一種方法可以使程序在Java中快速停止,所以答案是......如果這是完全錯誤的, 你已經知道了。 另外,你可以將sysouts放在那里,看看是否發生這種情況(顯然不是這樣)。
也就是說,可能有更好的方法來處理您的問題,但如果您的答案有效,請暫時堅持下去。
AFAIK,repaint()只是告訴系統在下一個繪制周期中重新繪制該組件,即如果你在paint()方法中調用repaint(),重繪可能會在下一個周期完成。
通常,Swing在自己的線程中運行,因此重復重繪不應該停止應用程序邏輯。 但是,它可能正在使用處理能力,如果系統總是重新繪制ui,即使沒有變化。
您可以嘗試編寫日志消息,以查看按鈕的繪制時間和頻率。 如果這種情況持續發生,即使沒有任何事情會導致ui更新,您可能必須找到解決方案。 如果沒有,你應該沒事。
編輯:有一個名為RepaintManager的類,您可能會感興趣。
在那里做一個重畫肯定是一個壞主意。 嘗試在installUI()
添加b.setOpaque(false)
installUI()
。 由於抗鋸齒,您的按鈕不再繪制其整個邊界。 您需要讓背景顯示以填補空白。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.