I'm attempting to create a customized JProgressBar that utilizes the JLayer class so that it can be colored differently depending on the situation, a la this solution. The thing is that I want to wrap it up as a JComponent of some sort since it makes it more manageable. I would expose it as a customized JLayer, but that class is sealed, so nothing doing.
I've tried just making it a JComponent, but nothing is drawn on the screen (probably because I have no idea how to make a custom JComponent that contains other components inside of it?). I've tried a JFrame, which works, but the sizing is all wrong, likely because the progress bar is using the layout manager of the JFrame I made instead of the layout manager containing the JFrame. I've tried JProgressBar, which draws, but then I have no way of returning the JLayer and preserving the correct hierarchy without additional method calls after the constructor, which just doesn't seem elegant.
Here's my sample code, based heavily on the code from the question I linked to above:
public class Test {
public JComponent makeUI() {
final BoundedRangeModel model = new DefaultBoundedRangeModel();
model.setValue(20);
final JPanel p = new JPanel(new GridLayout(4, 1, 12, 12));
p.setBorder(BorderFactory.createEmptyBorder(24,24,24,24));
// This does not draw
final ColorProgressBar pb4 = new ColorProgressBar(model);
p.add(pb4);
JPanel panel = new JPanel(new BorderLayout());
panel.add(p, BorderLayout.NORTH);
return panel;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception e) {
e.printStackTrace();
}
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Test().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ColorProgressBar extends JComponent {
private static final long serialVersionUID = -1265489165072929352L;
private BlockedColorLayerUI colorUI = new BlockedColorLayerUI();
private JLayer<JProgressBar> layer;
private JProgressBar bar;
private PropertyChangeSupport supporter = new PropertyChangeSupport(new WeakReference<ColorProgressBar>(this));
public ColorProgressBar(BoundedRangeModel model) {
bar = new JProgressBar(model);
layer = new JLayer<JProgressBar>(bar, colorUI);
this.add(layer);
}
public Color getColor() {
if (colorUI == null)
return null;
return colorUI.color;
}
public void setColor(Color color) {
Color oldColor = colorUI.color;
colorUI.color = color;
supporter.firePropertyChange("color", oldColor, color);
}
@Override
public void paintComponents(Graphics g) {
layer.paintComponents(g);
}
class BlockedColorLayerUI extends LayerUI<JProgressBar> {
public Color color = null;
private BufferedImage bi;
private int prevw = -1;
private int prevh = -1;
@Override public void paint(Graphics g, JComponent c) {
if(color != null) {
JLayer<?> jlayer = (JLayer<?>)c;
JProgressBar progress = (JProgressBar)jlayer.getView();
int w = progress.getSize().width;
int h = progress.getSize().height;
if(bi==null || w!=prevw || h!=prevh) {
bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
prevw = w;
prevh = h;
Graphics2D g2 = bi.createGraphics();
super.paint(g2, c);
g2.dispose();
Image image = c.createImage(
new FilteredImageSource(bi.getSource(),
new ColorFilter(color)));
g.drawImage(image, 0, 0, c);
} else {
super.paint(g, c);
}
}
}
class ColorFilter extends RGBImageFilter {
Color color;
public ColorFilter(Color color) {
this.color = color;
}
@Override public int filterRGB(int x, int y, int argb) {
int r = (int)((argb >> 16) & 0xff);
int g = (int)((argb >> 8) & 0xff);
int b = (int)((argb ) & 0xff);
return (argb & color.getRGB()) | (g<<16) | (r<<8) | (b);
}
}
}
Anyone know where I'm going wrong? Thanks!
EDIT: I've trimmed down the example slightly and modified it to better express my issue. Sorry for the confusion.